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

Adjust useDialogFocus untabbable behaviour

Changed files
+26 -24
src
+9 -8
src/__tests__/useDialogFocus.test.tsx
···
const ref = useRef<HTMLUListElement>(null);
useDialogFocus(ref);
return (
-
<ul ref={ref} role="dialog">
-
<li tabIndex={0}>#1</li>
-
<li tabIndex={0}>#2</li>
-
<li tabIndex={0}>#3</li>
-
</ul>
+
<div>
+
<input type="text" name="text" />
+
<ul ref={ref} role="dialog">
+
<li tabIndex={0}>#1</li>
+
<li tabIndex={0}>#2</li>
+
<li tabIndex={0}>#3</li>
+
</ul>
+
</div>
);
};
const App = () => {
-
const [hasDialog, setDialog] = useState(false);
return (
<main>
<button>before</button>
-
<input type="text" name="text" onFocus={() => setDialog(true)} />
-
{hasDialog && <Dialog />}
+
<Dialog />
<button>after</button>
</main>
);
+9 -16
src/useDialogFocus.ts
···
getNextFocusTarget,
} from './utils/focus';
import { useLayoutEffect } from './utils/react';
-
import { contains, isInputElement } from './utils/element';
+
import { contains, focus, isInputElement } from './utils/element';
import { makePriorityHook } from './usePriority';
import { Ref } from './types';
···
return;
}
-
const { relatedTarget, target } = event;
// Check whether focus is about to move into the container and prevent it
-
if (
-
contains(ref.current, target) &&
-
!contains(ref.current, relatedTarget)
-
) {
+
if (contains(ref.current, event.target)) {
+
event.preventDefault();
// Get the next focus target of the container
const focusTarget = getNextFocusTarget(element, !focusMovesForward);
-
if (focusTarget) {
-
focusMovesForward = true;
-
event.preventDefault();
-
focusTarget.focus();
-
}
+
focusMovesForward = true;
+
focus(focusTarget);
}
}
···
return;
} else if (event.code === 'Tab') {
// Skip over the listbox via the parent if we press tab
+
event.preventDefault();
const currentTarget = contains(owner, active) ? owner! : element;
-
const focusTarget = getNextFocusTarget(currentTarget, event.shiftKey);
-
if (focusTarget) {
-
event.preventDefault();
-
focusTarget.focus();
-
}
+
const newTarget = getNextFocusTarget(currentTarget, event.shiftKey);
+
if (newTarget) focus(newTarget);
} else if (
(!isInputElement(active) && event.code === 'ArrowRight') ||
event.code === 'ArrowDown'
···
/^(?:Key|Digit)/.test(event.code)
) {
// Restore selection if a key is pressed on input
+
event.preventDefault();
willReceiveFocus = false;
restoreSelection(selection);
}
+8
src/utils/element.ts
···
owner &&
(owner === node || (owner as Element).contains(node as Element))
);
+
+
export const focus = (element: Element | null) => {
+
if (element) {
+
(element as HTMLElement).focus();
+
} else if (document.activeElement) {
+
(document.activeElement as HTMLElement).blur();
+
}
+
};