[fix] Modal: refocus trigger-element after closing

Close #1821
Fix #1822
This commit is contained in:
Rafał Zakrzewski
2020-11-25 17:08:04 +01:00
committed by Nicolas Gallagher
parent e0cebea073
commit 58a8bbe094
2 changed files with 128 additions and 0 deletions
@@ -123,6 +123,19 @@ const ModalFocusTrap = ({ active, children }: ModalFocusTrapProps) => {
}
}, [active]);
// To be fully compliant with WCAG we need to refocus element that triggered opening modal
// after closing it
useEffect(function() {
if (canUseDOM) {
const lastFocusedElementOutsideTrap = document.activeElement;
return function() {
if (lastFocusedElementOutsideTrap && document.contains(lastFocusedElementOutsideTrap)) {
UIManager.focus(lastFocusedElementOutsideTrap);
}
};
}
}, []);
return (
<>
<FocusBracket />
@@ -280,6 +280,121 @@ describe('components/Modal', () => {
expect(document.activeElement).toBe(insideElement);
});
test('focus is brought back to the element that triggered modal after closing', () => {
const { rerender } = render(
<>
<a data-testid={'outside'} href={'#outside'}>
Outside
</a>
<Modal visible={false}>
<a data-testid={'inside'} href={'#hello'}>
Hello
</a>
</Modal>
<a data-testid={'modal-trigger'} href={'#modal-trigger'}>
Outside
</a>
</>
);
const modalTrigger = document.querySelector('[data-testid="modal-trigger"]');
modalTrigger.focus();
expect(document.activeElement).toBe(modalTrigger);
rerender(
<>
<a data-testid={'outside'} href={'#outside'}>
Outside
</a>
<Modal visible={true}>
<a data-testid={'inside'} href={'#hello'}>
Hello
</a>
</Modal>
<a data-testid={'modal-trigger'} href={'#modal-trigger'}>
Outside
</a>
</>
);
const insideElement = document.querySelector('[data-testid="inside"]');
expect(document.activeElement).toBe(insideElement);
rerender(
<>
<a data-testid={'outside'} href={'#outside'}>
Outside
</a>
<Modal visible={false}>
<a data-testid={'inside'} href={'#hello'}>
Hello
</a>
</Modal>
<a data-testid={'modal-trigger'} href={'#modal-trigger'}>
Outside
</a>
</>
);
expect(document.activeElement).toBe(modalTrigger);
});
test('focus is brought back to the body when element that triggered modal is removed from the DOM after closing modal', () => {
const { rerender } = render(
<>
<a data-testid={'outside'} href={'#outside'}>
Outside
</a>
<Modal visible={false}>
<a data-testid={'inside'} href={'#hello'}>
Hello
</a>
</Modal>
<a data-testid={'modal-trigger'} href={'#modal-trigger'}>
Outside
</a>
</>
);
const modalTrigger = document.querySelector('[data-testid="modal-trigger"]');
modalTrigger.focus();
expect(document.activeElement).toBe(modalTrigger);
rerender(
<>
<a data-testid={'outside'} href={'#outside'}>
Outside
</a>
<Modal visible={true}>
<a data-testid={'inside'} href={'#hello'}>
Hello
</a>
</Modal>
<a data-testid={'modal-trigger'} href={'#modal-trigger'}>
Outside
</a>
</>
);
const insideElement = document.querySelector('[data-testid="inside"]');
expect(document.activeElement).toBe(insideElement);
rerender(
<>
<a data-testid={'outside'} href={'#outside'}>
Outside
</a>
<Modal visible={false}>
<a data-testid={'inside'} href={'#hello'}>
Hello
</a>
</Modal>
</>
);
expect(document.activeElement).toBe(document.body);
});
test('focus is trapped when active', () => {
render(
<>