diff --git a/src/App.tsx b/src/App.tsx index 7c1ead1d86d3..52baedc79421 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,6 +6,7 @@ import Onyx from 'react-native-onyx'; import {PickerStateProvider} from 'react-native-picker-select'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import '../wdyr'; +import ActiveElementRoleProvider from './components/ActiveElementRoleProvider'; import ActiveWorkspaceContextProvider from './components/ActiveWorkspace/ActiveWorkspaceProvider'; import ColorSchemeWrapper from './components/ColorSchemeWrapper'; import ComposeProviders from './components/ComposeProviders'; @@ -78,6 +79,7 @@ function App({url}: AppProps) { PickerStateProvider, EnvironmentProvider, CustomStatusBarAndBackgroundContextProvider, + ActiveElementRoleProvider, ActiveWorkspaceContextProvider, ]} > diff --git a/src/components/ActiveElementRoleProvider/index.native.tsx b/src/components/ActiveElementRoleProvider/index.native.tsx new file mode 100644 index 000000000000..4a9f2290b2b0 --- /dev/null +++ b/src/components/ActiveElementRoleProvider/index.native.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import type {ActiveElementRoleContextValue, ActiveElementRoleProps} from './types'; + +const ActiveElementRoleContext = React.createContext({ + role: null, +}); + +function ActiveElementRoleProvider({children}: ActiveElementRoleProps) { + const value = React.useMemo( + () => ({ + role: null, + }), + [], + ); + + return {children}; +} + +export default ActiveElementRoleProvider; +export {ActiveElementRoleContext}; diff --git a/src/components/ActiveElementRoleProvider/index.tsx b/src/components/ActiveElementRoleProvider/index.tsx new file mode 100644 index 000000000000..630af8618c08 --- /dev/null +++ b/src/components/ActiveElementRoleProvider/index.tsx @@ -0,0 +1,40 @@ +import React, {useEffect, useState} from 'react'; +import type {ActiveElementRoleContextValue, ActiveElementRoleProps} from './types'; + +const ActiveElementRoleContext = React.createContext({ + role: null, +}); + +function ActiveElementRoleProvider({children}: ActiveElementRoleProps) { + const [activeRoleRef, setRole] = useState(document?.activeElement?.role ?? null); + + const handleFocusIn = () => { + setRole(document?.activeElement?.role ?? null); + }; + + const handleFocusOut = () => { + setRole(null); + }; + + useEffect(() => { + document.addEventListener('focusin', handleFocusIn); + document.addEventListener('focusout', handleFocusOut); + + return () => { + document.removeEventListener('focusin', handleFocusIn); + document.removeEventListener('focusout', handleFocusOut); + }; + }, []); + + const value = React.useMemo( + () => ({ + role: activeRoleRef, + }), + [activeRoleRef], + ); + + return {children}; +} + +export default ActiveElementRoleProvider; +export {ActiveElementRoleContext}; diff --git a/src/components/ActiveElementRoleProvider/types.ts b/src/components/ActiveElementRoleProvider/types.ts new file mode 100644 index 000000000000..f22343b12550 --- /dev/null +++ b/src/components/ActiveElementRoleProvider/types.ts @@ -0,0 +1,9 @@ +type ActiveElementRoleContextValue = { + role: string | null; +}; + +type ActiveElementRoleProps = { + children: React.ReactNode; +}; + +export type {ActiveElementRoleContextValue, ActiveElementRoleProps}; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index f4b6e8b23ecf..1961829b6aa7 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -265,14 +265,16 @@ function Button( return ( <> - + {pressOnEnter && ( + + )} { diff --git a/src/hooks/useActiveElementRole/index.native.ts b/src/hooks/useActiveElementRole/index.native.ts deleted file mode 100644 index 4278014f02a8..000000000000 --- a/src/hooks/useActiveElementRole/index.native.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type UseActiveElementRole from './types'; - -/** - * Native doesn't have the DOM, so we just return null. - */ -const useActiveElementRole: UseActiveElementRole = () => null; - -export default useActiveElementRole; diff --git a/src/hooks/useActiveElementRole/index.ts b/src/hooks/useActiveElementRole/index.ts index a98999105ac8..98ae285f92b0 100644 --- a/src/hooks/useActiveElementRole/index.ts +++ b/src/hooks/useActiveElementRole/index.ts @@ -1,4 +1,5 @@ -import {useEffect, useRef} from 'react'; +import {useContext} from 'react'; +import {ActiveElementRoleContext} from '@components/ActiveElementRoleProvider'; import type UseActiveElementRole from './types'; /** @@ -6,27 +7,9 @@ import type UseActiveElementRole from './types'; * On native, we just return null. */ const useActiveElementRole: UseActiveElementRole = () => { - const activeRoleRef = useRef(document?.activeElement?.role); + const {role} = useContext(ActiveElementRoleContext); - const handleFocusIn = () => { - activeRoleRef.current = document?.activeElement?.role; - }; - - const handleFocusOut = () => { - activeRoleRef.current = null; - }; - - useEffect(() => { - document.addEventListener('focusin', handleFocusIn); - document.addEventListener('focusout', handleFocusOut); - - return () => { - document.removeEventListener('focusin', handleFocusIn); - document.removeEventListener('focusout', handleFocusOut); - }; - }, []); - - return activeRoleRef.current; + return role; }; export default useActiveElementRole; diff --git a/src/hooks/useActiveElementRole/types.ts b/src/hooks/useActiveElementRole/types.ts index c31b8ab7ddbf..f6884548785f 100644 --- a/src/hooks/useActiveElementRole/types.ts +++ b/src/hooks/useActiveElementRole/types.ts @@ -1,3 +1,3 @@ -type UseActiveElementRole = () => string | null | undefined; +type UseActiveElementRole = () => string | null; export default UseActiveElementRole;