Skip to content

Commit 8878931

Browse files
committed
Add aria-describedby automatically
1 parent 9faf2c6 commit 8878931

File tree

6 files changed

+61
-2
lines changed

6 files changed

+61
-2
lines changed

src/App.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,15 @@ function App() {
4747
>
4848
My button
4949
</button>
50-
<Tooltip place="bottom" anchorId={anchorId} isOpen={isDarkOpen} setIsOpen={setIsDarkOpen} />
5150
<Tooltip
51+
id="button1"
52+
place="bottom"
53+
anchorId={anchorId}
54+
isOpen={isDarkOpen}
55+
setIsOpen={setIsDarkOpen}
56+
/>
57+
<Tooltip
58+
id="button2"
5259
place="top"
5360
variant="success"
5461
anchorId="button2"
@@ -101,6 +108,7 @@ function App() {
101108
<Tooltip id="anchor-select">Tooltip content</Tooltip>
102109
<Tooltip
103110
ref={tooltipRef}
111+
id="tooltip-content"
104112
anchorSelect="section[id='section-anchor-select'] > p > button"
105113
place="bottom"
106114
openEvents={{ click: true }}
@@ -127,6 +135,7 @@ function App() {
127135
</div>
128136
<Tooltip
129137
anchorId="floatAnchor"
138+
id="float-tooltip"
130139
content={
131140
toggle
132141
? 'This is a float tooltip with a very very large content string'
@@ -151,6 +160,7 @@ function App() {
151160
</div>
152161
<Tooltip
153162
anchorId="onClickAnchor"
163+
id="onclick-tooltip"
154164
content={`This is an on click tooltip (x:${position.x},y:${position.y})`}
155165
events={['click']}
156166
position={position}

src/components/Tooltip/Tooltip.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const Tooltip = ({
6363
isOpen,
6464
defaultIsOpen = false,
6565
setIsOpen,
66+
previousActiveAnchor,
6667
activeAnchor,
6768
setActiveAnchor,
6869
border,
@@ -205,6 +206,35 @@ const Tooltip = ({
205206
}, 10)
206207
}
207208

209+
/**
210+
* Add aria-describedby to activeAnchor when tooltip is active
211+
*/
212+
useEffect(() => {
213+
if (!id) return
214+
215+
function getAriaDescribedBy(element: HTMLElement | null) {
216+
return element?.getAttribute('aria-describedby')?.split(' ') || []
217+
}
218+
219+
function removeAriaDescribedBy(element: HTMLElement | null) {
220+
const newDescribedBy = getAriaDescribedBy(element).filter((s) => s !== id)
221+
if (newDescribedBy.length) {
222+
element?.setAttribute('aria-describedby', newDescribedBy.join(' '))
223+
} else {
224+
element?.removeAttribute('aria-describedby')
225+
}
226+
}
227+
228+
if (show) {
229+
removeAriaDescribedBy(previousActiveAnchor)
230+
const currentDescribedBy = getAriaDescribedBy(activeAnchor)
231+
const describedBy = [...new Set([...currentDescribedBy, id])].filter(Boolean).join(' ')
232+
activeAnchor?.setAttribute('aria-describedby', describedBy)
233+
} else {
234+
removeAriaDescribedBy(activeAnchor)
235+
}
236+
}, [activeAnchor, show])
237+
208238
/**
209239
* this replicates the effect from `handleShow()`
210240
* when `isOpen` is changed from outside

src/components/Tooltip/TooltipTypes.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ export interface ITooltip {
153153
afterShow?: () => void
154154
afterHide?: () => void
155155
disableTooltip?: (anchorRef: HTMLElement | null) => boolean
156+
previousActiveAnchor: HTMLElement | null
156157
activeAnchor: HTMLElement | null
157158
setActiveAnchor: (anchor: HTMLElement | null) => void
158159
border?: CSSProperties['border']

src/components/TooltipController/TooltipController.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const TooltipController = React.forwardRef<TooltipRefProps, ITooltipController>(
8181
const [tooltipPositionStrategy, setTooltipPositionStrategy] = useState(positionStrategy)
8282
const [tooltipClassName, setTooltipClassName] = useState<string | null>(null)
8383
const [activeAnchor, setActiveAnchor] = useState<HTMLElement | null>(null)
84+
const previousActiveAnchorRef = useRef<HTMLElement | null>(null)
8485
const styleInjectionRef = useRef(disableStyleInjection)
8586
/**
8687
* @todo Remove this in a future version (provider/wrapper method is deprecated)
@@ -375,7 +376,13 @@ const TooltipController = React.forwardRef<TooltipRefProps, ITooltipController>(
375376
afterHide,
376377
disableTooltip,
377378
activeAnchor,
378-
setActiveAnchor: (anchor: HTMLElement | null) => setActiveAnchor(anchor),
379+
previousActiveAnchor: previousActiveAnchorRef.current,
380+
setActiveAnchor: (anchor: HTMLElement | null) => {
381+
if (!anchor?.isSameNode(activeAnchor)) {
382+
previousActiveAnchorRef.current = activeAnchor
383+
}
384+
setActiveAnchor(anchor)
385+
},
379386
role,
380387
}
381388

src/test/__snapshots__/tooltip-attributes.spec.js.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
exports[`tooltip attributes basic tooltip 1`] = `
44
<div>
55
<span
6+
aria-describedby="basic-example-attr"
67
data-tooltip-content="Hello World!"
78
data-tooltip-id="basic-example-attr"
89
>
@@ -26,6 +27,7 @@ exports[`tooltip attributes basic tooltip 1`] = `
2627
exports[`tooltip attributes tooltip with class name 1`] = `
2728
<div>
2829
<span
30+
aria-describedby="example-class-name-attr"
2931
data-tooltip-class-name="tooltip-class-name"
3032
data-tooltip-content="Hello World!"
3133
data-tooltip-id="example-class-name-attr"
@@ -50,6 +52,7 @@ exports[`tooltip attributes tooltip with class name 1`] = `
5052
exports[`tooltip attributes tooltip with place 1`] = `
5153
<div>
5254
<span
55+
aria-describedby="example-place-attr"
5356
data-tooltip-content="Hello World!"
5457
data-tooltip-id="example-place-attr"
5558
data-tooltip-place="right"

src/test/__snapshots__/tooltip-props.spec.js.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
exports[`tooltip props basic tooltip 1`] = `
44
<div>
55
<span
6+
aria-describedby="basic-example"
67
data-tooltip-id="basic-example"
78
>
89
Lorem Ipsum
@@ -25,6 +26,7 @@ exports[`tooltip props basic tooltip 1`] = `
2526
exports[`tooltip props clickable tooltip 1`] = `
2627
<div>
2728
<span
29+
aria-describedby="example-clickable"
2830
data-tooltip-id="example-clickable"
2931
>
3032
Lorem Ipsum
@@ -49,6 +51,7 @@ exports[`tooltip props clickable tooltip 1`] = `
4951
exports[`tooltip props tooltip with custom position 1`] = `
5052
<div>
5153
<span
54+
aria-describedby="example-place"
5255
data-tooltip-id="example-place"
5356
>
5457
Lorem Ipsum
@@ -81,6 +84,7 @@ exports[`tooltip props tooltip with delay hide 1`] = `
8184
exports[`tooltip props tooltip with delay show 1`] = `
8285
<div>
8386
<span
87+
aria-describedby="example-delay-show"
8488
data-tooltip-id="example-delay-show"
8589
>
8690
Lorem Ipsum
@@ -103,6 +107,7 @@ exports[`tooltip props tooltip with delay show 1`] = `
103107
exports[`tooltip props tooltip with disableTooltip return false 1`] = `
104108
<div>
105109
<span
110+
aria-describedby="example-disableTooltip-false"
106111
data-tooltip-id="example-disableTooltip-false"
107112
>
108113
Lorem Ipsum
@@ -125,6 +130,7 @@ exports[`tooltip props tooltip with disableTooltip return false 1`] = `
125130
exports[`tooltip props tooltip with float 1`] = `
126131
<div>
127132
<span
133+
aria-describedby="example-float"
128134
data-tooltip-id="example-float"
129135
>
130136
Lorem Ipsum
@@ -147,6 +153,7 @@ exports[`tooltip props tooltip with float 1`] = `
147153
exports[`tooltip props tooltip with html 1`] = `
148154
<div>
149155
<span
156+
aria-describedby="example-html"
150157
data-tooltip-id="example-html"
151158
>
152159
Lorem Ipsum
@@ -174,6 +181,7 @@ exports[`tooltip props tooltip with html 1`] = `
174181
exports[`tooltip props tooltip with place 1`] = `
175182
<div>
176183
<span
184+
aria-describedby="example-place"
177185
data-tooltip-id="example-place"
178186
>
179187
Lorem Ipsum

0 commit comments

Comments
 (0)