Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2672a50

Browse files
chardin1gabrieljablonski
andauthored
feat: add aria-describedby to tooltip automatically (#1257)
* Add aria-describedby automatically * Minor adjustments * Add cleanup function * test: update snapshots --------- Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
1 parent 9faf2c6 commit 2672a50

File tree

6 files changed

+76
-8
lines changed

6 files changed

+76
-8
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: 37 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,42 @@ 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+
237+
// eslint-disable-next-line consistent-return
238+
return () => {
239+
// cleanup aria-describedby when the tooltip is closed
240+
removeAriaDescribedBy(activeAnchor)
241+
removeAriaDescribedBy(previousActiveAnchor)
242+
}
243+
}, [activeAnchor, show, id, previousActiveAnchor])
244+
208245
/**
209246
* this replicates the effect from `handleShow()`
210247
* 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: 10 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,15 @@ 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+
setActiveAnchor((prev) => {
382+
if (!anchor?.isSameNode(prev)) {
383+
previousActiveAnchorRef.current = prev
384+
}
385+
return anchor
386+
})
387+
},
379388
role,
380389
}
381390

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

Lines changed: 6 additions & 3 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,22 +52,23 @@ 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"
5659
>
5760
Lorem Ipsum
5861
</span>
5962
<div
60-
class="react-tooltip react-tooltip__place-right react-tooltip__show"
63+
class="react-tooltip react-tooltip__place-left react-tooltip__show"
6164
id="example-place-attr"
6265
role="tooltip"
63-
style="left: 10px; top: 5px;"
66+
style="left: -10px; top: 5px;"
6467
>
6568
Hello World!
6669
<div
6770
class="react-tooltip-arrow"
68-
style="--rt-arrow-size: 8px; left: -4px; top: -1px;"
71+
style="--rt-arrow-size: 8px; top: -1px; right: -4px;"
6972
/>
7073
</div>
7174
</div>

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

Lines changed: 11 additions & 3 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,20 +181,21 @@ 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
180188
</span>
181189
<div
182-
class="react-tooltip react-tooltip__place-right react-tooltip__show"
190+
class="react-tooltip react-tooltip__place-left react-tooltip__show"
183191
id="example-place"
184192
role="tooltip"
185-
style="left: 10px; top: 5px;"
193+
style="left: -10px; top: 5px;"
186194
>
187195
Hello World!
188196
<div
189197
class="react-tooltip-arrow"
190-
style="--rt-arrow-size: 8px; left: -4px; top: -1px;"
198+
style="--rt-arrow-size: 8px; top: -1px; right: -4px;"
191199
/>
192200
</div>
193201
</div>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /