Skip to content

Commit 941a5dc

Browse files
authored
feat: add aria-describedby modifier for tooltips (#842)
* feat: add aria-describedby modifier for tooltips * add test
1 parent 79fadf2 commit 941a5dc

File tree

2 files changed

+99
-5
lines changed

2 files changed

+99
-5
lines changed

src/usePopper.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,38 @@ export interface UsePopperState {
5959
state?: State;
6060
}
6161

62+
const ariaDescribedByModifier: Modifier<'ariaDescribedBy', undefined> = {
63+
name: 'ariaDescribedBy',
64+
enabled: true,
65+
phase: 'afterWrite',
66+
effect: ({ state }) => {
67+
return () => {
68+
const { reference, popper } = state.elements;
69+
if ('removeAttribute' in reference) {
70+
const ids = (reference.getAttribute('aria-describedby') || '')
71+
.split(',')
72+
.filter((id) => id.trim() !== popper.id);
73+
74+
if (!ids.length) reference.removeAttribute('aria-describedby');
75+
else reference.setAttribute('aria-describedby', ids.join(','));
76+
}
77+
};
78+
},
79+
fn: ({ state }) => {
80+
const { popper, reference } = state.elements;
81+
82+
const role = popper.getAttribute('role')?.toLowerCase();
83+
84+
if (popper.id && role === 'tooltip' && 'setAttribute' in reference) {
85+
const ids = reference.getAttribute('aria-describedby');
86+
reference.setAttribute(
87+
'aria-describedby',
88+
ids ? `${ids},${popper.id}` : popper.id,
89+
);
90+
}
91+
},
92+
};
93+
6294
const EMPTY_MODIFIERS = [] as any;
6395
/**
6496
* Position an element relative some reference element using Popper.js
@@ -159,7 +191,7 @@ function usePopper(
159191
...config,
160192
placement,
161193
strategy,
162-
modifiers: [...modifiers, updateModifier],
194+
modifiers: [...modifiers, ariaDescribedByModifier, updateModifier],
163195
});
164196

165197
return () => {

test/usePopperSpec.js

+66-4
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import React from 'react';
33
import usePopper from '../src/usePopper';
44

55
describe('usePopper', () => {
6-
function renderHook(fn) {
6+
function renderHook(fn, initialProps) {
77
let result = { current: null };
88

9-
function Wrapper() {
10-
result.current = fn();
9+
function Wrapper(props) {
10+
result.current = fn(props);
1111
return null;
1212
}
1313

14-
result.mount = mount(<Wrapper />);
14+
result.mount = mount(<Wrapper {...initialProps} />);
15+
result.update = (props) => result.mount.setProps(props);
1516

1617
return result;
1718
}
@@ -46,4 +47,65 @@ describe('usePopper', () => {
4647
done();
4748
});
4849
});
50+
51+
it('should add aria-describedBy for tooltips', (done) => {
52+
elements.popper.setAttribute('role', 'tooltip');
53+
elements.popper.setAttribute('id', 'example123');
54+
55+
const result = renderHook(() =>
56+
usePopper(elements.reference, elements.popper),
57+
);
58+
59+
setTimeout(() => {
60+
expect(
61+
document.querySelector('[aria-describedby="example123"]'),
62+
).to.equal(elements.reference);
63+
64+
result.mount.unmount();
65+
66+
expect(
67+
document.querySelector('[aria-describedby="example123"]'),
68+
).to.equal(null);
69+
70+
done();
71+
});
72+
});
73+
74+
it('should add to existing describedBy', (done) => {
75+
elements.popper.setAttribute('role', 'tooltip');
76+
elements.popper.setAttribute('id', 'example123');
77+
elements.reference.setAttribute('aria-describedby', 'foo, bar , baz ');
78+
79+
const result = renderHook(() =>
80+
usePopper(elements.reference, elements.popper),
81+
);
82+
83+
setTimeout(() => {
84+
expect(
85+
document.querySelector(
86+
'[aria-describedby="foo, bar , baz ,example123"]',
87+
),
88+
).to.equal(elements.reference);
89+
90+
result.mount.unmount();
91+
92+
expect(
93+
document.querySelector('[aria-describedby="foo, bar , baz "]'),
94+
).to.equal(elements.reference);
95+
96+
done();
97+
});
98+
});
99+
100+
it('should not aria-describedBy any other role', (done) => {
101+
renderHook(() => usePopper(elements.reference, elements.popper));
102+
103+
setTimeout(() => {
104+
expect(
105+
document.querySelector('[aria-describedby="example123"]'),
106+
).to.equal(null);
107+
108+
done();
109+
});
110+
});
49111
});

0 commit comments

Comments
 (0)