From 2dcededcf00feb2ee29a33be637d62ef333abff5 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Mon, 7 Jul 2025 17:10:51 +0200 Subject: [PATCH] fix: Fix can't skip search field by tabbing (#35281) --- .../features/compose/components/search.tsx | 51 ++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/search.tsx b/app/javascript/mastodon/features/compose/components/search.tsx index ae242190e..2d44772ba 100644 --- a/app/javascript/mastodon/features/compose/components/search.tsx +++ b/app/javascript/mastodon/features/compose/components/search.tsx @@ -47,10 +47,6 @@ const labelForRecentSearch = (search: RecentSearch) => { } }; -const unfocus = () => { - document.querySelector('.ui')?.parentElement?.focus(); -}; - const ClearButton: React.FC<{ onClick: () => void; hasValue: boolean; @@ -107,6 +103,11 @@ export const Search: React.FC<{ }, [initialValue]); const searchOptions: SearchOption[] = []; + const unfocus = useCallback(() => { + document.querySelector('.ui')?.parentElement?.focus(); + setExpanded(false); + }, []); + if (searchEnabled) { searchOptions.push( { @@ -282,7 +283,7 @@ export const Search: React.FC<{ history.push({ pathname: '/search', search: queryParams.toString() }); unfocus(); }, - [dispatch, history], + [dispatch, history, unfocus], ); const handleChange = useCallback( @@ -402,7 +403,7 @@ export const Search: React.FC<{ setQuickActions(newQuickActions); }, - [dispatch, history, signedIn, setValue, setQuickActions, submit], + [signedIn, dispatch, unfocus, history, submit], ); const handleClear = useCallback(() => { @@ -410,7 +411,7 @@ export const Search: React.FC<{ setQuickActions([]); setSelectedOption(-1); unfocus(); - }, [setValue, setQuickActions, setSelectedOption]); + }, [unfocus]); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { @@ -461,7 +462,7 @@ export const Search: React.FC<{ break; } }, - [navigableOptions, value, selectedOption, setSelectedOption, submit], + [unfocus, navigableOptions, selectedOption, submit, value], ); const handleFocus = useCallback(() => { @@ -481,12 +482,38 @@ export const Search: React.FC<{ }, [setExpanded, setSelectedOption, singleColumn]); const handleBlur = useCallback(() => { - setExpanded(false); setSelectedOption(-1); - }, [setExpanded, setSelectedOption]); + }, [setSelectedOption]); + + const formRef = useRef(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 ( -
+ -
+
{!hasValue && ( <>