Mirror: React hooks for accessible, common web interactions. UI super powers without the UI.

Remove useDismissable triggering on focus contains

Nested useDismissable's would still trigger if the nested element
contains focus, since they're nested. This would trigger an
unintentional cascade dismiss.

Changed files
+2 -33
src
-25
src/__tests__/useDismissable.test.tsx
···
cy.get('@inside').should('not.be.visible');
});
-
it('is dismissed without priority when it has focus', () => {
-
const Second = () => {
-
const ref = useRef<HTMLDivElement>(null);
-
useDismissable(ref, () => {});
-
return <div ref={ref} />;
-
};
-
-
mount(
-
<main>
-
<button className="outside">outside</button>
-
<Dialog />
-
<Second />
-
</main>
-
);
-
-
cy.get('.inside').as('inside').should('be.visible');
-
// not dismissed with escape press
-
cy.realPress('Escape');
-
cy.get('@inside').should('be.visible');
-
// is dismissed when it has focus
-
cy.get('@inside').focus();
-
cy.realPress('Escape');
-
cy.get('@inside').should('not.be.visible');
-
});
-
it('is dismissed when focus moves out of it, with focus loss active', () => {
mount(
<main>
+2 -8
src/useDismissable.ts
···
return;
}
-
const active = document.activeElement;
-
if (
-
event.code === 'Escape' &&
-
(hasPriority.current || (active && contains(element, active)))
-
) {
+
if (event.code === 'Escape' && hasPriority.current) {
// The current dialog can be dismissed by pressing escape if it either has focus
// or it has priority
event.preventDefault();
···
function onClick(event: MouseEvent | TouchEvent) {
const { target } = event;
-
const active = document.activeElement;
if (event.defaultPrevented) {
return;
} else if (contains(element, target)) {
willLoseFocus = false;
return;
-
} else if (hasPriority || (active && contains(element, active))) {
+
} else if (hasPriority.current) {
// The current dialog can be dismissed by pressing outside of it if it either has
// focus or it has priority
-
event.preventDefault();
onDismissRef.current();
}
}