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

Configure Feed

Select the types of activity you want to include in your feed.

Adjust useDialogFocus untabbable behaviour

+26 -24
+9 -8
src/__tests__/useDialogFocus.test.tsx
··· 61 61 const ref = useRef<HTMLUListElement>(null); 62 62 useDialogFocus(ref); 63 63 return ( 64 - <ul ref={ref} role="dialog"> 65 - <li tabIndex={0}>#1</li> 66 - <li tabIndex={0}>#2</li> 67 - <li tabIndex={0}>#3</li> 68 - </ul> 64 + <div> 65 + <input type="text" name="text" /> 66 + <ul ref={ref} role="dialog"> 67 + <li tabIndex={0}>#1</li> 68 + <li tabIndex={0}>#2</li> 69 + <li tabIndex={0}>#3</li> 70 + </ul> 71 + </div> 69 72 ); 70 73 }; 71 74 72 75 const App = () => { 73 - const [hasDialog, setDialog] = useState(false); 74 76 return ( 75 77 <main> 76 78 <button>before</button> 77 - <input type="text" name="text" onFocus={() => setDialog(true)} /> 78 - {hasDialog && <Dialog />} 79 + <Dialog /> 79 80 <button>after</button> 80 81 </main> 81 82 );
+9 -16
src/useDialogFocus.ts
··· 5 5 getNextFocusTarget, 6 6 } from './utils/focus'; 7 7 import { useLayoutEffect } from './utils/react'; 8 - import { contains, isInputElement } from './utils/element'; 8 + import { contains, focus, isInputElement } from './utils/element'; 9 9 import { makePriorityHook } from './usePriority'; 10 10 import { Ref } from './types'; 11 11 ··· 59 59 return; 60 60 } 61 61 62 - const { relatedTarget, target } = event; 63 62 // Check whether focus is about to move into the container and prevent it 64 - if ( 65 - contains(ref.current, target) && 66 - !contains(ref.current, relatedTarget) 67 - ) { 63 + if (contains(ref.current, event.target)) { 64 + event.preventDefault(); 68 65 // Get the next focus target of the container 69 66 const focusTarget = getNextFocusTarget(element, !focusMovesForward); 70 - if (focusTarget) { 71 - focusMovesForward = true; 72 - event.preventDefault(); 73 - focusTarget.focus(); 74 - } 67 + focusMovesForward = true; 68 + focus(focusTarget); 75 69 } 76 70 } 77 71 ··· 98 92 return; 99 93 } else if (event.code === 'Tab') { 100 94 // Skip over the listbox via the parent if we press tab 95 + event.preventDefault(); 101 96 const currentTarget = contains(owner, active) ? owner! : element; 102 - const focusTarget = getNextFocusTarget(currentTarget, event.shiftKey); 103 - if (focusTarget) { 104 - event.preventDefault(); 105 - focusTarget.focus(); 106 - } 97 + const newTarget = getNextFocusTarget(currentTarget, event.shiftKey); 98 + if (newTarget) focus(newTarget); 107 99 } else if ( 108 100 (!isInputElement(active) && event.code === 'ArrowRight') || 109 101 event.code === 'ArrowDown' ··· 165 157 /^(?:Key|Digit)/.test(event.code) 166 158 ) { 167 159 // Restore selection if a key is pressed on input 160 + event.preventDefault(); 168 161 willReceiveFocus = false; 169 162 restoreSelection(selection); 170 163 }
+8
src/utils/element.ts
··· 32 32 owner && 33 33 (owner === node || (owner as Element).contains(node as Element)) 34 34 ); 35 + 36 + export const focus = (element: Element | null) => { 37 + if (element) { 38 + (element as HTMLElement).focus(); 39 + } else if (document.activeElement) { 40 + (document.activeElement as HTMLElement).blur(); 41 + } 42 + };