fix: stabilize reopen ordering in basic pickers#85447
fix: stabilize reopen ordering in basic pickers#85447marufsharifi wants to merge 11 commits intoExpensify:mainfrom
Conversation
|
Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers! |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
linhvovan29546
left a comment
There was a problem hiding this comment.
LGTM! Could you please prioritize this one so we can hopefully merge it today?
There was a problem hiding this comment.
@marufsharifi Could you also apply this change to the DynamicCountrySelectionPage, StateSelectionPage, CountrySelectionList and CountrySelection component?
|
Bug: After removing/clearing the search, the selected item is no longer highlighted. Screen.Recording.2026-03-17.at.16.10.56.mov |
|
Bug: After clearing the search, the selection list automatically scrolls Screen.Recording.2026-03-17.at.16.12.03.mov |
|
Sorry for the delay i was OOO, i will update here today. Thanks. |
|
@linhvovan29546 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
…allers Rename useInitialSelectionRef to useInitialSelection to match its reactive state return value, add a clarifying comment about why the snapshot only resets on open/focus boundaries, and switch the current single-selection picker callers to pass scalar values instead of string arrays.
Pass the debounced search value into SelectionList focus synchronization for the basic picker flows so search-clear transitions use the same state that drives the rendered data.
Fixed. Root cause was that the picker data was driven by I updated the affected first-PR pickers to pass the debounced search value into the shared focus-sync path, and added a focused regression test around |
Also fixed. This turned out to have the same root cause as the first regression. The picker data was built from Aligning the focus-sync path with the debounced search state fixed that transition as well. |
… flows Apply the initial-selection snapshot and reopen-only ordering behavior to the dynamic profile country/state pickers and the shared wallet country selection list. Search results now keep their natural filtered order while focus sync follows the debounced search value, and the wallet country list matches the no-focus-scroll behavior used in the first picker slice.
Keep the pre-existing memoization already present on main, but avoid the new manual useMemo patterns introduced in this picker-ordering work where they are not needed.
Include `isSelected` on the base country options used by `SelectCountryStep` so the reordered and searched lists satisfy the shared `searchOptions` option shape, and update the test to select a real rendered row instead of constructing a narrower list item.
|
@linhvovan29546, sorry for the delay. I've addressed all your comments, and I am completing the PR Author checklist. You can continue your review. thanks. |
trjExpensify
left a comment
There was a problem hiding this comment.
Please add tests. Also, CC: @Expensify/design as I think ya'll will want eyes on this one!
|
I like the interaction in the video |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-03-31.at.14.22.35.movAndroid: mWeb ChromeScreen.Recording.2026-03-31.at.14.20.36.moviOS: HybridAppScreen.Recording.2026-03-31.at.14.18.48.moviOS: mWeb SafariScreen.Recording.2026-03-31.at.14.16.49.movMacOS: Chrome / SafariScreen.Recording.2026-03-30.at.22.01.48.mov |
|
@marufsharifi Please add the test steps and QA steps |
|
Yeah same, feeling good to me too! Goodbye to all those annoying page jumps, finally! |
|
I am still testing; it needs more testing on all platforms. @srikarparsi, you can review the codes, but please wait until I finish testing, then merge. thanks. |
|
@MelvinBot can you please review this PR |
|
Overall this is a well-structured PR. The 1. In const initialSelectedValues = initialSelectedValue ? [initialSelectedValue] : [];
// ...
const options = useMemo(() => { ... }, [initialSelectedValues, items, selectedItem?.value]);Since const initialSelectedValues = useMemo(() => (initialSelectedValue ? [initialSelectedValue] : []), [initialSelectedValue]);The same pattern exists in the other picker components ( 2.
3. Minor:
Everything else looks good — the debounced search value decoupling via |
|
@marufsharifi Any updates? I don’t see the issue on my end. |
|
@linhvovan29546, I've finished testing. I will check the latest comment. and finalize today. thanks. |
Avoid rebuilding `ValueSelectionList` options on every render by depending on the scalar initial selection value instead of a freshly created array, and trim the unused `shouldSyncSelection` / `isEqual` options from `useInitialSelection`.
|
@srikarparsi, I've addressed all the comments and finished testing on all platforms. Could you please take another look? thanks. |
Explanation of Change
Fixed Issues
$ #69184
PROPOSAL: #69184 (Comment)
Tests
Note: Below is just an example of one place this issue occurs. There are others, like the country list when adding a personal bank account to the Wallet. This PR fixes the bug on all relevant lists in the app at once.
Offline tests
Same as Tests.
QA Steps
Same as Tests.
// TODO: These must be filled out, or the issue title must include "[No QA]."
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Record 1:
Screen.Recording.2026-03-30.at.7.24.01.PM.mov
Record 2:
Screen.Recording.2026-03-30.at.7.30.26.PM.mov
Record 3:
Screen.Recording.2026-03-30.at.7.24.55.PM.mov
Record 4:
Screen.Recording.2026-03-30.at.7.25.23.PM.mov
Record 5:
Screen.Recording.2026-03-30.at.7.26.39.PM.mov
Record 6:
Screen.Recording.2026-03-30.at.7.24.19.PM.mov
Record 8:
Screen.Recording.2026-03-30.at.7.27.10.PM.mov
Record 9:
Screen.Recording.2026-03-30.at.7.23.41.PM.mov
Record 10:
Screen.Recording.2026-04-01.at.3.13.33.PM.mov
Record 11:
Screen.Recording.2026-04-01.at.5.02.14.PM.mov
Record 12:
Screen.Recording.2026-04-01.at.5.03.25.PM.mov
Record 13:
Screen.Recording.2026-04-01.at.5.44.40.PM.mov
Record 14:
Screen.Recording.2026-04-01.at.5.45.11.PM.mov
Record 15:
Screen.Recording.2026-04-05.at.2.08.14.PM.mov
Record 16:
Screen.Recording.2026-04-05.at.2.13.16.PM.mov
Record 17:
Screen.Recording.2026-04-05.at.2.13.44.PM.mov
Record 18:
Screen.Recording.2026-04-05.at.2.14.28.PM.mov
Record 19:
Screen.Recording.2026-04-05.at.2.15.09.PM.mov
Record 20:
Screen.Recording.2026-04-05.at.2.17.19.PM.mov
Record 21:
Screen.Recording.2026-04-05.at.2.18.22.PM.mov
Record 22:
Screen.Recording.2026-04-05.at.2.22.26.PM.mov
Android: mWeb Chrome
Record 1:
Screen.Recording.2026-03-30.at.5.01.00.PM.mov
Record 2:
Screen.Recording.2026-03-30.at.5.03.08.PM.mov
Record 3:
Screen.Recording.2026-03-30.at.5.03.39.PM.mov
Record 4:
Screen.Recording.2026-03-30.at.5.13.30.PM.mov
Record 5:
Screen.Recording.2026-03-30.at.5.50.42.PM.mov
Record 6:
Screen.Recording.2026-03-30.at.4.55.18.PM.mov
Record 7:
Screen.Recording.2026-03-30.at.5.46.11.PM.mov
Record 8:
Screen.Recording.2026-03-30.at.4.54.08.PM.mov
Record 9:
Screen.Recording.2026-04-01.at.3.16.46.PM.mov
Record 10:
Screen.Recording.2026-04-01.at.3.17.57.PM.mov
Record 11:
Screen.Recording.2026-04-01.at.4.55.08.PM.mov
Record 12:
Screen.Recording.2026-04-01.at.4.57.46.PM.mov
Record 13:
Screen.Recording.2026-04-01.at.4.55.46.PM.mov
Record 14:
Screen.Recording.2026-04-01.at.4.58.45.PM.mov
Record 15:
Screen.Recording.2026-04-05.at.2.42.49.PM.mov
Record 16:
Screen.Recording.2026-04-05.at.3.02.24.PM.mov
Record 17:
Screen.Recording.2026-04-05.at.3.04.38.PM.mov
Record 18:
Screen.Recording.2026-04-05.at.3.05.12.PM.mov
Record 19:
Screen.Recording.2026-04-05.at.3.05.34.PM.mov
Record 20:
Screen.Recording.2026-04-05.at.3.06.56.PM.mov
Record 21:
Screen.Recording.2026-04-05.at.3.10.56.PM.mov
Record 22:
Screen.Recording.2026-04-05.at.3.11.45.PM.mov
Record 23:
Screen.Recording.2026-04-05.at.3.12.25.PM.mov
iOS: Native
Record 1:
Screen.Recording.2026-03-30.at.6.18.08.PM.mov
Record 2:
Screen.Recording.2026-03-30.at.6.18.31.PM.mov
Record 3:
Screen.Recording.2026-03-30.at.6.19.18.PM.mov
Record 4:
Screen.Recording.2026-03-30.at.6.20.11.PM.mov
Record 5:
Screen.Recording.2026-03-30.at.6.16.51.PM.mov
Record 6:
Screen.Recording.2026-03-30.at.6.20.53.PM.mov
Record 7:
Screen.Recording.2026-03-30.at.6.16.07.PM.mov
Record 8:
Screen.Recording.2026-04-01.at.11.33.09.AM.mov
Record 9:
Screen.Recording.2026-04-01.at.11.34.11.AM.mov
Record 10:
Screen.Recording.2026-04-01.at.11.35.50.AM.mov
Record 11:
Screen.Recording.2026-04-01.at.3.49.36.PM.mov
Record 12:
Screen.Recording.2026-04-01.at.3.53.02.PM.mov
Record 13:
Screen.Recording.2026-04-01.at.4.15.54.PM.mov
Record 14:
Screen.Recording.2026-04-01.at.4.16.47.PM.mov
Record 15:
Screen.Recording.2026-04-05.at.10.18.53.AM.mov
Record 16:
Screen.Recording.2026-04-05.at.11.00.26.AM.mov
Record 17:
Screen.Recording.2026-04-05.at.11.02.02.AM.mov
Record 18:
Screen.Recording.2026-04-05.at.11.02.53.AM.mov
Record 19:
Screen.Recording.2026-04-05.at.11.03.59.AM.mov
Record 20:
Screen.Recording.2026-04-05.at.11.05.48.AM.mov
Record 21:
Screen.Recording.2026-04-05.at.11.16.18.AM.mov
Record 22:
Screen.Recording.2026-04-05.at.12.22.30.PM.mov
iOS: mWeb Safari
Record 1:
Screen.Recording.2026-03-30.at.6.36.12.PM.mov
Record 2:
Screen.Recording.2026-03-30.at.6.36.51.PM.mov
Record 3:
Screen.Recording.2026-03-30.at.6.37.32.PM.mov
Record 4:
Screen.Recording.2026-03-30.at.6.36.33.PM.mov
Record 5:
Screen.Recording.2026-03-30.at.6.37.56.PM.mov
Record 6:
Screen.Recording.2026-03-30.at.6.38.40.PM.mov
Record 7:
Screen.Recording.2026-03-30.at.7.09.25.PM.mov
Record 8:
Screen.Recording.2026-03-30.at.7.14.01.PM.mov
Record 9:
Screen.Recording.2026-04-01.at.11.45.24.AM.mov
Record 10:
Screen.Recording.2026-04-01.at.11.46.41.AM.mov
Record 11:
Screen.Recording.2026-04-01.at.4.24.31.PM.mov
Record 12:
Screen.Recording.2026-04-01.at.4.25.32.PM.mov
Record 13:
Screen.Recording.2026-04-01.at.4.43.59.PM.mov
Record 14:
Screen.Recording.2026-04-05.at.11.53.14.AM.mov
Record 15:
Screen.Recording.2026-04-01.at.4.44.26.PM.mov
Record 16:
Screen.Recording.2026-04-05.at.11.55.14.AM.mov
Record 17:
Screen.Recording.2026-04-05.at.12.07.44.PM.mov
Record 18:
Screen.Recording.2026-04-05.at.12.08.31.PM.mov
Record 19:
Screen.Recording.2026-04-05.at.12.09.00.PM.mov
Record 20:
Screen.Recording.2026-04-05.at.12.09.16.PM.mov
Record 21:
Screen.Recording.2026-04-05.at.12.11.14.PM.mov
Record 22:
Screen.Recording.2026-04-05.at.12.13.37.PM.mov
Record 23:
Screen.Recording.2026-04-05.at.12.14.43.PM.mov
MacOS: Chrome / Safari
Record 1.
workspace.company.cards.add.card.feed.country.step.mov
Record 2.
settings.wallet.add.-.bank.account.account.detaisl.currencly.mov
Record 3.
Settings.Wallet.add.bank.account.bank.information.bank.region.mov
Record 4.
Settings.Wallet.add.bank.account.account.holder.detaisl.mov
Record 5.
create.expense.per.diem.subrate.mov
Record 6.
start.chat.roo.workspace.mov
Record 7.
start.chat.room.who.can.post.mov
Record 8.
only.available.by.route.copy.the.route.from.expensify.cards.mov
Record 9.
stat.chat.room.visibility.mov
Record 10.
expensify.card.issue.card.limit.type.mov
Record 11.
Expensify.card.card.item.Limit.Type.mov
Record 12.
create.expense.per.diem.add.subrate.then.update.that.subrate.mov
Record 13.
settings.profile.address.country.mov
Record 14.
Settings.profile.address.state.mov
Record 15.
Settings.Wallet.Add.bank.account.country.selection.mov
Record 16.
Screencast.From.2026-04-04.08-47-52.mp4
Record 17.
Screencast.From.2026-04-04.09-03-18.mp4
Record 18.
Screencast.From.2026-04-04.09-05-28.mp4
Record 19.
Screencast.From.2026-04-04.09-06-56.mp4
Record 20.
Screencast.From.2026-04-04.09-09-18.mp4
Record 21.
Screencast.From.2026-04-04.09-15-24.mp4
Record 22.
Screencast.From.2026-04-04.10-10-40.mp4
Record 23.
Screencast.From.2026-04-04.10-45-51.mp4
Record 24:
Screencast.From.2026-04-05.07-53-13.mp4