Repository navigation
Move track-expense rows and the report guard out of Report Details page - #103319
Conversation
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
@nkdengineer 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] |
|
@MelvinBot Review |
🤖 Code reviewI found no blocking issues. The refactor keeps the existing behaviour, and CI passes except for the human checklist jobs. I checked these points:
Suggestions (non-blocking):
|
|
PR doesn’t need product input as a performance refactor PR. Unassigning and unsubscribing myself. |
|
About Melvin's review: I applied 2 (renamed the decision to 'blank') and 3 (added a test for a focused screen with a delete-transaction navigation in flight). I kept the ReportOrNotFoundGuard name because it matches the withReportOrNotFound HOC it replaces. On 1: the access rules already live in one place, getReportOrNotFoundDecision, so a change to them touches one function. What's left is subscription wiring, and the two differ on purpose. The HOC also reads policy and reportMetadata for the screen it wraps, and it tracks shown content in a ref instead of state. A shared hook would also change every screen that uses the HOC, which is outside this Report Details slice. |
|
@MelvinBot review again |
🤖 Code review (follow-up)The PR looks good to me, with no blocking issues. The only failing check besides the approval job is Changes since my last review:
Your reasons for keeping the guard name and not adding a shared hook make sense. The access rules live only in Why the perf failure looks like noise
|
|
@sosek108 Change looks good. Can you commit something and revert to re-run the perf-test before approving. |
|
@sosek108 Look like the perf failure is not a noise |
|
I'm veryfing this. Problem is, it works on my machine so it's hard to diagnose. I'll be back w/ update. btw. it's flaky because it failed on different test |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-10-09.at.14.37.59.movAndroid: mWeb ChromeScreen.Recording.2026-10-09.at.14.33.19.moviOS: HybridAppScreen.Recording.2026-10-09.at.14.34.00.moviOS: mWeb SafariScreen.Recording.2026-10-09.at.14.35.00.movMacOS: Chrome / SafariScreen.Recording.2026-10-09.at.14.31.24.movScreen.Recording.2026-10-09.at.14.31.46.movScreen.Recording.2026-10-09.at.14.32.10.mov |
|
🚧 mjasikowski 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, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/mjasikowski in version: 9.5.7-0 🚀
|
|
🤖 No help site update needed. This PR is an internal refactor of the Report details page that keeps the same rows, labels, and behaviour, so no article sentence becomes incorrect. |
|
🚀 Deployed to production by https://github.com/mountiny in version: 9.5.7-3 🚀
Bundle Size Analysis (Sentry): |

Explanation of Change
This is the last PR of the Report Details page decomposition, PR 8 of 8.
The four track-expense rows and their 11 Onyx subscriptions move out of
ReportDetailsMenuItemsinto a newReportDetailsMenuTrackExpenseItemscomponent. It only mounts for a track expense that has request data and a parent action that is not deleted.ReportDetailsMenuItemsnow keeps only the self DM / archived gate and the track-expense check. TheLeaveItempart of the issue already landed in #102988.The report-or-not-found decision moves out of
withReportOrNotFoundintoReportOrNotFoundGuard, which exportsgetReportOrNotFoundDecisionand a guard component that takes children. The deeplinkOpenReporteffect moves into auseReportDeepLinkOnOpenhook.withReportOrNotFoundcalls both and keeps its own subscriptions, itscontentShownref and its prop forwarding, so the 20 other screens that use it do not change. The issue asks to move the body ofwithReportOrNotFoundinto a guard. I moved the decision instead, because a guard that takes children cannot inject props into 20 screens without cloning elements.The page entry now reads
reportIDfrom the route and rendersReportDetailsContentinsideReportOrNotFoundGuard. The entry has no Onyx subscription.ReportDetailsContentis the former page shell and readsreportand the report loading state itself. The guard's subscriptions sit one level above the content, so the content does not re-render when they change.The guard records that content was shown with state set during render, not with a ref written during render. This lints clean and compiles with React Compiler.
One deviation:
ReportDetailsContentuses the composedHeaderWithBackButtonAndTitleinstead of the restrictedHeaderWithBackButton. The title and back handler are the same, and this follows batch 1 of the Header migration. It keeps the lint seatbelt baseline untouched.The two page tests and the perf test only lose the 5 HOC props that no longer exist, plus one report object that was only used for those props. A new
withReportOrNotFoundtest covers the HOC and the guard: loading, not found, content, a blank screen while navigating away unfocused, not found while focused, the deeplinkOpenReportcall, and the HOC passing Onyx values over caller props. There was no test for the wrapper before. One unit page test never had a seeded report and always rendered the loading indicator. That was already the case and it still is.This is a refactor and does not change behaviour.
Fixed Issues
$ #102183
PROPOSAL:
Tests
prodOffline tests
N/A
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, 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.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
Android: mWeb Chrome
iOS: Native
Nagranie.z.ekranu.2026-10-7.o.13.04.16.mov
iOS: mWeb Safari
MacOS: Chrome / Safari
Nagranie.z.ekranu.2026-10-7.o.13.01.37.mov
Nagranie.z.ekranu.2026-10-8.o.11.06.23.mov