You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Wire the three presentational snackbar components shipped by #12508 (progress), #12509 (success), and #12510 (error) into the live PDF export flow via #12655's core/pdf datastore. Until this ticket lands those components exist in Storybook only; after this ticket, they appear in the dashboard at the right moments and respond to user input by dispatching the right store actions.
This ticket introduces a single PDFExportSnackbar container that reads select( CORE_PDF ).getStatus() and renders the matching presentational snackbar (or nothing on 'idle'). Each snackbar's callback props are bound to core/pdf actions: progress's Cancel dispatches requestCancel(); success's auto-dismiss and X-button dispatch clearExport(); success's retry-download triggers a re-download from the existing getBlob() URL; error's X-button dispatches clearExport(); error's Retry bubbles up via a parent callback so the parent can re-open the side sheet panel (per the design doc's Dashboard Snackbar: Error section). The container is mounted once at the dashboard root, alongside OfflineNotification in DashboardMainApp.
The container is the only consumer of the snackbar components in production. The snackbar components themselves remain presentational and unchanged: this ticket adds a layer above them, not a modification of them.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
During PDF generation, the progress snackbar appears at the bottom-right of the dashboard, shows live progress, and stays visible until the export completes, fails, or is cancelled.
Clicking the progress snackbar's Cancel button aborts the in-flight export and the snackbar disappears.
When the export completes, the success snackbar replaces the progress snackbar and the PDF downloads automatically.
The success snackbar's retry-download link re-triggers the download for the same generated PDF without re-running the export.
The success snackbar auto-dismisses after 10 seconds or via its close (X), returning the dashboard to its idle state either way.
If generation fails at any stage, the error snackbar replaces the progress snackbar, persists until user action, and offers a Retry link, an inline "get help" link in the body text, and a close (X).
Clicking Retry on the error snackbar re-opens the side sheet panel with the user's previous selection preserved; the export does not restart automatically, the user clicks Download to retry.
Clicking close (X) on the error snackbar dismisses it and returns the dashboard to its idle state.
Feature gated behind the PDF export feature flag.
Implementation Brief
Files to modify
Frontend - container component
Create file assets/js/components/pdf-export/PDFExportSnackbar.tsx - container connecting the three presentational snackbars from Create the Progress snackbar component #12508 / Create the Success snackbar component #12509 / Create the Error snackbar component #12510 to core/pdf. Reads select( CORE_PDF ).getStatus() and renders nothing on 'idle', <PDFReportProgressSnackbar> on 'progress', <PDFReportSuccessSnackbar> on 'success', and <PDFReportErrorSnackbar> on 'error'. Accepts a single onRetry prop (callback to re-open the side sheet panel). No other props.
Wire the progress snackbar inside the container. progress is select( CORE_PDF ).getProgress() / 100 (store holds 0..100; the component expects 0..1 per Create the Progress snackbar component #12508). onCancel dispatches dispatch( CORE_PDF ).requestCancel(). The orchestrator from Core pipeline: Export an MVP PDF #12536 observes the resulting cancelRequested flag, calls abort() itself, and dispatches clearCancelRequest() and setStatus( 'idle' ) on its own; the container does no further cancel work.
Wire the success snackbar inside the container. onDismiss and onAutoDismiss both dispatch dispatch( CORE_PDF ).clearExport(). onRetryDownload reads { url, filename } via select( CORE_PDF ).getBlob(), creates a transient <a href={ url } download={ filename }> element, calls .click(), and removes it. No store dispatch from retry-download: the existing blob is reused, the export is not re-run. This matches the design doc's manual-download semantics where the blob is the source of truth and the orchestrator is not involved.
Wire the error snackbar inside the container. onDismiss dispatches dispatch( CORE_PDF ).clearExport(). onRetry dispatches dispatch( CORE_PDF ).clearExport() and then invokes the container's onRetry prop: clearExport() resets status back to 'idle' (so the error snackbar unmounts and no longer lingers behind the re-opened panel), and selection survives clearExport() per Create the core/pdf datastore #12655 so the panel still reflects the user's previous picks. The parent's onRetry callback re-opens the side sheet panel per the design doc. onHelpClick / helpURL are not wired by the container: Create the Error snackbar component #12510's component already reads the documentation URL via select( CORE_SITE ).getDocumentationLinkURL( 'pdf-reporting' ) itself.
Frontend - mount the container at the dashboard root
Update file assets/js/components/DashboardMainApp.js - mount <PDFExportSnackbar onRetry={ ... } /> alongside the existing <OfflineNotification /> block. Gate the mount on the existing PDF export feature flag check. The onRetry prop is wired to whichever parent state opens the side sheet panel (the same flag toggled by the dashboard header's download icon button per Create the PDF Generation menu item and sidesheet #12507), so Retry from the error snackbar and the header's download icon both go through one panel-open path.
Test Coverage
Jest: assets/js/components/pdf-export/PDFExportSnackbar.test.tsx - renders nothing when status === 'idle'; renders only <PDFReportProgressSnackbar> when status === 'progress' with progress prop equal to getProgress() / 100; renders only <PDFReportSuccessSnackbar> when status === 'success'; renders only <PDFReportErrorSnackbar> when status === 'error'. Cancel on progress dispatches requestCancel. Dismiss and auto-dismiss on success both dispatch clearExport. Retry-download on success reads getBlob(), creates a transient anchor, triggers .click(), and removes it, without dispatching any store action. Dismiss on error dispatches clearExport. Retry on error dispatches clearExport (status returns to 'idle') and invokes the onRetry prop callback exactly once; selection state is not touched.
No Storybook / VRT: the presentational snackbar components are already covered by their own stories in #12508 / #12509 / #12510. This container is a pure wiring layer with no visual surface of its own.
QA Brief
The snackbar has already been connected to the core/pdf state, so this ticket has just added the part that let's to retry the download process. So your task is to verify that when you click on the download your report link in the success state description, you get the pdf downloaded for you.
Changelog entry
Add support for manual download and retry in PDF generation snackbars.
Feature Description
Wire the three presentational snackbar components shipped by #12508 (progress), #12509 (success), and #12510 (error) into the live PDF export flow via #12655's
core/pdfdatastore. Until this ticket lands those components exist in Storybook only; after this ticket, they appear in the dashboard at the right moments and respond to user input by dispatching the right store actions.This ticket introduces a single
PDFExportSnackbarcontainer that readsselect( CORE_PDF ).getStatus()and renders the matching presentational snackbar (or nothing on'idle'). Each snackbar's callback props are bound tocore/pdfactions: progress's Cancel dispatchesrequestCancel(); success's auto-dismiss and X-button dispatchclearExport(); success's retry-download triggers a re-download from the existinggetBlob()URL; error's X-button dispatchesclearExport(); error's Retry bubbles up via a parent callback so the parent can re-open the side sheet panel (per the design doc's Dashboard Snackbar: Error section). The container is mounted once at the dashboard root, alongsideOfflineNotificationinDashboardMainApp.The container is the only consumer of the snackbar components in production. The snackbar components themselves remain presentational and unchanged: this ticket adds a layer above them, not a modification of them.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
Implementation Brief
Files to modify
Frontend - container component
assets/js/components/pdf-export/PDFExportSnackbar.tsx- container connecting the three presentational snackbars from Create the Progress snackbar component #12508 / Create the Success snackbar component #12509 / Create the Error snackbar component #12510 tocore/pdf. Readsselect( CORE_PDF ).getStatus()and renders nothing on'idle',<PDFReportProgressSnackbar>on'progress',<PDFReportSuccessSnackbar>on'success', and<PDFReportErrorSnackbar>on'error'. Accepts a singleonRetryprop (callback to re-open the side sheet panel). No other props.progressisselect( CORE_PDF ).getProgress() / 100(store holds 0..100; the component expects 0..1 per Create the Progress snackbar component #12508).onCanceldispatchesdispatch( CORE_PDF ).requestCancel(). The orchestrator from Core pipeline: Export an MVP PDF #12536 observes the resultingcancelRequestedflag, callsabort()itself, and dispatchesclearCancelRequest()andsetStatus( 'idle' )on its own; the container does no further cancel work.onDismissandonAutoDismissboth dispatchdispatch( CORE_PDF ).clearExport().onRetryDownloadreads{ url, filename }viaselect( CORE_PDF ).getBlob(), creates a transient<a href={ url } download={ filename }>element, calls.click(), and removes it. No store dispatch from retry-download: the existing blob is reused, the export is not re-run. This matches the design doc's manual-download semantics where the blob is the source of truth and the orchestrator is not involved.onDismissdispatchesdispatch( CORE_PDF ).clearExport().onRetrydispatchesdispatch( CORE_PDF ).clearExport()and then invokes the container'sonRetryprop:clearExport()resetsstatusback to'idle'(so the error snackbar unmounts and no longer lingers behind the re-opened panel), andselectionsurvivesclearExport()per Create thecore/pdfdatastore #12655 so the panel still reflects the user's previous picks. The parent'sonRetrycallback re-opens the side sheet panel per the design doc.onHelpClick/helpURLare not wired by the container: Create the Error snackbar component #12510's component already reads the documentation URL viaselect( CORE_SITE ).getDocumentationLinkURL( 'pdf-reporting' )itself.Frontend - mount the container at the dashboard root
assets/js/components/DashboardMainApp.js- mount<PDFExportSnackbar onRetry={ ... } />alongside the existing<OfflineNotification />block. Gate the mount on the existing PDF export feature flag check. TheonRetryprop is wired to whichever parent state opens the side sheet panel (the same flag toggled by the dashboard header's download icon button per Create the PDF Generation menu item and sidesheet #12507), so Retry from the error snackbar and the header's download icon both go through one panel-open path.Test Coverage
assets/js/components/pdf-export/PDFExportSnackbar.test.tsx- renders nothing whenstatus === 'idle'; renders only<PDFReportProgressSnackbar>whenstatus === 'progress'withprogressprop equal togetProgress() / 100; renders only<PDFReportSuccessSnackbar>whenstatus === 'success'; renders only<PDFReportErrorSnackbar>whenstatus === 'error'. Cancel on progress dispatchesrequestCancel. Dismiss and auto-dismiss on success both dispatchclearExport. Retry-download on success readsgetBlob(), creates a transient anchor, triggers.click(), and removes it, without dispatching any store action. Dismiss on error dispatchesclearExport. Retry on error dispatchesclearExport(status returns to'idle') and invokes theonRetryprop callback exactly once; selection state is not touched.No Storybook / VRT: the presentational snackbar components are already covered by their own stories in #12508 / #12509 / #12510. This container is a pure wiring layer with no visual surface of its own.
QA Brief
core/pdfstate, so this ticket has just added the part that let's to retry the download process. So your task is to verify that when you click on thedownload your reportlink in the success state description, you get the pdf downloaded for you.Changelog entry