fix: Fix can't skip search field by tabbing (#35281)

This commit is contained in:
diondiondion 2025-07-07 17:10:51 +02:00 committed by Claire
commit 2dcededcf0

View file

@ -47,10 +47,6 @@ const labelForRecentSearch = (search: RecentSearch) => {
} }
}; };
const unfocus = () => {
document.querySelector('.ui')?.parentElement?.focus();
};
const ClearButton: React.FC<{ const ClearButton: React.FC<{
onClick: () => void; onClick: () => void;
hasValue: boolean; hasValue: boolean;
@ -107,6 +103,11 @@ export const Search: React.FC<{
}, [initialValue]); }, [initialValue]);
const searchOptions: SearchOption[] = []; const searchOptions: SearchOption[] = [];
const unfocus = useCallback(() => {
document.querySelector('.ui')?.parentElement?.focus();
setExpanded(false);
}, []);
if (searchEnabled) { if (searchEnabled) {
searchOptions.push( searchOptions.push(
{ {
@ -282,7 +283,7 @@ export const Search: React.FC<{
history.push({ pathname: '/search', search: queryParams.toString() }); history.push({ pathname: '/search', search: queryParams.toString() });
unfocus(); unfocus();
}, },
[dispatch, history], [dispatch, history, unfocus],
); );
const handleChange = useCallback( const handleChange = useCallback(
@ -402,7 +403,7 @@ export const Search: React.FC<{
setQuickActions(newQuickActions); setQuickActions(newQuickActions);
}, },
[dispatch, history, signedIn, setValue, setQuickActions, submit], [signedIn, dispatch, unfocus, history, submit],
); );
const handleClear = useCallback(() => { const handleClear = useCallback(() => {
@ -410,7 +411,7 @@ export const Search: React.FC<{
setQuickActions([]); setQuickActions([]);
setSelectedOption(-1); setSelectedOption(-1);
unfocus(); unfocus();
}, [setValue, setQuickActions, setSelectedOption]); }, [unfocus]);
const handleKeyDown = useCallback( const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => { (e: React.KeyboardEvent) => {
@ -461,7 +462,7 @@ export const Search: React.FC<{
break; break;
} }
}, },
[navigableOptions, value, selectedOption, setSelectedOption, submit], [unfocus, navigableOptions, selectedOption, submit, value],
); );
const handleFocus = useCallback(() => { const handleFocus = useCallback(() => {
@ -481,12 +482,38 @@ export const Search: React.FC<{
}, [setExpanded, setSelectedOption, singleColumn]); }, [setExpanded, setSelectedOption, singleColumn]);
const handleBlur = useCallback(() => { const handleBlur = useCallback(() => {
setExpanded(false);
setSelectedOption(-1); setSelectedOption(-1);
}, [setExpanded, setSelectedOption]); }, [setSelectedOption]);
const formRef = useRef<HTMLFormElement>(null);
useEffect(() => {
// If the search popover is expanded, close it when tabbing or
// clicking outside of it or the search form, while allowing
// tabbing or clicking inside of the popover
if (expanded) {
function closeOnLeave(event: FocusEvent | MouseEvent) {
const form = formRef.current;
const isClickInsideForm =
form &&
(form === event.target || form.contains(event.target as Node));
if (!isClickInsideForm) {
setExpanded(false);
}
}
document.addEventListener('focusin', closeOnLeave);
document.addEventListener('click', closeOnLeave);
return () => {
document.removeEventListener('focusin', closeOnLeave);
document.removeEventListener('click', closeOnLeave);
};
}
return () => null;
}, [expanded]);
return ( return (
<form className={classNames('search', { active: expanded })}> <form ref={formRef} className={classNames('search', { active: expanded })}>
<input <input
ref={searchInputRef} ref={searchInputRef}
className='search__input' className='search__input'
@ -506,7 +533,7 @@ export const Search: React.FC<{
<ClearButton hasValue={hasValue} onClick={handleClear} /> <ClearButton hasValue={hasValue} onClick={handleClear} />
<div className='search__popout'> <div className='search__popout' tabIndex={-1}>
{!hasValue && ( {!hasValue && (
<> <>
<h4> <h4>