11import { useFocusEffect , useIsFocused , useNavigation , usePreventRemove } from '@react-navigation/native' ;
22import { isSingleNewDotEntrySelector } from '@selectors/HybridApp' ;
33import type { ReactNode } from 'react' ;
4- import React , { useCallback , useContext , useEffect , useMemo , useState } from 'react' ;
5- import type { StyleProp , ViewStyle } from 'react-native' ;
4+ import React , { useCallback , useContext , useEffect , useMemo , useRef , useState } from 'react' ;
5+ import type { StyleProp , View , ViewStyle } from 'react-native' ;
66import { DeviceEventEmitter , Keyboard } from 'react-native' ;
77import type { EdgeInsets } from 'react-native-safe-area-context' ;
88import CustomDevMenu from '@components/CustomDevMenu' ;
@@ -16,7 +16,10 @@ import useOnyx from '@hooks/useOnyx';
1616import useResponsiveLayout from '@hooks/useResponsiveLayout' ;
1717import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings' ;
1818import useThemeStyles from '@hooks/useThemeStyles' ;
19+ import { isMobile } from '@libs/Browser' ;
1920import type { ForwardedFSClassProps } from '@libs/Fullstory/types' ;
21+ import getPlatform from '@libs/getPlatform' ;
22+ import mergeRefs from '@libs/mergeRefs' ;
2023import NarrowPaneContext from '@libs/Navigation/AppNavigator/Navigators/NarrowPaneContext' ;
2124import Navigation from '@libs/Navigation/Navigation' ;
2225import type { PlatformStackNavigationProp } from '@libs/Navigation/PlatformStackNavigation/types' ;
@@ -104,10 +107,13 @@ function ScreenWrapper({
104107 const navigationFallback = useNavigation < PlatformStackNavigationProp < RootNavigatorParamList > > ( ) ;
105108 const navigation = navigationProp ?? navigationFallback ;
106109 const isFocused = useIsFocused ( ) ;
110+ const screenWrapperRef = useRef < View | HTMLElement > ( null ) ;
111+ const mergedScreenWrapperRef = mergeRefs ( screenWrapperRef , ref ) ;
107112
108113 // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout for a case where we want to show the offline indicator only on small screens
109114 // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
110115 const { isSmallScreenWidth} = useResponsiveLayout ( ) ;
116+ const shouldMoveAccessibilityFocus = getPlatform ( ) === CONST . PLATFORM . WEB && isMobile ( ) ;
111117
112118 const styles = useThemeStyles ( ) ;
113119 const { isDevelopment} = useEnvironment ( ) ;
@@ -220,6 +226,44 @@ function ScreenWrapper({
220226 // eslint-disable-next-line react-hooks/exhaustive-deps
221227 } , [ ] ) ;
222228
229+ useEffect ( ( ) => {
230+ if ( ! shouldMoveAccessibilityFocus || ! didScreenTransitionEnd || ! isFocused ) {
231+ return ;
232+ }
233+
234+ if ( typeof document === 'undefined' ) {
235+ return ;
236+ }
237+
238+ const element = screenWrapperRef . current ;
239+ if ( ! element || ! ( 'contains' in element ) || ! ( 'querySelectorAll' in element ) ) {
240+ return ;
241+ }
242+
243+ const activeElement = document . activeElement ;
244+ if ( activeElement && element . contains ( activeElement ) ) {
245+ return ;
246+ }
247+
248+ const focusTargets = element . querySelectorAll < HTMLElement > ( 'button, [href], [role="button"], [role="link"], [tabindex]:not([tabindex="-1"])' ) ;
249+ for ( const focusTarget of focusTargets ) {
250+ const isDisabledTarget = focusTarget . matches ( ':disabled' ) || focusTarget . getAttribute ( 'aria-disabled' ) ?. toLowerCase ( ) === 'true' ;
251+ if ( isDisabledTarget || focusTarget . getAttribute ( 'aria-hidden' ) === 'true' ) {
252+ continue ;
253+ }
254+
255+ if ( focusTarget === activeElement ) {
256+ return ;
257+ }
258+
259+ focusTarget . focus ( ) ;
260+ const focusedElement = document . activeElement ;
261+ if ( focusedElement === focusTarget || ( focusedElement && focusTarget . contains ( focusedElement ) ) ) {
262+ return ;
263+ }
264+ }
265+ } , [ didScreenTransitionEnd , isFocused , shouldMoveAccessibilityFocus ] ) ;
266+
223267 useFocusEffect (
224268 useCallback ( ( ) => {
225269 // On iOS, the transitionEnd event doesn't trigger some times. As such, we need to set a timeout
@@ -255,7 +299,7 @@ function ScreenWrapper({
255299 return (
256300 < FocusTrapForScreen focusTrapSettings = { focusTrapSettings } >
257301 < ScreenWrapperContainer
258- ref = { ref }
302+ ref = { mergedScreenWrapperRef }
259303 style = { [ styles . flex1 , style ] }
260304 bottomContent = { bottomContent }
261305 didScreenTransitionEnd = { didScreenTransitionEnd }
0 commit comments