Repository navigation
Conversation
Move PDFDownloadModal into the modal layer as a presentation-only FileDownloadModal, replace the two domain-specific global wrappers with a single FileDownloadModalWrapper, and move the report and card statement Onyx/download logic into their hooks, which update the open modal in place and never reopen a dismissed one. Co-authored-by: daledah <daledah@users.noreply.github.com>
|
I created this PR from an upstream branch because I don't have push access to your fork. To take ownership of this branch and push updates, run: Then you can close this PR and open a new one from your fork. |
Co-authored-by: daledah <daledah@users.noreply.github.com>
|
Fixed the failing Knip flagged
|
|
The remaining failing check, Analysis: The suite Evidence: This PR doesn't change The knip check now passes after the fix in 152d7d6. |
|
🤖 Web test steps verification results: The "Download as PDF" modal opened correctly in its generating state. When the PDF was ready, the app started the automatic download by sending the browser tab to
The PR's Tests section lists only the console-error check, so I also ran the report PDF download flow this PR changes. To check the ready and dismiss states, run this in a local dev environment where the secure download URL works. |
This comment has been minimized.
This comment has been minimized.
…Modal hook - Add useFileDownloadModal, which owns the fixed-id show, in-place updates, and no-reopen-after-dismiss lifecycle. The report and Expensify Card statement hooks now only derive the title, status, message, and download action. - Drop onCancel and the wrapper's onClose passthrough. The show promise resolves with CLOSE when the modal is dismissed while generating and CONFIRM otherwise, and SubmitPrimaryAction chains the retract off openPDFDownload(). - Pass the header text into FileDownloadModal instead of hardcoding it. Co-authored-by: daledah <daledah@users.noreply.github.com>
|
I aligned the PR with the pattern from Move hold/reject educational modals to the global modal stack in ee24686. I also updated the PR description.
Two behavior notes:
Checks
view run · no recording available |
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.
|
|
@MelvinBot please resolve conflict |
…wnloadModal Co-authored-by: daledah <daledah@users.noreply.github.com> # Conflicts: # src/components/MoneyReportHeaderModals.tsx # src/components/MoneyReportHeaderModalsContext.tsx # tests/unit/Search/SearchBulkActionsButtonTest.tsx
|
Resolved the conflicts by merging
Checks:
|
Co-authored-by: daledah <daledah@users.noreply.github.com>
|
Fixed the failing
|
|
MelvinBot has made 5 consecutive commits trying to fix CI failures. Automatic CI fix dispatches are now paused for this PR. Next steps: A human should review the recent commits and resolve the conflicting CI errors manually. |
This comment has been minimized.
This comment has been minimized.
…and guard the report PDF Onyx keys Co-authored-by: daledah <daledah@users.noreply.github.com>
|
I pushed 69947a0 and added the behavior note to the description. Items 1 and 2 are done. I skipped item 3 because it fails lint. Item 1 also needed two extra changes.
Tests
|
|
@MelvinBot please open this PR |
|
I marked this PR as ready for review. |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 69947a0ba7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
This comment has been minimized.
This comment has been minimized.
Co-authored-by: daledah <daledah@users.noreply.github.com>
|
@roryabraham 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] |
|
I checked both comments and replied in each thread.
|
dylanexpensify
left a comment
There was a problem hiding this comment.
refactor, input not required
…modal stack renders Co-authored-by: daledah <daledah@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…lveModal out of FileDownloadModal Co-authored-by: daledah <daledah@users.noreply.github.com>
|
I pushed 4895720 with all three follow-ups:
I removed the These checks passed: typecheck, lint and the React Compiler check on the changed files, and 6 affected Jest suites (60 tests). |
|
Making this a draft, @daledah please reopen once you give it your C+ approval. Also please resolve review threads or other resolved back-and-forths with Melvin |
This comment has been minimized.
This comment has been minimized.
|
I updated the PR description with your steps:
Nothing else in the description changed. |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppandroid.movAndroid: mWeb Chromeandroid-mweb.moviOS: HybridAppios.moviOS: mWeb Safariios-mweb.movMacOS: Chrome / Safariweb.mov |
|
@MelvinBot i've completed my checklist, please complete the author checklist |
|
I completed the PR Author Checklist. All 36 boxes are now checked.
The PR is still a draft. Mark it ready for review when you want it reviewed. |
|
@MelvinBot please open this PR |
|
I marked this PR as ready for review. |




