Skip to content

Commit

Permalink
feat: add aria-describedby modifier for tooltips (#842)
Browse files Browse the repository at this point in the history
* feat: add aria-describedby modifier for tooltips

* add test
  • Loading branch information
jquense authored Jul 20, 2020
1 parent 79fadf2 commit 941a5dc
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 5 deletions.
34 changes: 33 additions & 1 deletion src/usePopper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,38 @@ export interface UsePopperState {
state?: State;
}

const ariaDescribedByModifier: Modifier<'ariaDescribedBy', undefined> = {
name: 'ariaDescribedBy',
enabled: true,
phase: 'afterWrite',
effect: ({ state }) => {
return () => {
const { reference, popper } = state.elements;
if ('removeAttribute' in reference) {
const ids = (reference.getAttribute('aria-describedby') || '')
.split(',')
.filter((id) => id.trim() !== popper.id);

if (!ids.length) reference.removeAttribute('aria-describedby');
else reference.setAttribute('aria-describedby', ids.join(','));
}
};
},
fn: ({ state }) => {
const { popper, reference } = state.elements;

const role = popper.getAttribute('role')?.toLowerCase();

if (popper.id && role === 'tooltip' && 'setAttribute' in reference) {
const ids = reference.getAttribute('aria-describedby');
reference.setAttribute(
'aria-describedby',
ids ? `${ids},${popper.id}` : popper.id,
);
}
},
};

const EMPTY_MODIFIERS = [] as any;
/**
* Position an element relative some reference element using Popper.js
Expand Down Expand Up @@ -159,7 +191,7 @@ function usePopper(
...config,
placement,
strategy,
modifiers: [...modifiers, updateModifier],
modifiers: [...modifiers, ariaDescribedByModifier, updateModifier],
});

return () => {
Expand Down
70 changes: 66 additions & 4 deletions test/usePopperSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import React from 'react';
import usePopper from '../src/usePopper';

describe('usePopper', () => {
function renderHook(fn) {
function renderHook(fn, initialProps) {
let result = { current: null };

function Wrapper() {
result.current = fn();
function Wrapper(props) {
result.current = fn(props);
return null;
}

result.mount = mount(<Wrapper />);
result.mount = mount(<Wrapper {...initialProps} />);
result.update = (props) => result.mount.setProps(props);

return result;
}
Expand Down Expand Up @@ -46,4 +47,65 @@ describe('usePopper', () => {
done();
});
});

it('should add aria-describedBy for tooltips', (done) => {
elements.popper.setAttribute('role', 'tooltip');
elements.popper.setAttribute('id', 'example123');

const result = renderHook(() =>
usePopper(elements.reference, elements.popper),
);

setTimeout(() => {
expect(
document.querySelector('[aria-describedby="example123"]'),
).to.equal(elements.reference);

result.mount.unmount();

expect(
document.querySelector('[aria-describedby="example123"]'),
).to.equal(null);

done();
});
});

it('should add to existing describedBy', (done) => {
elements.popper.setAttribute('role', 'tooltip');
elements.popper.setAttribute('id', 'example123');
elements.reference.setAttribute('aria-describedby', 'foo, bar , baz ');

const result = renderHook(() =>
usePopper(elements.reference, elements.popper),
);

setTimeout(() => {
expect(
document.querySelector(
'[aria-describedby="foo, bar , baz ,example123"]',
),
).to.equal(elements.reference);

result.mount.unmount();

expect(
document.querySelector('[aria-describedby="foo, bar , baz "]'),
).to.equal(elements.reference);

done();
});
});

it('should not aria-describedBy any other role', (done) => {
renderHook(() => usePopper(elements.reference, elements.popper));

setTimeout(() => {
expect(
document.querySelector('[aria-describedby="example123"]'),
).to.equal(null);

done();
});
});
});

0 comments on commit 941a5dc

Please sign in to comment.