mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-31 01:36:11 +00:00
[fix] Modal: refocus trigger-element after closing
Close #1821 Fix #1822
This commit is contained in:
committed by
Nicolas Gallagher
parent
e0cebea073
commit
58a8bbe094
@@ -123,6 +123,19 @@ const ModalFocusTrap = ({ active, children }: ModalFocusTrapProps) => {
|
|||||||
}
|
}
|
||||||
}, [active]);
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<FocusBracket />
|
<FocusBracket />
|
||||||
|
|||||||
@@ -280,6 +280,121 @@ describe('components/Modal', () => {
|
|||||||
expect(document.activeElement).toBe(insideElement);
|
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', () => {
|
test('focus is trapped when active', () => {
|
||||||
render(
|
render(
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user