Explanation of Change
src/components/Modalcontained report-specific and Expensify Card statement-specific wrappers. Each one repeated the same visibility and hide-animation handling. This PR keeps the modal layer free of business logic: one generic wrapper handles the modal lifecycle, and two domain components outsidesrc/components/Modalown the data. It follows the promise-based modal pattern from Move hold/reject educational modals to the global modal stack. The user-visible behavior should not change, except for the timing noted below.Modal/FileDownloadModal.tsx(moved fromPDFDownloadModal.tsx) takes presentation props only:title,status(CONST.FILE_DOWNLOAD_STATUS: generating / ready / failed),message,onDownload,onClose,onModalHide, and the button flags. It has no Onyx, report, or card-statement imports.Modal/Global/FileDownloadModalWrapper.tsxis the single generic wrapper. It ownsisVisibleand callscloseModalonce fromonModalHide, so the show promise resolves only after the modal finishes hiding. It resolves withCLOSEif the user dismissed the modal while the file was still generating, andCONFIRMotherwise. A dismissed modal keeps its last props while it hides. It replacesReportPDFDownloadModalWrapperandExpensifyCardStatementPDFDownloadModalWrapper.ReportPDFDownloadModal.tsx(outsideModal/) is the component the global stack renders for a report. It owns the Onyx subscriptions anduseStallLogger, derives the title,status,message, andonDownload, and rendersFileDownloadModalWrapper. Because the global stack renders it, its subscriptions live as long as the modal, as they did onmain. The report header or Search page can unmount while the PDF is generating, and the modal still picks up the filename.ExpensifyCardStatementPDFDownloadModal.tsxdoes the same withstatementParams, theEXPENSIFY_CARD_STATEMENTsubscription, anddownloadExpensifyCardStatementPDF.useReportPDFDownloadModalanduseExpensifyCardStatementPDFDownloadModalonly callshowModalwith those components. The card statement hook keeps its fixed id, so the second show call with thestatementKeyupdates the open modal in place, and exposes close by id. TherequestIDguard and the clear-selection-after-promise logic inuseSearchBulkActionsare unchanged.MoneyReportHeaderModals.openPDFDownloadnow returns the show promise.SubmitPrimaryActionchains the Submit via PDF retract off it when the result isCLOSE, so there is noonCancelcallback.Behavior note: the Submit via PDF retract and
stopAnimation()now run after the modal finishes hiding, not at the moment the user dismisses it.Tests: added
FileDownloadModalWrapperTest,useReportPDFDownloadModalTest, anduseExpensifyCardStatementPDFDownloadModalTest. They cover the hide lifecycle,CLOSEvsCONFIRMresults, frozen props while hiding, in-place updates without remounting, no reopen after dismissal, and updates that still reach the modal after the caller unmounts. Ported and removedReportPDFDownloadModalTestandExpensifyCardStatementPDFDownloadModalWrapperTest. Updated the mocks inuseSearchBulkActionsDownloadPDFTestandSearchBulkActionsButtonTest.Out of scope:
Modal/Global/HoldMenuModalWrapper.tsx(existing, not touched) still reads report data from Onyx.AI Tests (run locally by MelvinBot):
npm run typecheck: passednpm run linton the changed files: passednpm run fmt(oxfmt): no changes outside this PR's filesnpm run spell-changedon the changed files: no issuesreact-compiler-compliance-check check <changed files>: passedtests/unit/hooks,FileDownloadModalWrapperTest,ModalContextCloseByIDTest,OnyxDrivenGlobalModalsTest,SearchBulkActionsButtonTest, theMoneyReportHeader*UI tests, andMoneyRequestReportViewTest. A mutation check confirmed that the new caller-unmount tests fail on the previous hook-owned subscriptions.ModalContextCloseByIDTest,FileDownloadModalWrapperTest,useReportPDFDownloadModalTest,useExpensifyCardStatementPDFDownloadModalTest,SearchBulkActionsButtonTest, anduseSearchBulkActionsDownloadPDFTestpassed (6 suites, 60 tests).Fixed Issues
$ #104440
PROPOSAL: #104440 (comment)
Tests
Offline tests
QA Steps
Same as tests.
PR Author Checklist
Note
The C+ reviewer,
daledah, did the manual, offline, and cross-platform testing and recorded the videos below (reviewer checklist). MelvinBot added the unit tests and ran the AI tests listed above. Items that don't apply to this PR are checked as N/A.### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)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
Recorded by
daledah:android.mov
Android: mWeb Chrome
Recorded by
daledah:android-mweb.mov
iOS: Native
Recorded by
daledah:ios.mov
iOS: mWeb Safari
Recorded by
daledah:ios-mweb.mov
MacOS: Chrome / Safari
Recorded by
daledah:web.mov