Skip to content

Commit

Permalink
Merge pull request #34480 from shubham1206agra/fix-focus-behavior
Browse files Browse the repository at this point in the history
Fixed focus behavior and optimize active element role implementation
  • Loading branch information
Julesssss authored Feb 13, 2024
2 parents 784aa27 + bab646b commit b635cc5
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 38 deletions.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -78,6 +79,7 @@ function App({url}: AppProps) {
PickerStateProvider,
EnvironmentProvider,
CustomStatusBarAndBackgroundContextProvider,
ActiveElementRoleProvider,
ActiveWorkspaceContextProvider,
]}
>
Expand Down
20 changes: 20 additions & 0 deletions src/components/ActiveElementRoleProvider/index.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import type {ActiveElementRoleContextValue, ActiveElementRoleProps} from './types';

const ActiveElementRoleContext = React.createContext<ActiveElementRoleContextValue>({
role: null,
});

function ActiveElementRoleProvider({children}: ActiveElementRoleProps) {
const value = React.useMemo(
() => ({
role: null,
}),
[],
);

return <ActiveElementRoleContext.Provider value={value}>{children}</ActiveElementRoleContext.Provider>;
}

export default ActiveElementRoleProvider;
export {ActiveElementRoleContext};
40 changes: 40 additions & 0 deletions src/components/ActiveElementRoleProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, {useEffect, useState} from 'react';
import type {ActiveElementRoleContextValue, ActiveElementRoleProps} from './types';

const ActiveElementRoleContext = React.createContext<ActiveElementRoleContextValue>({
role: null,
});

function ActiveElementRoleProvider({children}: ActiveElementRoleProps) {
const [activeRoleRef, setRole] = useState<string | null>(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 <ActiveElementRoleContext.Provider value={value}>{children}</ActiveElementRoleContext.Provider>;
}

export default ActiveElementRoleProvider;
export {ActiveElementRoleContext};
9 changes: 9 additions & 0 deletions src/components/ActiveElementRoleProvider/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type ActiveElementRoleContextValue = {
role: string | null;
};

type ActiveElementRoleProps = {
children: React.ReactNode;
};

export type {ActiveElementRoleContextValue, ActiveElementRoleProps};
18 changes: 10 additions & 8 deletions src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,16 @@ function Button(

return (
<>
<KeyboardShortcutComponent
isDisabled={isDisabled}
isLoading={isLoading}
allowBubble={allowBubble}
onPress={onPress}
pressOnEnter={pressOnEnter}
enterKeyEventListenerPriority={enterKeyEventListenerPriority}
/>
{pressOnEnter && (
<KeyboardShortcutComponent
isDisabled={isDisabled}
isLoading={isLoading}
allowBubble={allowBubble}
onPress={onPress}
pressOnEnter={pressOnEnter}
enterKeyEventListenerPriority={enterKeyEventListenerPriority}
/>
)}
<PressableWithFeedback
ref={ref}
onPress={(event) => {
Expand Down
8 changes: 0 additions & 8 deletions src/hooks/useActiveElementRole/index.native.ts

This file was deleted.

25 changes: 4 additions & 21 deletions src/hooks/useActiveElementRole/index.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,15 @@
import {useEffect, useRef} from 'react';
import {useContext} from 'react';
import {ActiveElementRoleContext} from '@components/ActiveElementRoleProvider';
import type UseActiveElementRole from './types';

/**
* Listens for the focusin and focusout events and sets the DOM activeElement to the state.
* 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;
2 changes: 1 addition & 1 deletion src/hooks/useActiveElementRole/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
type UseActiveElementRole = () => string | null | undefined;
type UseActiveElementRole = () => string | null;

export default UseActiveElementRole;

0 comments on commit b635cc5

Please sign in to comment.