From 9e7f3690aaa74c6f222eb8752f21de390d97173c Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 25 Aug 2026 13:25:29 +0000 Subject: [PATCH] Fix search being auto-focused randomly (#40271) --- .../components/modal_shell/redesign.tsx | 3 ++- .../navigation_focus_target/index.tsx | 17 +++++++++++------ .../features/compose/components/search.tsx | 13 ++++++++++--- .../navigation_panel/redesign/index.tsx | 6 +++++- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/components/modal_shell/redesign.tsx b/app/javascript/mastodon/components/modal_shell/redesign.tsx index 4702269346e..059969cd39b 100644 --- a/app/javascript/mastodon/components/modal_shell/redesign.tsx +++ b/app/javascript/mastodon/components/modal_shell/redesign.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames'; import type { PolymorphicProps } from '@/types/polymorphic'; +import type { NamedFocusTarget } from '../navigation_focus_target'; import { NavigationFocusTarget } from '../navigation_focus_target'; import classes from './redesign.module.scss'; @@ -48,7 +49,7 @@ type HeadingLevels = 1 | 2 | 3 | 4 | 5 | 6; type ModalTitleProps = { children: React.ReactNode; level?: HeadingLevels } & ( | { noFocus: true } - | { noFocus?: false; focusTargetName?: string } + | { noFocus?: false; focusTargetName?: NamedFocusTarget } ); export const ModalTitle: React.FC< diff --git a/app/javascript/mastodon/components/navigation_focus_target/index.tsx b/app/javascript/mastodon/components/navigation_focus_target/index.tsx index 5a971685f29..56a5070fae8 100644 --- a/app/javascript/mastodon/components/navigation_focus_target/index.tsx +++ b/app/javascript/mastodon/components/navigation_focus_target/index.tsx @@ -14,11 +14,12 @@ import type { MastodonLocation } from '../router'; export const FOCUS_TARGET = { POST: 'detailed-status', + SEARCH: 'search', } as const; -export type FocusTarget = - | boolean - | (typeof FOCUS_TARGET)[keyof typeof FOCUS_TARGET]; +export type NamedFocusTarget = (typeof FOCUS_TARGET)[keyof typeof FOCUS_TARGET]; + +export type FocusTarget = boolean | NamedFocusTarget; const FocusTargetContext = createContext | null>( null, @@ -98,7 +99,7 @@ export const FocusTargetProvider: React.FC<{ ); }; -export function useFocusOnNavigation(targetName?: string) { +export function useFocusOnNavigation(targetName?: NamedFocusTarget) { const focusTargetRef = useContext(FocusTargetContext); return useCallback( @@ -110,7 +111,11 @@ export function useFocusOnNavigation(targetName?: string) { return; } - if (focusTarget === true || focusTarget === targetName) { + const shouldSetFocus = targetName + ? focusTarget === targetName + : focusTarget === true; + + if (shouldSetFocus) { setTimeout(() => { element.focus({ preventScroll: true }); }, 0); @@ -121,7 +126,7 @@ export function useFocusOnNavigation(targetName?: string) { } interface FocusTargetElementProps extends React.ComponentPropsWithoutRef<'h1'> { - focusTargetName?: string; + focusTargetName?: NamedFocusTarget; } export const NavigationFocusTarget = polymorphicForwardRef< diff --git a/app/javascript/mastodon/features/compose/components/search.tsx b/app/javascript/mastodon/features/compose/components/search.tsx index c8aa5ab08d2..8bdae5f91c9 100644 --- a/app/javascript/mastodon/features/compose/components/search.tsx +++ b/app/javascript/mastodon/features/compose/components/search.tsx @@ -19,9 +19,13 @@ import { useHistory } from 'react-router-dom'; import { isFulfilled } from '@reduxjs/toolkit'; -import { useFocusOnNavigation } from '@/mastodon/components/navigation_focus_target'; +import { + FOCUS_TARGET, + useFocusOnNavigation, +} from '@/mastodon/components/navigation_focus_target'; import { getCollectionPath } from '@/mastodon/features/collections/utils'; import { useMergedRefs } from '@/mastodon/hooks/useMergedRefs'; +import { isRedesignEnabled } from '@/mastodon/utils/environment'; import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import SearchIcon from '@/material-icons/400-24px/search.svg?react'; @@ -107,7 +111,7 @@ export const Search: React.FC<{ const [expanded, setExpanded] = useState(false); const [selectedOption, setSelectedOption] = useState(-1); const [quickActions, setQuickActions] = useState([]); - const focusOnNavigation = useFocusOnNavigation('search'); + const focusOnNavigation = useFocusOnNavigation(FOCUS_TARGET.SEARCH); const unfocus = useCallback(() => { document.querySelector('.ui')?.parentElement?.focus(); @@ -561,7 +565,10 @@ export const Search: React.FC<{ className={classNames('search', { active: expanded })} > = ({