Add loading UI for search page menu + default to approve section for admins#72034
Add loading UI for search page menu + default to approve section for admins#72034JS00001 merged 19 commits intoExpensify:mainfrom
Conversation
|
@Krishna2323 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] |
|
@ShridharGoel, I see you're actively working on this and the checks are also failing. Could you please move this back to draft and let me know once you've finished? Thanks! |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
@Krishna2323 It's ready for review. |
|
Thanks! Will start reviewing today. |
|
@Krishna2323 Did you get a chance to check this? |
|
@ShridharGoel I didn't get a chance to review this yesterday or today because of some deployment blockers. I'll make it my first priority tomorrow. |
|
Thanks! |
|
Reviewing... |
| isTopLevelBar?: boolean; | ||
| }; | ||
|
|
||
| function getDefaultTodoSuggestedSearch(typeMenuSections: SearchTypeMenuSection[]) { |
There was a problem hiding this comment.
Please move this function into SearchUIUtils.
|
@ShridharGoel the skeleton is misaligned:
|
|
Apart from some minor changes, the PR looks good! I'll complete the review tomorrow. |
|
@Krishna2323 Thanks for the review, it's updated. |
|
Note: Lint issue is on main. |
|
@Krishna2323 will you be able to re-review today, please? |
|
I will review this within the next 12 hours. |
|
Thanks! |
| const defaultSuggestedSearch = suggestedSearchesReady ? getDefaultTodoSuggestedSearch(typeMenuSections) : undefined; | ||
| const savedSearchQuery = Object.values(savedSearches ?? {}).at(0)?.query; | ||
| Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: nonExploreTypeQuery ?? savedSearchQuery ?? buildCannedSearchQuery()})); | ||
| Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: defaultSuggestedSearch?.searchQuery ?? savedSearchQuery ?? buildCannedSearchQuery()})); |
There was a problem hiding this comment.
- If Approve/Submit are unavailable, it now selects the first item in the Todo section.
- If suggestedSearchesReady is false, it falls back to the first saved search (if any), otherwise to the canned query.
Previously, it always used the first item of the first section.
Could you please explain this? I just want to confirm that the current behavior is correct.
Why don’t we use the first item of the first section when suggestedSearchesReady is false and as the default value for getDefaultTodoSuggestedSearch?
I know it’s the same logic as in getDefaultTodoSuggestedSearch, but here, when navigating, we fall back to a saved search — whereas previously, it was simply the first item of the first section, correct?
There was a problem hiding this comment.
If Approve/Submit are unavailable, it now selects the first item in the Todo section.
This is expected, right?
Could you please explain this? I just want to confirm that the current behavior is correct.
Why don’t we use the first item of the first section when suggestedSearchesReady is false and as the default value for getDefaultTodoSuggestedSearch?
I know it’s the same logic as in getDefaultTodoSuggestedSearch, but here, when navigating, we fall back to a saved search — whereas previously, it was simply the first item of the first section, correct?
When suggestions are ready we route to Approve for admins (or Submit/first Todo for others). If suggestions aren’t ready, we prefer the first saved search to avoid surfacing a random canned query, otherwise we fall back to the canned default.
src/hooks/useSearchTypeMenu.tsx
Outdated
| }, [similarSearchHash, isSavedSearchActive, typeMenuSections]); | ||
| const similarSearchIndex = flattenedMenuItems.findIndex((item) => item.similarSearchHash === similarSearchHash); | ||
|
|
||
| if (!shouldFallbackToTodoDefault) { |
There was a problem hiding this comment.
Add || similarSearchIndex === defaultTodoIndex to avoid additional checks.
src/pages/Search/SearchTypeMenu.tsx
Outdated
| return similarSearchIndex; | ||
| } | ||
|
|
||
| return defaultTodoIndex; |
There was a problem hiding this comment.
This will be "-1" when Approve/Submit doesn’t exist, I think we can fall back to first Todo item?
There was a problem hiding this comment.
Updated, it would return the first Todo entry when Approve/Submit are absent.
|
|
||
| expect(result?.key).toBe(CONST.SEARCH.SEARCH_KEYS.PAY); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Add an edge case where the todo section is empty or missing to assert it returns undefined or expected fallback.
There was a problem hiding this comment.
Sorry about that. This test looks completely redundant. Could you please remove it?
|
Left few comments. Will try to complete the checklist today once resolved. |
|
Thanks! @ShridharGoel can you jump on that? |
|
Updated. |
|
@ShridharGoel I'm facing an issue on iOS -- after being added to a workspace the submit button is added to the todo options but for some reason the modal isn't opening correctly: Steps to reproduce:
Monosnap.screencast.2025-10-18.06-55-59.mp4 |
|
Updated, should we get another adhoc build? |
|
The test failures are unrelated. |
|
Yeah let's do it - I can run one now. |
|
🚧 @dannymcclain has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, Desktop, and Web. Happy testing! 🧪🧪
|
|
@dannymcclain @dubielzyk-expensify Did you get a chance to check the updated build? |
|
It's looking pretty good to me, but I'd like @dubielzyk-expensify to have a final look too. |
|
Looks great! Thanks for implementing our feedback, @ShridharGoel ! Great work 👏 |
dubielzyk-expensify
left a comment
There was a problem hiding this comment.
Approve from a design side 👍
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppandroid_chrome.mp4Android: mWeb Chromeandroid_chrome.mp4iOS: HybridAppios_native.mp4iOS: mWeb Safariios_safari.mp4MacOS: Chrome / Safariweb_chrome.mp4MacOS: Desktopdesktop_app.mp4 |
|
@JS00001 could you please re-run the reviewer checklist check? I forgot to delete the old one before approving. |
|
🚀 Deployed to staging by https://github.com/JS00001 in version: 9.2.61-0 🚀
|
|
🚀 Deployed to production by https://github.com/grgia in version: 9.2.61-5 🚀
|
| const defaultMenuItem = | ||
| flattenedMenuItems.find((item) => item.key === CONST.SEARCH.SEARCH_KEYS.APPROVE) ?? flattenedMenuItems.find((item) => item.key === CONST.SEARCH.SEARCH_KEYS.SUBMIT); | ||
|
|
||
| if (!defaultMenuItem || similarSearchHash === defaultMenuItem.similarSearchHash) { |
There was a problem hiding this comment.
The PR introduced a bug where the app overrides user-selected search tabs (e.g., Expenses) by auto-navigating to a default tab (e.g., Approve) after skeleton loading. This happens because the condition doesn’t fully account for intentional user navigation. More details and fix proposal.

Explanation of Change
Add loading UI for search page LHN + default to approve section for admins
Fixed Issues
$ #71550
PROPOSAL: #71550 (comment)
Tests
Submitis the default highlighted entry.Approveentry becomes available.On iOS native:
Offline tests
QA Steps
Same as tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand 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
Screen.Recording.2025-10-11.at.9.26.26.PM.mov
Android: mWeb Chrome
Screen.Recording.2025-10-11.at.9.21.22.PM.mov
iOS: Native
Simulator.Screen.Recording.-.iPhone.16.Plus.-.2025-10-11.at.21.14.08.mp4
iOS: mWeb Safari
Simulator.Screen.Recording.-.iPhone.16.Plus.-.2025-10-11.at.21.18.15.mp4
MacOS: Chrome / Safari
Screen.Recording.2025-10-10.at.7.35.18.PM.mov
Screen.Recording.2025-10-10.at.7.31.40.PM.mov
MacOS: Desktop
Screen.Recording.2025-10-11.at.9.06.08.PM.mov
Screen.Recording.2025-10-11.at.9.02.47.PM.mov
Screen.Recording.2025-10-11.at.9.02.22.PM.mov