-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix - Web - Blue selection border on plan option after a refresh #54682
Merged
cristipaval
merged 16 commits into
Expensify:main
from
FitseTLT:fix-blue-selection-on-page-refresh
Feb 4, 2025
Merged
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
920fd89
update useSyncFocus to operate only on isFocused change
FitseTLT 80a568c
added test
FitseTLT 5002fc2
fix test
FitseTLT d138c13
Merge branch 'main' into fix-blue-selection-on-page-refresh
FitseTLT f8a0691
allow focus sync only after key press
FitseTLT c78599c
fix test
FitseTLT 2bfaa1e
Merge branch 'main' into fix-blue-selection-on-page-refresh
FitseTLT 4c13d8b
Merge branch 'main' into fix-blue-selection-on-page-refresh
FitseTLT 32a2550
fix sync focus not working on first key press
FitseTLT 6cc1de7
remove previous focus condition
FitseTLT 090183c
rename the ref
FitseTLT 0ad24b4
Merge branch 'main' into fix-blue-selection-on-page-refresh
FitseTLT 64dc0d0
resolved conflict
FitseTLT 5374ae9
Merge branch 'main' into fix-blue-selection-on-page-refresh
FitseTLT 189cfb7
Added another check
FitseTLT 1c9c6a7
added comment
FitseTLT File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,3 @@ | ||
import {useContext, useLayoutEffect} from 'react'; | ||
import type {RefObject} from 'react'; | ||
import type {View} from 'react-native'; | ||
import {ScreenWrapperStatusContext} from '@components/ScreenWrapper'; | ||
import useSyncFocusImplementation from './useSyncFocusImplementation'; | ||
|
||
/** | ||
* Custom React hook created to handle sync of focus on an element when the user navigates through the app with keyboard. | ||
* When the user navigates through the app using the arrows and then the tab button, the focus on the element and the native focus of the browser differs. | ||
* To maintain consistency when an element is focused in the app, the focus() method is additionally called on the focused element to eliminate the difference between native browser focus and application focus. | ||
*/ | ||
const useSyncFocus = (ref: RefObject<View>, isFocused: boolean, shouldSyncFocus = true) => { | ||
// this hook can be used outside ScreenWrapperStatusContext (eg. in Popovers). So we to check if the context is present. | ||
// If we are outside context we don't have to look at transition status | ||
const contextValue = useContext(ScreenWrapperStatusContext); | ||
|
||
const didScreenTransitionEnd = contextValue ? contextValue.didScreenTransitionEnd : true; | ||
|
||
useLayoutEffect(() => { | ||
if (!isFocused || !shouldSyncFocus || !didScreenTransitionEnd) { | ||
return; | ||
} | ||
|
||
ref.current?.focus(); | ||
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps | ||
}, [didScreenTransitionEnd, isFocused, ref]); | ||
}; | ||
|
||
export default useSyncFocus; | ||
export default useSyncFocusImplementation; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import {useContext, useLayoutEffect} from 'react'; | ||
FitseTLT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import type {RefObject} from 'react'; | ||
import type {View} from 'react-native'; | ||
import {ScreenWrapperStatusContext} from '@components/ScreenWrapper'; | ||
import usePrevious from '@hooks/usePrevious'; | ||
|
||
/** | ||
* Custom React hook created to handle sync of focus on an element when the user navigates through the app with keyboard. | ||
* When the user navigates through the app using the arrows and then the tab button, the focus on the element and the native focus of the browser differs. | ||
* To maintain consistency when an element is focused in the app, the focus() method is additionally called on the focused element to eliminate the difference between native browser focus and application focus. | ||
*/ | ||
const useSyncFocusImplementation = (ref: RefObject<View>, isFocused: boolean, shouldSyncFocus = true) => { | ||
// this hook can be used outside ScreenWrapperStatusContext (eg. in Popovers). So we to check if the context is present. | ||
// If we are outside context we don't have to look at transition status | ||
const contextValue = useContext(ScreenWrapperStatusContext); | ||
|
||
const didScreenTransitionEnd = contextValue ? contextValue.didScreenTransitionEnd : true; | ||
|
||
const prevIsFocused = usePrevious(isFocused); | ||
FitseTLT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
useLayoutEffect(() => { | ||
if (!(isFocused && !prevIsFocused) || !shouldSyncFocus || !didScreenTransitionEnd) { | ||
return; | ||
} | ||
|
||
ref.current?.focus(); | ||
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps | ||
}, [didScreenTransitionEnd, isFocused, ref]); | ||
}; | ||
|
||
export default useSyncFocusImplementation; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import {renderHook} from '@testing-library/react-native'; | ||
import type {RefObject} from 'react'; | ||
import type {View} from 'react-native'; | ||
import useSyncFocus from '@hooks/useSyncFocus/useSyncFocusImplementation'; | ||
|
||
describe('useSyncFocus', () => { | ||
it('useSyncFocus should only focus when isFocused changes from false to true', () => { | ||
const refMock = {current: {focus: jest.fn()}}; | ||
|
||
// When useSyncFocus is rendered initially with isFocused true | ||
const {rerender} = renderHook( | ||
({ | ||
ref = refMock, | ||
isFocused = true, | ||
}: { | ||
isFocused?: boolean; | ||
ref?: { | ||
current: { | ||
focus: jest.Mock; | ||
}; | ||
}; | ||
}) => useSyncFocus(ref as unknown as RefObject<View>, isFocused), | ||
{initialProps: {}}, | ||
); | ||
// Then the ref focus will not be called. | ||
expect(refMock.current.focus).not.toHaveBeenCalled(); | ||
|
||
const event = new KeyboardEvent('keydown', {key: 'Tab'}); | ||
document.dispatchEvent(event); | ||
|
||
// When isFocused changes from false to true | ||
rerender({isFocused: false}); | ||
rerender({isFocused: true}); | ||
// Then the ref focus will be called. | ||
expect(refMock.current.focus).toHaveBeenCalled(); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same, some comments might help for whoever comes here in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cristipaval I added a comment here
App/src/components/SelectionList/BaseSelectionList.tsx
Lines 328 to 330 in 1c9c6a7
Isn't that enough? Because here the name of the ref already gives a hint why it is being set on key down event. WDYT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I think that should be enough to know the reason for this code. Thanks