Skip to content

Commit 04c98c5

Browse files
committed
refactor(CPopover): improve accessibility
1 parent 5dfb126 commit 04c98c5

File tree

3 files changed

+64
-45
lines changed

3 files changed

+64
-45
lines changed

packages/coreui-vue/src/components/popover/CPopover.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 CPopover = defineComponent({
1111
name: 'CPopover',
@@ -117,6 +117,7 @@ const CPopover = defineComponent({
117117
setup(props, { attrs, slots, emit }) {
118118
const togglerRef = ref()
119119
const popoverRef = ref()
120+
const uID = ref()
120121
const visible = ref(props.visible)
121122
const { initPopper, destroyPopper } = usePopper()
122123

@@ -147,6 +148,10 @@ const CPopover = defineComponent({
147148
placement: getRTLPlacement(props.placement, togglerRef.value),
148149
}
149150

151+
onMounted(() => {
152+
uID.value = getUID('popover')
153+
})
154+
150155
const handleEnter = (el: RendererElement, done: () => void) => {
151156
emit('show')
152157
initPopper(togglerRef.value, popoverRef.value, popperConfig)
@@ -206,6 +211,7 @@ const CPopover = defineComponent({
206211
},
207212
attrs.class,
208213
],
214+
id: uID.value,
209215
ref: popoverRef,
210216
role: 'tooltip',
211217
},
@@ -234,6 +240,7 @@ const CPopover = defineComponent({
234240
),
235241
slots.toggler &&
236242
slots.toggler({
243+
id: visible.value ? uID.value : null,
237244
on: {
238245
click: (event: Event) =>
239246
props.trigger.includes('click') && toggleVisible(event, !visible.value),

packages/coreui-vue/src/directives/v-c-popover.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 createPopoverElement = (id: string, header: string, content: string): HTMLDivElement => {
@@ -14,30 +16,40 @@ const createPopoverElement = (id: string, header: string, content: string): HTML
1416
return popover
1517
}
1618

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

26-
const removePopoverElement = (popover: HTMLDivElement) => {
33+
const removePopoverElement = (el: HTMLElement, popover: HTMLDivElement) => {
34+
el.removeAttribute('aria-describedby')
2735
popover.classList.remove('show')
2836
setTimeout(() => {
2937
popover.remove()
3038
}, 300)
3139
}
3240

33-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
34-
const togglePopoverElement = (popover: HTMLDivElement, el: HTMLElement, popperOptions: any) => {
41+
const togglePopoverElement = (
42+
el: HTMLElement,
43+
popover: HTMLDivElement,
44+
popperOptions: Partial<Options>,
45+
uID: string,
46+
) => {
3547
const popperElement = document.getElementById(popover.id)
3648
if (popperElement && popperElement.classList.contains('show')) {
37-
removePopoverElement(popover)
49+
removePopoverElement(el, popover)
3850
return
3951
}
40-
addPopoverElement(popover, el, popperOptions)
52+
addPopoverElement(el, popover, popperOptions, uID)
4153
}
4254

4355
export default {
@@ -65,30 +77,30 @@ export default {
6577
],
6678
}
6779

68-
const popoverUID = getUID('popover')
69-
binding.arg = popoverUID
70-
const popover = createPopoverElement(popoverUID, header, content)
80+
const uID = getUID('popover')
81+
binding.arg = uID
82+
const popover = createPopoverElement(uID, header, content)
7183

7284
trigger.includes('click') &&
7385
el.addEventListener('click', () => {
74-
togglePopoverElement(popover, el, popperOptions)
86+
togglePopoverElement(el, popover, popperOptions, uID)
7587
})
7688

7789
if (trigger.includes('focus')) {
7890
el.addEventListener('focus', () => {
79-
addPopoverElement(popover, el, popperOptions)
91+
addPopoverElement(el, popover, popperOptions, uID)
8092
})
8193
el.addEventListener('blur', () => {
82-
removePopoverElement(popover)
94+
removePopoverElement(el, popover)
8395
})
8496
}
8597

8698
if (trigger.includes('hover')) {
8799
el.addEventListener('mouseenter', () => {
88-
addPopoverElement(popover, el, popperOptions)
100+
addPopoverElement(el, popover, popperOptions, uID)
89101
})
90102
el.addEventListener('mouseleave', () => {
91-
removePopoverElement(popover)
103+
removePopoverElement(el, popover)
92104
})
93105
}
94106
},

packages/docs/components/popover.md

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ other_frameworks: popover
1111

1212
::: demo
1313
<CPopover title="Popover title" content="And here\’s some amazing content. It’s very engaging. Right?" placement="right">
14-
<template #toggler="{ on }">
15-
<CButton color="danger" size="lg" v-on="on">Click to toggle popover</CButton>
14+
<template #toggler="{ id, on }">
15+
<CButton color="danger" size="lg" :aria-describedby="id" v-on="on">Click to toggle popover</CButton>
1616
</template>
1717
</CPopover>
1818
:::
1919
```vue
2020
<CPopover title="Popover title" content="And here\’s some amazing content. It’s very engaging. Right?" placement="right">
21-
<template #toggler="{ on }">
22-
<CButton color="danger" size="lg" v-on="on">Click to toggle popover</CButton>
21+
<template #toggler="{ id, on }">
22+
<CButton color="danger" size="lg" :aria-describedby="id" v-on="on">Click to toggle popover</CButton>
2323
</template>
2424
</CPopover>
2525
```
@@ -41,45 +41,45 @@ Four options are available: top, right, bottom, and left aligned. Directions are
4141

4242
::: demo
4343
<CPopover content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="top">
44-
<template #toggler="{ on }">
45-
<CButton color="secondary" v-on="on">Popover on top</CButton>
44+
<template #toggler="{ id, on }">
45+
<CButton color="secondary" :aria-describedby="id" v-on="on">Popover on top</CButton>
4646
</template>
4747
</CPopover>
4848
<CPopover content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="right">
49-
<template #toggler="{ on }">
50-
<CButton color="secondary" v-on="on">Popover on right</CButton>
49+
<template #toggler="{ id, on }">
50+
<CButton color="secondary" :aria-describedby="id" v-on="on">Popover on right</CButton>
5151
</template>
5252
</CPopover>
5353
<CPopover content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="bottom">
54-
<template #toggler="{ on }">
55-
<CButton color="secondary" v-on="on">Popover on bottom</CButton>
54+
<template #toggler="{ id, on }">
55+
<CButton color="secondary" :aria-describedby="id" v-on="on">Popover on bottom</CButton>
5656
</template>
5757
</CPopover>
5858
<CPopover content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="left">
59-
<template #toggler="{ on }">
60-
<CButton color="secondary" v-on="on">Popover on left</CButton>
59+
<template #toggler="{ id, on }">
60+
<CButton color="secondary" :aria-describedby="id" v-on="on">Popover on left</CButton>
6161
</template>
6262
</CPopover>
6363
:::
6464
```vue
6565
<CPopover content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="top">
66-
<template #toggler="{ on }">
67-
<CButton color="secondary" v-on="on">Popover on top</CButton>
66+
<template #toggler="{ id, on }">
67+
<CButton color="secondary" :aria-describedby="id" v-on="on">Popover on top</CButton>
6868
</template>
6969
</CPopover>
7070
<CPopover content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="right">
71-
<template #toggler="{ on }">
72-
<CButton color="secondary" v-on="on">Popover on right</CButton>
71+
<template #toggler="{ id, on }">
72+
<CButton color="secondary" :aria-describedby="id" v-on="on">Popover on right</CButton>
7373
</template>
7474
</CPopover>
7575
<CPopover content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="bottom">
76-
<template #toggler="{ on }">
77-
<CButton color="secondary" v-on="on">Popover on bottom</CButton>
76+
<template #toggler="{ id, on }">
77+
<CButton color="secondary" :aria-describedby="id" v-on="on">Popover on bottom</CButton>
7878
</template>
7979
</CPopover>
8080
<CPopover content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus" placement="left">
81-
<template #toggler="{ on }">
82-
<CButton color="secondary" v-on="on">Popover on left</CButton>
81+
<template #toggler="{ id, on }">
82+
<CButton color="secondary" :aria-describedby="id" v-on="on">Popover on left</CButton>
8383
</template>
8484
</CPopover>
8585
```
@@ -110,8 +110,8 @@ You can customize the appearance of popovers using [CSS variables](#css-variable
110110
title="Custom popover"
111111
:style="customPopoverStyle"
112112
>
113-
<template #toggler="{ on }">
114-
<CButton color="secondary" v-on="on">Custom popover</CButton>
113+
<template #toggler="{ id, on }">
114+
<CButton color="secondary" :aria-describedby="id" v-on="on">Custom popover</CButton>
115115
</template>
116116
</CPopover>
117117
:::
@@ -123,8 +123,8 @@ You can customize the appearance of popovers using [CSS variables](#css-variable
123123
title="Custom popover"
124124
:style="customPopoverStyle"
125125
>
126-
<template #toggler="{ on }">
127-
<CButton color="secondary" v-on="on">Custom popover</CButton>
126+
<template #toggler="{ id, on }">
127+
<CButton color="secondary" :aria-describedby="id" v-on="on">Custom popover</CButton>
128128
</template>
129129
</CPopover>
130130
</template>
@@ -160,8 +160,8 @@ For disabled popover triggers, you may also prefer `:trigger="['hover', 'focus']
160160
placement="right"
161161
:trigger="['hover', 'focus']"
162162
>
163-
<template #toggler="{ on }">
164-
<span class="d-inline-block" :tabindex="0" v-on="on">
163+
<template #toggler="{ id, on }">
164+
<span class="d-inline-block" :tabindex="0" :aria-describedby="id" v-on="on">
165165
<CButton color="primary" disabled>Disabled button</CButton>
166166
</span>
167167
</template>
@@ -173,8 +173,8 @@ For disabled popover triggers, you may also prefer `:trigger="['hover', 'focus']
173173
placement="right"
174174
:trigger="['hover', 'focus']"
175175
>
176-
<template #toggler="{ on }">
177-
<span class="d-inline-block" :tabindex="0" v-on="on">
176+
<template #toggler="{ id, on }">
177+
<span class="d-inline-block" :tabindex="0" :aria-describedby="id" v-on="on">
178178
<CButton color="primary" disabled>Disabled button</CButton>
179179
</span>
180180
</template>

0 commit comments

Comments
 (0)