Repository navigation
Add page navigation to multi-page PDF receipts - #102776
ishpaul777 wants to merge 35 commits into
Conversation
On desktop the receipt preview already renders the full PDF over the page 1 thumbnail for hover zoom. The page count badge becomes a pill with previous/next buttons that scroll that PDF, once it has loaded.
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx bun ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
PDFPreviewer measures every page before showing any, which took tens of seconds on long PDFs. Page navigation now renders just the requested page with react-pdf, so it no longer needs the react-fast-pdf change.
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
@ishpaul777 Do we need to wait for the backend or anything else? If not, we can remove the draft status and start working on it. If there are any changes, I’ll update them here as well. |
Screen.Recording.2026-10-02.at.14.41.01.movCould you check why the page navigation controls are displayed so late? I can see that the PDF has already been uploaded successfully. |
Screen.Recording.2026-10-02.at.15.06.17.movBUG: When I click Next or Previous, the navigation controls jump in and out because there isn't enough width. |
Screen.Recording.2026-10-02.at.15.10.21.movBUG: The tooltip still appears when the button is disabled. |
Show the pill as soon as pages can be flipped, with its buttons disabled until the PDF loads, so it no longer grows when the arrows appear. Reserve the widest label's width, hide tooltips on disabled buttons, and fall back to the static badge when the PDF or a page fails to load.
This reverts commit cd16a8a.
minWidth on the label only set a floor; any count past it still grew the pill. Per huult's suggestion: the label is now an absolutely positioned overlay, so it never affects the row's layout. The pill's width comes only from the two buttons and a fixed spacer between them, so it never changes size. overflow: hidden masks a label too long to fit.
|
@huult can you please give this another review, Thanks! |
|
@ishpaul777 Could you check the failing checks? |
… redundant comment, fix page param in test
…cus outlines aren't clipped
|
@huult Both fixed. The pill now sizes to its longest label ("Page N of N" for the last page), so long translations no longer clip. Removing overflow: hidden also fixes the clipped focus outlines. Please re-check on your side. |
huult
left a comment
There was a problem hiding this comment.
Screen.Recording.2026-10-12.at.10.19.02.mov
LGTM.
|
We did not find an internal engineer to review this PR, trying to assign a random engineer to #102435 as well as to this PR... Please reach out for help on Slack if no one gets assigned! |
|
@MelvinBot I just approved this. Please update the PR title to include [Payment due @huult]. |
|
Done. I updated the PR title to |


Explanation of Change
Multi-page PDF receipts showed a static "Page 1 of N" badge, and there was no way to flip pages in the expense's receipt preview. On desktop, that preview already draws the real PDF over the page 1 thumbnail so hover zoom stays sharp. This PR turns the badge into a pill with previous/next buttons that change which page that layer shows.
For multi-page PDFs the layer now renders only the current page with react-pdf, instead of
PDFPreviewer, which measures every page before showing any. Every other receipt still usesPDFPrevieweras before. The previous page stays visible until the next one has drawn, so the thumbnail underneath doesn't flash.The pill shows as soon as pages can be flipped, with its buttons disabled until the PDF loads, so it doesn't change size once it can flip. On devices without hover support, or if the PDF fails to load, the static badge stays instead. Hover zoom pauses while the pointer is over the pill, since tracking the cursor there would hide the page you just flipped to.
receipt.pageCountnow takes the current page.Receipts uploaded in the current session are local files, which this layer didn't cover before. With page navigation on, it now does, which adds one extra PDF load for fresh multi-page uploads on desktop.
Known limitation: the PDF is downloaded again each time the receipt is opened, so on large PDFs the arrows take a few seconds to appear. Caching that is a separate out of scope task.
Fixed Issues
$ #102435
Tests
Offline tests
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
Screen.Recording.2026-10-02.at.6.25.47.PM.mov