-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Move pre-selected list items to top of list throughout the app #89584
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
Merged
robertjchen
merged 16 commits into
Expensify:main
from
marufsharifi:fix/preselected-items-top-of-list
May 19, 2026
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
3265456
Move pre-selected list items to top of list
marufsharifi 7055622
Merge branch 'main' into fix/preselected-items-top-of-list
marufsharifi 92c42f6
refactor: update mock for SelectionList items to use SingleSelectList…
marufsharifi 9dea8f0
test: update search focus sync scroll expectation
marufsharifi 755e980
refactor: consolidate initial selection list ordering helper
marufsharifi 1e044e6
simplify moveInitialSelectionToTop function by removing unused key e…
marufsharifi 8ed456e
fix: support focus sync search value in sectioned lists
marufsharifi fa28b1c
add highlighting for initially focused and selected items in Selectio…
marufsharifi df4a037
remove redundant alias variables for initiallyFocusedItemKey
marufsharifi f4cc1c2
Merge branch 'main' into fix/preselected-items-top-of-list
marufsharifi d28a9a1
remove highlighting for selected items
marufsharifi 75b570b
remove unnecessary tests
marufsharifi ea4b42f
apply fix in personal card country picker
marufsharifi 1f5b98a
Merge branch 'main' into fix/preselected-items-top-of-list
marufsharifi dc0733a
mock focused navigation state in country step test
marufsharifi 62d3a85
Merge branch 'main' into fix/preselected-items-top-of-list
marufsharifi 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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,56 @@ | ||
| import {useFocusEffect} from '@react-navigation/native'; | ||
| import {useCallback, useEffect, useRef, useState} from 'react'; | ||
|
|
||
| type UseInitialSelectionOptions = { | ||
| /** Whether the current cycle is visible; refresh the snapshot when it becomes visible */ | ||
| isVisible?: boolean; | ||
|
|
||
| /** Whether to refresh the snapshot whenever the screen gains focus */ | ||
| resetOnFocus?: boolean; | ||
| }; | ||
|
|
||
| /** | ||
| * Keeps an immutable snapshot of the initial selection for the current open/focus cycle. | ||
| * Callers can refresh the snapshot when a modal becomes visible or via screen focus. | ||
| */ | ||
| function useInitialSelection<T>(selection: T, options: UseInitialSelectionOptions = {}) { | ||
| const {isVisible, resetOnFocus = false} = options; | ||
| const [initialSelection, setInitialSelection] = useState(selection); | ||
| const latestSelectionRef = useRef(selection); | ||
| const previousIsVisibleRef = useRef(isVisible); | ||
|
|
||
| const updateInitialSelection = useCallback((nextSelection: T) => { | ||
| setInitialSelection((previousSelection) => (Object.is(previousSelection, nextSelection) ? previousSelection : nextSelection)); | ||
| }, []); | ||
|
|
||
| useEffect(() => { | ||
| latestSelectionRef.current = selection; | ||
| }, [selection]); | ||
|
|
||
| useEffect(() => { | ||
| const wasVisible = previousIsVisibleRef.current; | ||
| previousIsVisibleRef.current = isVisible; | ||
|
|
||
| if (isVisible === undefined || !isVisible || wasVisible === isVisible) { | ||
| return; | ||
| } | ||
|
|
||
| // Refresh only when a new visible cycle starts. | ||
| // Live selection changes while the picker stays open should not repin or refocus the list. | ||
| updateInitialSelection(latestSelectionRef.current); | ||
| }, [isVisible, updateInitialSelection]); | ||
|
|
||
| useFocusEffect( | ||
| useCallback(() => { | ||
| if (!resetOnFocus) { | ||
| return; | ||
| } | ||
|
|
||
| updateInitialSelection(latestSelectionRef.current); | ||
| }, [resetOnFocus, updateInitialSelection]), | ||
| ); | ||
|
|
||
| return initialSelection; | ||
| } | ||
|
|
||
| export default useInitialSelection; | ||
This file contains hidden or 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,24 @@ | ||
| import CONST from '@src/CONST'; | ||
|
|
||
| function moveInitialSelectionToTop<T extends {value: string}>(items: T[], initialSelectedValues: string[]): T[] { | ||
| if (initialSelectedValues.length === 0 || items.length <= CONST.MOVE_SELECTED_ITEMS_TO_TOP_OF_LIST_THRESHOLD) { | ||
| return items; | ||
| } | ||
|
|
||
| const selectedValues = new Set(initialSelectedValues); | ||
| const selected: T[] = []; | ||
| const remaining: T[] = []; | ||
|
|
||
| for (const item of items) { | ||
| if (selectedValues.has(item.value)) { | ||
| selected.push(item); | ||
| continue; | ||
| } | ||
|
|
||
| remaining.push(item); | ||
| } | ||
|
|
||
| return [...selected, ...remaining]; | ||
| } | ||
|
|
||
| export default moveInitialSelectionToTop; |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.