1
0
Fork 0

feat(docsearch): trap focus in modal

This commit is contained in:
François Chalifour 2020-04-10 14:36:19 +02:00
parent a73d050c80
commit ec7df43e80
2 changed files with 46 additions and 0 deletions

View file

@ -14,6 +14,7 @@ import {
} from './types';
import { createSearchClient, groupBy, noop } from './utils';
import { createStoredSearches } from './stored-searches';
import { useTrapFocus } from './useTrapFocus';
import { Hit } from './Hit';
import { SearchBox } from './SearchBox';
import { ScreenState } from './ScreenState';
@ -50,6 +51,7 @@ export function DocSearch({
suggestions: [],
} as any);
const containerRef = React.useRef<HTMLDivElement | null>(null);
const searchBoxRef = React.useRef<HTMLDivElement | null>(null);
const dropdownRef = React.useRef<HTMLDivElement | null>(null);
const inputRef = React.useRef<HTMLInputElement | null>(null);
@ -63,6 +65,8 @@ export function DocSearch({
: ''
).current;
useTrapFocus({ container: containerRef.current });
const searchClient = React.useMemo(() => createSearchClient(appId, apiKey), [
appId,
apiKey,
@ -320,6 +324,7 @@ export function DocSearch({
return (
<div
ref={containerRef}
className={[
'DocSearch-Container',
state.status === 'stalled' && 'DocSearch-Container--Stalled',

41
src/useTrapFocus.ts Normal file
View file

@ -0,0 +1,41 @@
import React from 'react';
interface UseTrapFocusProps {
container: HTMLElement | null;
}
export function useTrapFocus({ container }: UseTrapFocusProps) {
React.useEffect(() => {
if (!container) {
return undefined;
}
const focusableElements = container.querySelectorAll<HTMLElement>(
'a[href]:not([disabled]), button:not([disabled]), input:not([disabled])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
function trapFocus(event: KeyboardEvent) {
if (event.key !== 'Tab') {
return;
}
if (event.shiftKey) {
if (document.activeElement === firstElement) {
event.preventDefault();
lastElement.focus();
}
} else if (document.activeElement === lastElement) {
event.preventDefault();
firstElement.focus();
}
}
container.addEventListener('keydown', trapFocus);
return () => {
container.removeEventListener('keydown', trapFocus);
};
}, [container]);
}