Skip to content

Commit 3911802

Browse files
committed
refactor(CTooltip): improve accessibility
1 parent 04c98c5 commit 3911802

File tree

3 files changed

+60
-41
lines changed

3 files changed

+60
-41
lines changed

packages/coreui-vue/src/components/tooltip/CTooltip.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { defineComponent, h, PropType, ref, RendererElement, Transition } from 'vue'
1+
import { defineComponent, h, onMounted, PropType, ref, RendererElement, Transition } from 'vue'
22
import type { Placement } from '@popperjs/core'
33

44
import { CConditionalTeleport } from '../conditional-teleport'
55
import { usePopper } from '../../composables'
66
import type { Placements, Triggers } from '../../types'
77
import { executeAfterTransition } from '../../utils/transition'
8-
import { getRTLPlacement } from '../../utils'
8+
import { getRTLPlacement, getUID } from '../../utils'
99

1010
const CTooltip = defineComponent({
1111
name: 'CTooltip',
@@ -113,6 +113,7 @@ const CTooltip = defineComponent({
113113
setup(props, { attrs, slots, emit }) {
114114
const togglerRef = ref()
115115
const tooltipRef = ref()
116+
const uID = ref()
116117
const visible = ref(props.visible)
117118
const { initPopper, destroyPopper } = usePopper()
118119

@@ -143,6 +144,10 @@ const CTooltip = defineComponent({
143144
placement: getRTLPlacement(props.placement, togglerRef.value),
144145
}
145146

147+
onMounted(() => {
148+
uID.value = getUID('tooltip')
149+
})
150+
146151
const handleEnter = (el: RendererElement, done: () => void) => {
147152
emit('show')
148153
initPopper(togglerRef.value, tooltipRef.value, popperConfig)
@@ -202,6 +207,7 @@ const CTooltip = defineComponent({
202207
},
203208
attrs.class,
204209
],
210+
id: uID.value,
205211
ref: tooltipRef,
206212
role: 'tooltip',
207213
},
@@ -222,6 +228,7 @@ const CTooltip = defineComponent({
222228
),
223229
slots.toggler &&
224230
slots.toggler({
231+
id: visible.value ? uID.value : null,
225232
on: {
226233
click: (event: Event) =>
227234
props.trigger.includes('click') && toggleVisible(event, !visible.value),

packages/coreui-vue/src/directives/v-c-tooltip.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { DirectiveBinding } from 'vue'
22
import { createPopper } from '@popperjs/core'
33

4+
import type { Options } from '@popperjs/core'
5+
46
import { getUID } from '../utils'
57

68
const createTooltipElement = (id: string, content: string): HTMLDivElement => {
@@ -13,30 +15,40 @@ const createTooltipElement = (id: string, content: string): HTMLDivElement => {
1315
return tooltip
1416
}
1517

16-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
17-
const addTooltipElement = (tooltip: HTMLDivElement, el: HTMLElement, popperOptions: any) => {
18+
const addTooltipElement = (
19+
el: HTMLElement,
20+
tooltip: HTMLDivElement,
21+
popperOptions: Partial<Options>,
22+
uID: string,
23+
) => {
24+
el.setAttribute('aria-describedby', uID)
1825
document.body.appendChild(tooltip)
1926
createPopper(el, tooltip, popperOptions)
2027
setTimeout(() => {
2128
tooltip.classList.add('show')
2229
}, 1)
2330
}
2431

25-
const removeTooltipElement = (tooltip: HTMLDivElement) => {
32+
const removeTooltipElement = (el: HTMLElement, tooltip: HTMLDivElement) => {
33+
el.removeAttribute('aria-describedby')
2634
tooltip.classList.remove('show')
2735
setTimeout(() => {
2836
tooltip.remove()
2937
}, 300)
3038
}
3139

32-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
33-
const toggleTooltipElement = (tooltip: HTMLDivElement, el: HTMLElement, popperOptions: any) => {
40+
const toggleTooltipElement = (
41+
el: HTMLElement,
42+
tooltip: HTMLDivElement,
43+
popperOptions: Partial<Options>,
44+
uID: string,
45+
) => {
3446
const popperElement = document.getElementById(tooltip.id)
3547
if (popperElement && popperElement.classList.contains('show')) {
36-
removeTooltipElement(tooltip)
48+
removeTooltipElement(el, tooltip)
3749
return
3850
}
39-
addTooltipElement(tooltip, el, popperOptions)
51+
addTooltipElement(el, tooltip, popperOptions, uID)
4052
}
4153

4254
export default {
@@ -62,30 +74,30 @@ export default {
6274
],
6375
}
6476

65-
const tooltipUID = getUID('tooltip')
66-
binding.arg = tooltipUID
67-
const tooltip = createTooltipElement(tooltipUID, content)
77+
const uID = getUID('tooltip')
78+
binding.arg = uID
79+
const tooltip = createTooltipElement(uID, content)
6880

6981
trigger.includes('click') &&
7082
el.addEventListener('click', () => {
71-
toggleTooltipElement(tooltip, el, popperOptions)
83+
toggleTooltipElement(el, tooltip, popperOptions, uID)
7284
})
7385

7486
if (trigger.includes('focus')) {
7587
el.addEventListener('focus', () => {
76-
addTooltipElement(tooltip, el, popperOptions)
88+
addTooltipElement(el, tooltip, popperOptions, uID)
7789
})
7890
el.addEventListener('blur', () => {
79-
removeTooltipElement(tooltip)
91+
removeTooltipElement(el, tooltip)
8092
})
8193
}
8294

8395
if (trigger.includes('hover')) {
8496
el.addEventListener('mouseenter', () => {
85-
addTooltipElement(tooltip, el, popperOptions)
97+
addTooltipElement(el, tooltip, popperOptions, uID)
8698
})
8799
el.addEventListener('mouseleave', () => {
88-
removeTooltipElement(tooltip)
100+
removeTooltipElement(el, tooltip)
89101
})
90102
}
91103
},

packages/docs/components/tooltip.md

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,45 +56,45 @@ Hover over the buttons below to see the four tooltips directions: top, right, bo
5656

5757
::: demo
5858
<CTooltip content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="top">
59-
<template #toggler="{ on }">
60-
<CButton color="secondary" v-on="on">Tooltip on top</CButton>
59+
<template #toggler="{ id, on }">
60+
<CButton color="secondary" :aria-describedby="id" v-on="on">Tooltip on top</CButton>
6161
</template>
6262
</CTooltip>
6363
<CTooltip content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="right">
64-
<template #toggler="{ on }">
65-
<CButton color="secondary" v-on="on">Tooltip on right</CButton>
64+
<template #toggler="{ id, on }">
65+
<CButton color="secondary" :aria-describedby="id" v-on="on">Tooltip on right</CButton>
6666
</template>
6767
</CTooltip>
6868
<CTooltip content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="bottom">
69-
<template #toggler="{ on }">
70-
<CButton color="secondary" v-on="on">Tooltip on bottom</CButton>
69+
<template #toggler="{ id, on }">
70+
<CButton color="secondary" :aria-describedby="id" v-on="on">Tooltip on bottom</CButton>
7171
</template>
7272
</CTooltip>
7373
<CTooltip content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="left">
74-
<template #toggler="{ on }">
75-
<CButton color="secondary" v-on="on">Tooltip on left</CButton>
74+
<template #toggler="{ id, on }">
75+
<CButton color="secondary" :aria-describedby="id" v-on="on">Tooltip on left</CButton>
7676
</template>
7777
</CTooltip>
7878
:::
7979
```vue
8080
<CTooltip content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="top">
81-
<template #toggler="{ on }">
82-
<CButton color="secondary" v-on="on">Tooltip on top</CButton>
81+
<template #toggler="{ id, on }">
82+
<CButton color="secondary" :aria-describedby="id" v-on="on">Tooltip on top</CButton>
8383
</template>
8484
</CTooltip>
8585
<CTooltip content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="right">
86-
<template #toggler="{ on }">
87-
<CButton color="secondary" v-on="on">Tooltip on right</CButton>
86+
<template #toggler="{ id, on }">
87+
<CButton color="secondary" :aria-describedby="id" v-on="on">Tooltip on right</CButton>
8888
</template>
8989
</CTooltip>
9090
<CTooltip content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="bottom">
91-
<template #toggler="{ on }">
92-
<CButton color="secondary" v-on="on">Tooltip on bottom</CButton>
91+
<template #toggler="{ id, on }">
92+
<CButton color="secondary" :aria-describedby="id" v-on="on">Tooltip on bottom</CButton>
9393
</template>
9494
</CTooltip>
9595
<CTooltip content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="left">
96-
<template #toggler="{ on }">
97-
<CButton color="secondary" v-on="on">Tooltip on left</CButton>
96+
<template #toggler="{ id, on }">
97+
<CButton color="secondary" :aria-describedby="id" v-on="on">Tooltip on left</CButton>
9898
</template>
9999
</CTooltip>
100100
```
@@ -124,8 +124,8 @@ You can customize the appearance of tooltips using [CSS variables](#css-variable
124124
placement="top"
125125
:style="customTooltipStyle"
126126
>
127-
<template #toggler="{ on }">
128-
<CButton color="secondary" v-on="on">Custom popover</CButton>
127+
<template #toggler="{ id, on }">
128+
<CButton color="secondary" :aria-describedby="id" v-on="on">Custom popover</CButton>
129129
</template>
130130
</CTooltip>
131131
:::
@@ -136,8 +136,8 @@ You can customize the appearance of tooltips using [CSS variables](#css-variable
136136
placement="top"
137137
:style="customTooltipStyle"
138138
>
139-
<template #toggler="{ on }">
140-
<CButton color="secondary" v-on="on">Custom popover</CButton>
139+
<template #toggler="{ id, on }">
140+
<CButton color="secondary" :aria-describedby="id" v-on="on">Custom popover</CButton>
141141
</template>
142142
</CTooltip>
143143
</template>
@@ -162,17 +162,17 @@ Elements with the disabled attribute aren’t interactive, meaning users cannot
162162

163163
:::demo
164164
<CTooltip content="Disabled tooltip">
165-
<template #toggler="{ on }">
166-
<span class="d-inline-block" :tabindex="0" v-on="on">
165+
<template #toggler="{ id, on }">
166+
<span class="d-inline-block" :tabindex="0" :aria-describedby="id" v-on="on">
167167
<CButton color="primary" disabled>Disabled button</CButton>
168168
</span>
169169
</template>
170170
</CTooltip>
171171
:::
172172
```vue
173173
<CTooltip content="Disabled tooltip">
174-
<template #toggler="{ on }">
175-
<span class="d-inline-block" :tabindex="0" v-on="on">
174+
<template #toggler="{ id, on }">
175+
<span class="d-inline-block" :tabindex="0" :aria-describedby="id" v-on="on">
176176
<CButton color="primary" disabled>Disabled button</CButton>
177177
</span>
178178
</template>

0 commit comments

Comments
 (0)