From de6cc495c8c1867e45cdeff5c8766bcbefbb3caf Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 5 Oct 2026 14:52:59 +0800 Subject: [PATCH 1/2] Premium Analytics: Show report load errors in a notice Replace the report error state with the notice the detail pages use, so the message spans the report instead of sitting at the card's left edge. Report records now pass their error through, so access denied drops Retry there too. Access denied is an error notice on every page, matching the widgets. --- .../change-uni-846-report-error-notice | 4 ++ .../describe-detail-page-error.test.ts | 51 ------------------- .../detail-page/describe-detail-page-error.ts | 26 ---------- .../src/components/detail-page/index.ts | 2 - .../widgets-toolkit/src/components/index.ts | 6 +-- .../src/components/page-notice/index.ts | 1 + .../page-notice.tsx} | 15 +++--- .../__tests__/report-error-state.test.tsx | 46 ----------------- .../src/components/report-page/index.ts | 1 - .../report-page/report-error-state.tsx | 47 ----------------- .../packages/widgets-toolkit/src/index.ts | 7 +-- .../routes/author-detail/stage.test.tsx | 2 +- .../routes/author-detail/stage.tsx | 12 ++--- .../routes/post-detail/stage.test.tsx | 4 +- .../routes/post-detail/stage.tsx | 8 +-- .../config/use-report-records.ts | 1 + .../routes/reports/annual-insights/page.tsx | 14 +++-- .../authors/config/use-report-records.ts | 1 + .../routes/reports/authors/page.tsx | 14 +++-- .../clicks/config/use-report-records.ts | 1 + .../routes/reports/clicks/page.test.tsx | 2 +- .../routes/reports/clicks/page.tsx | 14 +++-- .../config/use-report-records.ts | 1 + .../routes/reports/comment-followers/page.tsx | 14 +++-- .../comments/config/use-report-records.ts | 1 + .../routes/reports/comments/page.tsx | 14 +++-- .../downloads/config/use-report-records.ts | 1 + .../routes/reports/downloads/page.test.tsx | 5 +- .../routes/reports/downloads/page.tsx | 14 +++-- .../earnings/config/use-report-records.ts | 1 + .../routes/reports/earnings/page.tsx | 14 +++-- .../emails/config/use-report-records.ts | 1 + .../routes/reports/emails/page.tsx | 14 +++-- .../locations/config/use-report-records.ts | 1 + .../routes/reports/locations/page.test.tsx | 32 +++++++----- .../routes/reports/locations/page.tsx | 14 +++-- .../posts/config/use-report-records.ts | 1 + .../routes/reports/posts/page.test.tsx | 24 ++++----- .../routes/reports/posts/page.tsx | 14 +++-- .../referrers/config/use-report-records.ts | 1 + .../routes/reports/referrers/page.tsx | 14 +++-- .../reports/report-csv-exports.test.tsx | 3 +- .../search-terms/config/use-report-records.ts | 1 + .../routes/reports/search-terms/page.test.tsx | 2 +- .../routes/reports/search-terms/page.tsx | 14 +++-- .../reports/tags/config/use-report-records.ts | 1 + .../routes/reports/tags/page.tsx | 14 +++-- .../reports/utm/config/use-report-records.ts | 1 + .../routes/reports/utm/page.test.tsx | 2 +- .../routes/reports/utm/page.tsx | 14 +++-- .../videos/config/use-report-records.ts | 1 + .../routes/reports/videos/page.test.tsx | 23 ++++----- .../routes/reports/videos/page.tsx | 14 +++-- .../routes/video-detail/stage.test.tsx | 2 +- .../routes/video-detail/stage.tsx | 12 ++--- .../widgets-toolkit-no-mocks-part2.test.tsx | 1 - .../change-uni-846-report-error-notice | 4 ++ .../change-uni-846-report-error-notice | 4 ++ .../change-uni-846-report-error-notice | 4 ++ 59 files changed, 265 insertions(+), 312 deletions(-) create mode 100644 projects/packages/premium-analytics/changelog/change-uni-846-report-error-notice delete mode 100644 projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/__tests__/describe-detail-page-error.test.ts delete mode 100644 projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/describe-detail-page-error.ts create mode 100644 projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/index.ts rename projects/packages/premium-analytics/packages/widgets-toolkit/src/components/{detail-page/detail-page-notice.tsx => page-notice/page-notice.tsx} (60%) delete mode 100644 projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/__tests__/report-error-state.test.tsx delete mode 100644 projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/report-error-state.tsx create mode 100644 projects/plugins/jetpack/changelog/change-uni-846-report-error-notice create mode 100644 projects/plugins/premium-analytics/changelog/change-uni-846-report-error-notice create mode 100644 projects/plugins/wpcomsh/changelog/change-uni-846-report-error-notice diff --git a/projects/packages/premium-analytics/changelog/change-uni-846-report-error-notice b/projects/packages/premium-analytics/changelog/change-uni-846-report-error-notice new file mode 100644 index 000000000000..4465356c316f --- /dev/null +++ b/projects/packages/premium-analytics/changelog/change-uni-846-report-error-notice @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Show load errors on reports in a notice that spans the report, stop offering Retry there when access is denied, and show access denied on author, post, and video details as an error. diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/__tests__/describe-detail-page-error.test.ts b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/__tests__/describe-detail-page-error.test.ts deleted file mode 100644 index eb41b90890c1..000000000000 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/__tests__/describe-detail-page-error.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * External dependencies - */ -import { StatsResponseShapeError } from '@jetpack-premium-analytics/data'; -/** - * Internal dependencies - */ -import { describeDetailPageError } from '../describe-detail-page-error'; - -const RETRY_DESCRIPTION = "We couldn't load this video. Please try again in a moment."; - -describe( 'describeDetailPageError', () => { - it( 'marks an unusable response as an error', () => { - expect( - describeDetailPageError( new StatsResponseShapeError( 'bad shape' ), { - retryDescription: RETRY_DESCRIPTION, - onRetry: jest.fn(), - } ) - ).toEqual( { - intent: 'error', - description: 'This data is unavailable right now.', - } ); - } ); - - it( 'marks a 403 as info, without actions', () => { - expect( - describeDetailPageError( - { error: 'unauthorized', status: 403 }, - { retryDescription: RETRY_DESCRIPTION, onRetry: jest.fn() } - ) - ).toEqual( { - intent: 'info', - description: "You don't have access to this data.", - } ); - } ); - - it.each( [ - [ 'a no_connection 403', { error: 'no_connection', status: 403 } ], - [ 'a generic failure', { status: 500 } ], - ] )( 'marks %s as a retryable error', ( _, error ) => { - const onRetry = jest.fn(); - - expect( - describeDetailPageError( error, { retryDescription: RETRY_DESCRIPTION, onRetry } ) - ).toEqual( { - intent: 'error', - description: RETRY_DESCRIPTION, - actions: [ { label: 'Retry', onClick: onRetry } ], - } ); - } ); -} ); diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/describe-detail-page-error.ts b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/describe-detail-page-error.ts deleted file mode 100644 index 277f7499d696..000000000000 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/describe-detail-page-error.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * External dependencies - */ -import { isAccessDenied } from '@jetpack-premium-analytics/data'; -/** - * Internal dependencies - */ -import { describeError, type DescribeErrorOptions } from '../../helpers/describe-error'; -import type { DetailPageNoticeProps } from './detail-page-notice'; - -/** - * `describeError()` plus the intent a `DetailPageNotice` needs. - * - * @param error - The failed query error. - * @param options - Error-state copy and retry options, as `describeError()` takes them. - * @return The detail page notice props. - */ -export function describeDetailPageError( - error: unknown, - options: DescribeErrorOptions -): DetailPageNoticeProps { - return { - ...describeError( error, options ), - intent: isAccessDenied( error ) ? 'info' : 'error', - }; -} diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/index.ts b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/index.ts index 895ef331145b..d9b25b3c8905 100644 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/index.ts +++ b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/index.ts @@ -7,8 +7,6 @@ export { type DetailPageSectionProps, } from './detail-page-layout'; export { DetailPageEmptyState, type DetailPageEmptyStateProps } from './detail-page-empty-state'; -export { DetailPageNotice, type DetailPageNoticeProps } from './detail-page-notice'; -export { describeDetailPageError } from './describe-detail-page-error'; export { DetailPageShell, type DetailPageShellProps } from './detail-page-shell'; export { DetailPageActions, diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/index.ts b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/index.ts index 87251aff3c39..c3f4cad56e81 100644 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/index.ts +++ b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/index.ts @@ -138,7 +138,6 @@ export { export { ReportChartSection, ReportDrilldownTable, - ReportErrorState, ReportLocationsMap, ReportPageLayout, ReportPageSection, @@ -156,7 +155,6 @@ export { type ReportChartMetric, type ReportChartSectionProps, type ReportDrilldownTableProps, - type ReportErrorStateProps, type ReportLocationsMapProps, type ReportPageLayoutProps, type ReportPageSectionProps, @@ -174,20 +172,18 @@ export { DETAIL_HEADER_GLYPH_SIZE, DetailPageEmptyState, DetailPageLayout, - DetailPageNotice, DetailPageSection, DetailPageShell, - describeDetailPageError, useDetailPageCustomize, type DetailPageActionsProps, type DetailPageBreadcrumbsProps, type DetailPageCustomize, type DetailPageHeaderSlots, type DetailPageLayoutProps, - type DetailPageNoticeProps, type DetailPageSectionProps, type DetailPageShellProps, } from './detail-page'; +export { PageNotice, type PageNoticeProps } from './page-notice'; export { FeedbackModal, PageOptionsMenu, diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/index.ts b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/index.ts new file mode 100644 index 000000000000..9319e99352e4 --- /dev/null +++ b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/index.ts @@ -0,0 +1 @@ +export { PageNotice, type PageNoticeProps } from './page-notice'; diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/detail-page-notice.tsx b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/page-notice.tsx similarity index 60% rename from projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/detail-page-notice.tsx rename to projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/page-notice.tsx index edee1fed2e9c..8fb722dbb7c1 100644 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/detail-page-notice.tsx +++ b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/page-notice.tsx @@ -8,20 +8,21 @@ import { Notice } from '@jetpack-premium-analytics/externals'; import type { WidgetStateError } from '../widget-state'; import type { ReactElement } from 'react'; -export interface DetailPageNoticeProps extends Pick< WidgetStateError, 'description' | 'actions' > { - /** `error` when something failed; `info` when the request answered and the answer is a fact, such as no access. */ - intent: 'error' | 'info'; +export interface PageNoticeProps extends Pick< WidgetStateError, 'description' | 'actions' > { + /** `info` for a fact rather than a fault, such as a missing item; access denied stays an `error`, as on the widgets. */ + intent?: 'error' | 'info'; /** A way out of the page, such as back to its report; `render` is a childless router link, as its children would replace `label`. */ link?: { label: string; render: ReactElement }; } /** - * Stand in for a detail page's widgets when the page's subject failed to load or does not exist. + * Stand in for a report's sections or a detail page's widgets when their data failed to load or does not exist. + * Replace the sections rather than sit beside them: report tables pick their empty state from row count, not fetch status. * - * @param {DetailPageNoticeProps} props - The component props. - * @return The detail page notice. + * @param {PageNoticeProps} props - The component props. + * @return The page notice. */ -export function DetailPageNotice( { intent, description, actions, link }: DetailPageNoticeProps ) { +export function PageNotice( { intent = 'error', description, actions, link }: PageNoticeProps ) { return ( // The default announcement (children) would trail the action labels. diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/__tests__/report-error-state.test.tsx b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/__tests__/report-error-state.test.tsx deleted file mode 100644 index 0e8f2751347a..000000000000 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/__tests__/report-error-state.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * External dependencies - */ -import { fireEvent, render, screen } from '@testing-library/react'; -/** - * Internal dependencies - */ -import { ReportErrorState } from '../report-error-state'; - -describe( 'ReportErrorState', () => { - it( 'renders the given title', () => { - render( ); - - expect( screen.getByText( 'Unable to load posts' ) ).toBeInTheDocument(); - } ); - - it( 'renders a custom description', () => { - render( - - ); - - expect( screen.getByText( 'The posts report is unavailable.' ) ).toBeInTheDocument(); - } ); - - it( 'renders the default description', () => { - render( ); - - expect( - screen.getByText( "We couldn't load this data. Please try again in a moment." ) - ).toBeInTheDocument(); - } ); - - it( 'calls onRetry when Retry is clicked', () => { - const onRetry = jest.fn(); - render( ); - - // eslint-disable-next-line testing-library/prefer-user-event -- @testing-library/user-event is not a direct dependency of this package. - fireEvent.click( screen.getByRole( 'button', { name: 'Retry' } ) ); - - expect( onRetry ).toHaveBeenCalledTimes( 1 ); - } ); -} ); diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/index.ts b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/index.ts index d9aaada3a1f1..eb5968b38825 100644 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/index.ts +++ b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/index.ts @@ -5,7 +5,6 @@ export { type ReportPageSectionProps, } from './report-page-layout'; export { ReportChartSection, type ReportChartSectionProps } from './report-chart-section'; -export { ReportErrorState, type ReportErrorStateProps } from './report-error-state'; export { ReportLocationsMap, type ReportLocationsMapProps } from './report-locations-map'; export { ExporterCsvAction } from './exporter-csv-action'; export { ReportCsvAction, type ReportCsvActionProps } from './report-csv-action'; diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/report-error-state.tsx b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/report-error-state.tsx deleted file mode 100644 index f16268ea1407..000000000000 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/report-error-state.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/** - * External dependencies - */ -import { Button, EmptyState } from '@jetpack-premium-analytics/externals'; -import { __ } from '@wordpress/i18n'; -/** - * Internal dependencies - */ -import { ReportPageSection } from './report-page-layout'; - -export interface ReportErrorStateProps { - title: string; - description?: string; - onRetry: () => void; -} - -/** - * Replace report sections with a shared error and retry state. - * - * The error state replaces the report sections rather than sitting beside - * them. The report tables pick their empty state from row count, not fetch - * status, so a failed first load would read as an empty report, and a failed - * refetch over cached rows would leave stale data with no way to retry. - * - * @param {ReportErrorStateProps} props - The component props. - * @return The report error state. - */ -export function ReportErrorState( { - title, - description = __( - "We couldn't load this data. Please try again in a moment.", - 'jetpack-premium-analytics-pkg' - ), - onRetry, -}: ReportErrorStateProps ) { - return ( - - - { title } - { description } - - - - - - ); -} diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/index.ts b/projects/packages/premium-analytics/packages/widgets-toolkit/src/index.ts index 7308db133d04..e10c53bfdf9a 100644 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/index.ts +++ b/projects/packages/premium-analytics/packages/widgets-toolkit/src/index.ts @@ -122,23 +122,22 @@ export { DETAIL_HEADER_GLYPH_SIZE, DetailPageEmptyState, DetailPageLayout, - DetailPageNotice, DetailPageSection, DetailPageActions, DetailPageBreadcrumbs, DetailPageShell, - describeDetailPageError, useDetailPageCustomize, type DetailPageActionsProps, type DetailPageBreadcrumbsProps, type DetailPageCustomize, type DetailPageHeaderSlots, type DetailPageLayoutProps, - type DetailPageNoticeProps, type DetailPageSectionProps, type DetailPageShellProps, FeedbackModal, type FeedbackSource, + PageNotice, + type PageNoticeProps, PageOptionsMenu, type PageOptionsMenuProps, ResetLayoutAction, @@ -150,7 +149,6 @@ export { type LocationsGeoRow, ReportChartSection, ReportDrilldownTable, - ReportErrorState, ReportLocationsMap, ReportPageLayout, ReportPageSection, @@ -166,7 +164,6 @@ export { type ReportChartMetric, type ReportChartSectionProps, type ReportDrilldownTableProps, - type ReportErrorStateProps, type ReportLocationsMapProps, type ReportPageLayoutProps, type ReportPageSectionProps, diff --git a/projects/packages/premium-analytics/routes/author-detail/stage.test.tsx b/projects/packages/premium-analytics/routes/author-detail/stage.test.tsx index 45f205da0b1e..ee26f5787287 100644 --- a/projects/packages/premium-analytics/routes/author-detail/stage.test.tsx +++ b/projects/packages/premium-analytics/routes/author-detail/stage.test.tsx @@ -332,7 +332,7 @@ describe( 'author detail stage', () => { expect( getNoticeText( "You don't have access to this data." ) ).toBeInTheDocument(); expect( - getNoticeAnnouncement( "You don't have access to this data.", 'polite' ) + getNoticeAnnouncement( "You don't have access to this data.", 'assertive' ) ).toBeInTheDocument(); expect( screen.queryByRole( 'button', { name: 'Retry' } ) ).not.toBeInTheDocument(); } ); diff --git a/projects/packages/premium-analytics/routes/author-detail/stage.tsx b/projects/packages/premium-analytics/routes/author-detail/stage.tsx index dad0424e095b..4703041ee8dc 100644 --- a/projects/packages/premium-analytics/routes/author-detail/stage.tsx +++ b/projects/packages/premium-analytics/routes/author-detail/stage.tsx @@ -17,14 +17,14 @@ import { DetailPageActions, DetailPageBreadcrumbs, DetailPageLayout, - DetailPageNotice, + PageNotice, DetailPageSection, DetailPageShell, - describeDetailPageError, + describeError, useDetailPageCustomize, useStoredDetailLayout, useTrackedDateRangeApply, - type DetailPageNoticeProps, + type PageNoticeProps, } from '@jetpack-premium-analytics/widgets-toolkit'; import { useCallback, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -143,7 +143,7 @@ function AuthorDetail(): JSX.Element { [ search ] ); - let notice: DetailPageNoticeProps | null = null; + let notice: PageNoticeProps | null = null; if ( summary.isError ) { // Same split as the widgets, plus a 404: sites that hide the users @@ -157,7 +157,7 @@ function AuthorDetail(): JSX.Element { 'jetpack-premium-analytics-pkg' ), } - : describeDetailPageError( summary.error, { + : describeError( summary.error, { retryDescription: __( "We couldn't load this author. Please try again in a moment.", 'jetpack-premium-analytics-pkg' @@ -230,7 +230,7 @@ function AuthorDetail(): JSX.Element { ) : null } { notice ? ( - + ) : null } diff --git a/projects/packages/premium-analytics/routes/post-detail/stage.test.tsx b/projects/packages/premium-analytics/routes/post-detail/stage.test.tsx index 69d8f818a1ed..ba94d9eef071 100644 --- a/projects/packages/premium-analytics/routes/post-detail/stage.test.tsx +++ b/projects/packages/premium-analytics/routes/post-detail/stage.test.tsx @@ -740,7 +740,7 @@ describe( 'post detail stage on the provisional all-time window', () => { expect( refetch ).toHaveBeenCalledTimes( 1 ); } ); - it( 'offers no Retry when the publish day is denied, as an info notice', () => { + it( 'offers no Retry when the publish day is denied', () => { mockSummary( { isError: true, error: { code: 'rest_forbidden', status: 403 }, @@ -751,7 +751,7 @@ describe( 'post detail stage on the provisional all-time window', () => { expect( getNoticeText( "You don't have access to this data." ) ).toBeInTheDocument(); expect( - getNoticeAnnouncement( "You don't have access to this data.", 'polite' ) + getNoticeAnnouncement( "You don't have access to this data.", 'assertive' ) ).toBeInTheDocument(); expect( screen.queryByRole( 'button', { name: 'Retry' } ) ).not.toBeInTheDocument(); } ); diff --git a/projects/packages/premium-analytics/routes/post-detail/stage.tsx b/projects/packages/premium-analytics/routes/post-detail/stage.tsx index 0dc6b4072376..232163bd8d72 100644 --- a/projects/packages/premium-analytics/routes/post-detail/stage.tsx +++ b/projects/packages/premium-analytics/routes/post-detail/stage.tsx @@ -21,10 +21,10 @@ import { DetailPageBreadcrumbs, DetailPageEmptyState, DetailPageLayout, - DetailPageNotice, + PageNotice, DetailPageSection, DetailPageShell, - describeDetailPageError, + describeError, useDetailPageCustomize, useStoredDetailLayout, useTrackedDateRangeApply, @@ -182,8 +182,8 @@ function PostDetail(): JSX.Element { const anchorErrorNotice = ! isEmailTab && isAnchoringAllTime && summary.isError ? ( - { records.isError ? ( - ) : ( diff --git a/projects/packages/premium-analytics/routes/reports/authors/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/authors/config/use-report-records.ts index 65ca39b4419e..3074e28c098d 100644 --- a/projects/packages/premium-analytics/routes/reports/authors/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/authors/config/use-report-records.ts @@ -31,6 +31,7 @@ export function useAuthorsReportRecords( reportParams: ReportParams ) { return { isError: authors.isError, + error: authors.error, refetch: authors.refetch, rows, hasComparison: authors.hasComparison, diff --git a/projects/packages/premium-analytics/routes/reports/authors/page.tsx b/projects/packages/premium-analytics/routes/reports/authors/page.tsx index 161fe171555a..685930c8f3b1 100644 --- a/projects/packages/premium-analytics/routes/reports/authors/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/authors/page.tsx @@ -6,7 +6,8 @@ import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { ReportDrilldownTable, ExporterCsvAction, - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, authorsCsvExporter, @@ -78,9 +79,14 @@ function AuthorsReport(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/reports/clicks/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/clicks/config/use-report-records.ts index d036cc967a0d..09cbf0b5a7cf 100644 --- a/projects/packages/premium-analytics/routes/reports/clicks/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/clicks/config/use-report-records.ts @@ -28,6 +28,7 @@ export function useClicksReportRecords( reportParams: ReportParams ) { return { isError: report.isError, + error: report.error, refetch: report.refetch, rows, hasComparison: report.hasComparison, diff --git a/projects/packages/premium-analytics/routes/reports/clicks/page.test.tsx b/projects/packages/premium-analytics/routes/reports/clicks/page.test.tsx index 5315518a8252..feccaf1c452f 100644 --- a/projects/packages/premium-analytics/routes/reports/clicks/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/clicks/page.test.tsx @@ -31,7 +31,7 @@ jest.mock( '@jetpack-premium-analytics/ui', () => ( { jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { ExporterCsvAction: () => null, ReportDrilldownTable: jest.fn( () => null ), - ReportErrorState: () => null, + PageNotice: () => null, ReportPageLayout: ( { children }: { children: ReactNode } ) => <>{ children }, ReportPageShell: ( { children }: { children: ReactNode } ) => <>{ children }, useReportRetry: ( refetch: () => unknown ) => refetch, diff --git a/projects/packages/premium-analytics/routes/reports/clicks/page.tsx b/projects/packages/premium-analytics/routes/reports/clicks/page.tsx index ceca82d1805f..6f380af097d5 100644 --- a/projects/packages/premium-analytics/routes/reports/clicks/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/clicks/page.tsx @@ -5,7 +5,8 @@ import { useReportDateFilters } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { ReportDrilldownTable, - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ExporterCsvAction, @@ -83,9 +84,14 @@ function ClicksReport(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/reports/comment-followers/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/comment-followers/config/use-report-records.ts index 21e05b107684..557674393f73 100644 --- a/projects/packages/premium-analytics/routes/reports/comment-followers/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/comment-followers/config/use-report-records.ts @@ -37,6 +37,7 @@ export function useCommentFollowersReportRecords() { isLoading: report.isLoading, isFetching: report.isFetching, isError: report.isError, + error: report.error, refetch: report.refetch, }; } diff --git a/projects/packages/premium-analytics/routes/reports/comment-followers/page.tsx b/projects/packages/premium-analytics/routes/reports/comment-followers/page.tsx index bd24884a5844..126f0f4823dc 100644 --- a/projects/packages/premium-analytics/routes/reports/comment-followers/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/comment-followers/page.tsx @@ -6,7 +6,8 @@ import { Text } from '@jetpack-premium-analytics/externals'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { MetricValue, - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageSection, ReportPageShell, @@ -102,9 +103,14 @@ function CommentFollowersReport(): JSX.Element { > { records.isError ? ( - ) : ( <> diff --git a/projects/packages/premium-analytics/routes/reports/comments/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/comments/config/use-report-records.ts index 897d9e2cb554..31cc8a34cf2a 100644 --- a/projects/packages/premium-analytics/routes/reports/comments/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/comments/config/use-report-records.ts @@ -34,6 +34,7 @@ export function useCommentsReportRecords( activeTab: CommentsReportTabId ) { isLoading: report.isLoading, isFetching: report.isFetching, isError: report.isError, + error: report.error, refetch: report.refetch, }; } diff --git a/projects/packages/premium-analytics/routes/reports/comments/page.tsx b/projects/packages/premium-analytics/routes/reports/comments/page.tsx index 9e506dfe80b9..4521f020bbf0 100644 --- a/projects/packages/premium-analytics/routes/reports/comments/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/comments/page.tsx @@ -5,7 +5,8 @@ import { useSectionTab } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { ExporterCsvAction, - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ReportPageTabs, @@ -87,9 +88,14 @@ function CommentsReport(): JSX.Element { tabs={ } > { records.isError ? ( - ) : ( diff --git a/projects/packages/premium-analytics/routes/reports/downloads/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/downloads/config/use-report-records.ts index a8466f9040bf..8e3d446e2a46 100644 --- a/projects/packages/premium-analytics/routes/reports/downloads/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/downloads/config/use-report-records.ts @@ -20,6 +20,7 @@ export function useDownloadsReportRecords( reportParams: ReportParams ) { return { isError: report.isError, + error: report.error, refetch: report.refetch, rows: report.comparisonRows?.rows ?? [], hasComparison: report.hasComparison, diff --git a/projects/packages/premium-analytics/routes/reports/downloads/page.test.tsx b/projects/packages/premium-analytics/routes/reports/downloads/page.test.tsx index 5c2868e44772..6bfdd3b01456 100644 --- a/projects/packages/premium-analytics/routes/reports/downloads/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/downloads/page.test.tsx @@ -29,7 +29,7 @@ jest.mock( '@jetpack-premium-analytics/ui', () => ( { jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { ExporterCsvAction: () => null, - ReportErrorState: () => null, + PageNotice: () => null, ReportPageLayout: ( { children }: { children: ReactNode } ) => <>{ children }, ReportPageShell: ( { children }: { children: ReactNode } ) => <>{ children }, ReportRecordsTable: jest.fn( () => null ), @@ -69,6 +69,7 @@ describe( 'DownloadsReportPage', () => { it( 'reports the loading state on first load, when there is nothing to show yet', () => { useRecordsMock.mockReturnValue( { isError: false, + error: null, refetch: jest.fn(), rows: [], hasComparison: false, @@ -91,6 +92,7 @@ describe( 'DownloadsReportPage', () => { // page position survive a date or comparison change instead of resetting. useRecordsMock.mockReturnValue( { isError: false, + error: null, refetch: jest.fn(), rows: [ row ], hasComparison: true, @@ -112,6 +114,7 @@ describe( 'DownloadsReportPage', () => { it( 'shows current rows once both requests are settled', () => { useRecordsMock.mockReturnValue( { isError: false, + error: null, refetch: jest.fn(), rows: [ row ], hasComparison: true, diff --git a/projects/packages/premium-analytics/routes/reports/downloads/page.tsx b/projects/packages/premium-analytics/routes/reports/downloads/page.tsx index bfd244c307e9..9ebdace23a07 100644 --- a/projects/packages/premium-analytics/routes/reports/downloads/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/downloads/page.tsx @@ -5,7 +5,8 @@ import { type StatsFileDownloadsComparisonItem } from '@jetpack-premium-analytic import { useReportDateFilters } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ReportRecordsTable, @@ -67,9 +68,14 @@ function DownloadsReport(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/reports/earnings/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/earnings/config/use-report-records.ts index 66976c724991..c73633ad714c 100644 --- a/projects/packages/premium-analytics/routes/reports/earnings/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/earnings/config/use-report-records.ts @@ -54,6 +54,7 @@ export function useEarningsReportRecords( tab: EarningsReportTabId ) { isLoading: report.isLoading, isFetching: report.isFetching, isError: report.isError, + error: report.error, refetch: report.refetch, }; } diff --git a/projects/packages/premium-analytics/routes/reports/earnings/page.tsx b/projects/packages/premium-analytics/routes/reports/earnings/page.tsx index 24264d1b63f8..3351355ddb71 100644 --- a/projects/packages/premium-analytics/routes/reports/earnings/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/earnings/page.tsx @@ -5,7 +5,8 @@ import { useSectionTab } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { ReportCsvAction, - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ReportPageTabs, @@ -148,9 +149,14 @@ function EarningsReport(): JSX.Element { } > { records.isError ? ( - ) : ( diff --git a/projects/packages/premium-analytics/routes/reports/emails/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/emails/config/use-report-records.ts index fbbed29507aa..b65d7a1de94a 100644 --- a/projects/packages/premium-analytics/routes/reports/emails/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/emails/config/use-report-records.ts @@ -22,6 +22,7 @@ export function useEmailsReportRecords() { isLoading: report.isLoading, isFetching: report.isFetching, isError: report.isError, + error: report.error, refetch: report.refetch, }; } diff --git a/projects/packages/premium-analytics/routes/reports/emails/page.tsx b/projects/packages/premium-analytics/routes/reports/emails/page.tsx index a4c50a4fcd54..78ab8fe59e8c 100644 --- a/projects/packages/premium-analytics/routes/reports/emails/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/emails/page.tsx @@ -4,7 +4,8 @@ import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { ExporterCsvAction, - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ReportRecordsTable, @@ -80,9 +81,14 @@ function EmailsReport(): JSX.Element { > { records.isError ? ( - ) : ( diff --git a/projects/packages/premium-analytics/routes/reports/locations/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/locations/config/use-report-records.ts index f4a70d2ef7ec..dc682167e693 100644 --- a/projects/packages/premium-analytics/routes/reports/locations/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/locations/config/use-report-records.ts @@ -77,6 +77,7 @@ export function useLocationsReportRecords( options: countryOptions, }, isError: activeReport.primary.isError, + error: activeReport.primary.error, refetch: activeReport.refetch, }; } diff --git a/projects/packages/premium-analytics/routes/reports/locations/page.test.tsx b/projects/packages/premium-analytics/routes/reports/locations/page.test.tsx index cea02ca563d2..f5bbe80a540d 100644 --- a/projects/packages/premium-analytics/routes/reports/locations/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/locations/page.test.tsx @@ -5,7 +5,7 @@ import { usePrefetchViewerCountry } from '@jetpack-premium-analytics/data'; import { useSectionTab } from '@jetpack-premium-analytics/routing'; import { ExporterCsvAction, - ReportErrorState, + PageNotice, ReportLocationsMap, ReportPageTabs, ReportRecordsTable, @@ -17,6 +17,7 @@ import { useState } from 'react'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { getLocationFields, useLocationsReportRecords } from './config'; import LocationsReportPage from './page'; import type { LocationRow, ReportLocationsTabId } from './config'; @@ -61,12 +62,10 @@ jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { supportsLocationsCountryFilter: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ) .supportsLocationsCountryFilter, flagUrl: ( countryCode: string ) => `https://example.com/${ countryCode }.svg`, - ReportErrorState: jest.fn( ( { title, onRetry }: { title: string; onRetry: () => void } ) => ( -
- { title } - -
- ) ), + PageNotice: jest.fn( + jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).PageNotice + ), + describeError: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).describeError, ReportPageLayout: ( { tabs, children }: { tabs: ReactNode; children: ReactNode } ) => ( <> { tabs } @@ -103,7 +102,7 @@ jest.mock( '@wordpress/route', () => ( { const useRecordsMock = jest.mocked( useLocationsReportRecords ); const useSectionTabMock = jest.mocked( useSectionTab ); -const reportErrorStateMock = jest.mocked( ReportErrorState ); +const pageNoticeMock = jest.mocked( PageNotice ); const reportPageTabsMock = jest.mocked( ReportPageTabs ); const reportRecordsTableMock = jest.mocked( ReportRecordsTable ); const reportLocationsMapMock = jest.mocked( ReportLocationsMap ); @@ -282,13 +281,22 @@ describe( 'LocationsReportPage', () => { render( ); - expect( screen.getByTestId( 'report-error-state' ) ).toHaveTextContent( - 'Unable to load locations' - ); - expect( reportErrorStateMock ).toHaveBeenCalled(); + expect( + getNoticeText( "We couldn't load locations. Please try again in a moment." ) + ).toBeInTheDocument(); + expect( pageNoticeMock ).toHaveBeenCalled(); expect( reportRecordsTableMock ).not.toHaveBeenCalled(); } ); + it( 'drops Retry when the request is denied', () => { + mockRecords( { isError: true, error: { error: 'unauthorized', status: 403 } } ); + + render( ); + + expect( getNoticeText( "You don't have access to this data." ) ).toBeInTheDocument(); + expect( screen.queryByRole( 'button', { name: 'Retry' } ) ).not.toBeInTheDocument(); + } ); + it( 'refetches the active tab when Retry is clicked', async () => { const records = mockRecords( { isError: true } ); diff --git a/projects/packages/premium-analytics/routes/reports/locations/page.tsx b/projects/packages/premium-analytics/routes/reports/locations/page.tsx index 8502dbad999a..5453b49ca036 100644 --- a/projects/packages/premium-analytics/routes/reports/locations/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/locations/page.tsx @@ -7,7 +7,8 @@ import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { ExporterCsvAction, LOCATIONS_GEO_MODES, - ReportErrorState, + PageNotice, + describeError, ReportLocationsMap, ReportPageLayout, ReportPageShell, @@ -164,9 +165,14 @@ export default function LocationsReportPage(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/reports/posts/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/posts/config/use-report-records.ts index 1e4197084666..8156c194550b 100644 --- a/projects/packages/premium-analytics/routes/reports/posts/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/posts/config/use-report-records.ts @@ -43,6 +43,7 @@ export function usePostsReportRecords( activeTab: ReportPostsTabId, reportParams return { isError: activeReport.isError, + error: activeReport.error, refetch: activeReport.refetch, posts: { rows: postRows, diff --git a/projects/packages/premium-analytics/routes/reports/posts/page.test.tsx b/projects/packages/premium-analytics/routes/reports/posts/page.test.tsx index 97d2437922dd..10563ec59903 100644 --- a/projects/packages/premium-analytics/routes/reports/posts/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/posts/page.test.tsx @@ -6,7 +6,7 @@ import { useSectionTab } from '@jetpack-premium-analytics/routing'; import { ExporterCsvAction, ReportDrilldownTable, - ReportErrorState, + PageNotice, ReportRecordsTable, archivesCsvExporter, postsPagesCsvExporter, @@ -16,6 +16,7 @@ import userEvent from '@testing-library/user-event'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { usePostsReportRecords } from './config'; import PostsReportPage from './page'; import type { ReactNode } from 'react'; @@ -52,12 +53,10 @@ jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { }; } )(), formatLegendLabels: () => [], - ReportErrorState: jest.fn( ( { title, onRetry }: { title: string; onRetry: () => void } ) => ( -
- { title } - -
- ) ), + PageNotice: jest.fn( + jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).PageNotice + ), + describeError: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).describeError, ReportPageLayout: ( { children }: { children: ReactNode } ) => <>{ children }, ReportPageShell: ( { actions, children }: { actions?: ReactNode; children: ReactNode } ) => ( <> @@ -93,7 +92,7 @@ const usePostThumbnailsMock = jest.mocked( usePostThumbnails ); const useSectionTabMock = jest.mocked( useSectionTab ); const exporterCsvActionMock = jest.mocked( ExporterCsvAction ); const reportDrilldownTableMock = jest.mocked( ReportDrilldownTable ); -const reportErrorStateMock = jest.mocked( ReportErrorState ); +const pageNoticeMock = jest.mocked( PageNotice ); const reportRecordsTableMock = jest.mocked( ReportRecordsTable ); /** @@ -116,6 +115,7 @@ function buildRecords( { } = {} ) { return { isError, + error: null, refetch: jest.fn(), posts: { rows: [ @@ -269,10 +269,10 @@ describe( 'PostsReportPage', () => { render( ); - expect( screen.getByTestId( 'report-error-state' ) ).toHaveTextContent( - 'Unable to load posts' - ); - expect( reportErrorStateMock ).toHaveBeenCalled(); + expect( + getNoticeText( "We couldn't load posts. Please try again in a moment." ) + ).toBeInTheDocument(); + expect( pageNoticeMock ).toHaveBeenCalled(); expect( reportRecordsTableMock ).not.toHaveBeenCalled(); expect( reportDrilldownTableMock ).not.toHaveBeenCalled(); } ); diff --git a/projects/packages/premium-analytics/routes/reports/posts/page.tsx b/projects/packages/premium-analytics/routes/reports/posts/page.tsx index 6da20b0e9297..f0a0650e9add 100644 --- a/projects/packages/premium-analytics/routes/reports/posts/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/posts/page.tsx @@ -8,7 +8,8 @@ import { import { useReportDateFilters, useSectionTab } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ReportPageTabs, @@ -188,9 +189,14 @@ function PostsReport(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/reports/referrers/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/referrers/config/use-report-records.ts index 4412541484d5..6f80dff1288d 100644 --- a/projects/packages/premium-analytics/routes/reports/referrers/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/referrers/config/use-report-records.ts @@ -25,6 +25,7 @@ export function useReferrersReportRecords( reportParams: ReportParams ) { return { isError: report.isError, + error: report.error, refetch: report.refetch, rows, isLoading: report.isLoading, diff --git a/projects/packages/premium-analytics/routes/reports/referrers/page.tsx b/projects/packages/premium-analytics/routes/reports/referrers/page.tsx index 5168948a7018..81ffdc51cb39 100644 --- a/projects/packages/premium-analytics/routes/reports/referrers/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/referrers/page.tsx @@ -5,7 +5,8 @@ import { useReportDateFilters } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { ReportDrilldownTable, - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ExporterCsvAction, @@ -78,9 +79,14 @@ function ReferrersReport(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/reports/report-csv-exports.test.tsx b/projects/packages/premium-analytics/routes/reports/report-csv-exports.test.tsx index 2b828e7b5761..ff7f257d44f8 100644 --- a/projects/packages/premium-analytics/routes/reports/report-csv-exports.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/report-csv-exports.test.tsx @@ -69,7 +69,7 @@ jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => { getWordAdsHistoryFields: () => [], ReportCsvAction: jest.fn( () => null ), ReportDrilldownTable: () => null, - ReportErrorState: () => null, + PageNotice: () => null, ReportPageLayout: Container, ReportPageSection: Container, ReportPageShell: Container, @@ -146,6 +146,7 @@ const reportStatus = { isLoading: false, isFetching: false, isError: false, + error: null, refetch: jest.fn(), }; diff --git a/projects/packages/premium-analytics/routes/reports/search-terms/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/search-terms/config/use-report-records.ts index 0c423f7ca4a2..32cf2e8f6e20 100644 --- a/projects/packages/premium-analytics/routes/reports/search-terms/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/search-terms/config/use-report-records.ts @@ -47,6 +47,7 @@ export function useSearchTermsReportRecords( reportParams: ReportParams ) { // A comparison-only failure still renders the table with primary rows // and no deltas, via the comparisonSettled guard above. isError: report.primary.isError, + error: report.primary.error, refetch: report.refetch, table: { ...table, diff --git a/projects/packages/premium-analytics/routes/reports/search-terms/page.test.tsx b/projects/packages/premium-analytics/routes/reports/search-terms/page.test.tsx index a077a74780e5..3540f1d960bf 100644 --- a/projects/packages/premium-analytics/routes/reports/search-terms/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/search-terms/page.test.tsx @@ -31,7 +31,7 @@ jest.mock( '@jetpack-premium-analytics/ui', () => ( { jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { ExporterCsvAction: () => null, - ReportErrorState: () => null, + PageNotice: () => null, ReportPageLayout: ( { children }: { children: ReactNode } ) => <>{ children }, ReportPageShell: ( { children }: { children: ReactNode } ) => <>{ children }, ReportRecordsTable: jest.fn( () => null ), diff --git a/projects/packages/premium-analytics/routes/reports/search-terms/page.tsx b/projects/packages/premium-analytics/routes/reports/search-terms/page.tsx index 6ef3bf9bfac7..72822eb26c63 100644 --- a/projects/packages/premium-analytics/routes/reports/search-terms/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/search-terms/page.tsx @@ -4,7 +4,8 @@ import { useReportDateFilters } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ReportRecordsTable, @@ -67,9 +68,14 @@ export default function SearchTermsReportPage(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/reports/tags/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/tags/config/use-report-records.ts index 7f62eb8e3f9c..52613e126bd1 100644 --- a/projects/packages/premium-analytics/routes/reports/tags/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/tags/config/use-report-records.ts @@ -32,6 +32,7 @@ export function useTagsReportRecords() { isLoading: tags.isLoading, isFetching: tags.isFetching, isError: tags.isError, + error: tags.error, refetch: tags.refetch, }; } diff --git a/projects/packages/premium-analytics/routes/reports/tags/page.tsx b/projects/packages/premium-analytics/routes/reports/tags/page.tsx index 768570276364..263314d46114 100644 --- a/projects/packages/premium-analytics/routes/reports/tags/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/tags/page.tsx @@ -4,7 +4,8 @@ import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { ExporterCsvAction, - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ReportRecordsTable, @@ -68,9 +69,14 @@ function TagsReport(): JSX.Element { > { records.isError ? ( - ) : ( diff --git a/projects/packages/premium-analytics/routes/reports/utm/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/utm/config/use-report-records.ts index 56d2e010cb58..3037595d2924 100644 --- a/projects/packages/premium-analytics/routes/reports/utm/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/utm/config/use-report-records.ts @@ -58,6 +58,7 @@ export function useUtmReportRecords( activeTab: UtmReportTabId, reportParams: Re isLoading: activeReport.isLoading, isFetching: activeReport.isFetching, isError: activeReport.isError, + error: activeReport.error, refetch: activeReport.refetch, }; } diff --git a/projects/packages/premium-analytics/routes/reports/utm/page.test.tsx b/projects/packages/premium-analytics/routes/reports/utm/page.test.tsx index 4c0a3829cfb6..32fca239da78 100644 --- a/projects/packages/premium-analytics/routes/reports/utm/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/utm/page.test.tsx @@ -35,7 +35,7 @@ jest.mock( '@jetpack-premium-analytics/ui', () => ( { jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { ExporterCsvAction: () => null, ReportDrilldownTable: jest.fn( () => null ), - ReportErrorState: () => null, + PageNotice: () => null, ReportPageLayout: ( { children }: { children: ReactNode } ) => <>{ children }, ReportPageShell: ( { children }: { children: ReactNode } ) => <>{ children }, ReportPageTabs: () => null, diff --git a/projects/packages/premium-analytics/routes/reports/utm/page.tsx b/projects/packages/premium-analytics/routes/reports/utm/page.tsx index 97ba1dd59d74..217422a0524a 100644 --- a/projects/packages/premium-analytics/routes/reports/utm/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/utm/page.tsx @@ -4,7 +4,8 @@ import { useReportDateFilters, useSectionTab } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { - ReportErrorState, + PageNotice, + describeError, ReportDrilldownTable, ReportPageLayout, ReportPageShell, @@ -85,9 +86,14 @@ function UtmReport(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/reports/videos/config/use-report-records.ts b/projects/packages/premium-analytics/routes/reports/videos/config/use-report-records.ts index b97655d5f65a..893f271f94ff 100644 --- a/projects/packages/premium-analytics/routes/reports/videos/config/use-report-records.ts +++ b/projects/packages/premium-analytics/routes/reports/videos/config/use-report-records.ts @@ -26,6 +26,7 @@ export function useVideosReportRecords( reportParams: ReportParams ) { return { isError: summary.primary.isError, + error: summary.primary.error, refetch: summary.refetch, rows: summary.comparisonRows?.rows ?? EMPTY_VIDEO_ROWS, hasComparison: summary.hasComparison, diff --git a/projects/packages/premium-analytics/routes/reports/videos/page.test.tsx b/projects/packages/premium-analytics/routes/reports/videos/page.test.tsx index 4b3720fe33c6..bd03e0276d83 100644 --- a/projects/packages/premium-analytics/routes/reports/videos/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/videos/page.test.tsx @@ -3,7 +3,7 @@ */ import { ExporterCsvAction, - ReportErrorState, + PageNotice, ReportRecordsTable, } from '@jetpack-premium-analytics/widgets-toolkit'; import { render, screen } from '@testing-library/react'; @@ -11,6 +11,7 @@ import userEvent from '@testing-library/user-event'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { getVideosFields, useVideosReportRecords } from './config'; import VideosReportPage from './page'; import type { StatsVideoPlaysComparisonItem } from '@jetpack-premium-analytics/data'; @@ -34,12 +35,10 @@ jest.mock( '@jetpack-premium-analytics/ui', () => ( { } ) ); jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { - ReportErrorState: jest.fn( ( { title, onRetry }: { title: string; onRetry: () => void } ) => ( -
- { title } - -
- ) ), + PageNotice: jest.fn( + jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).PageNotice + ), + describeError: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).describeError, ReportPageLayout: ( { children }: { children: ReactNode } ) => <>{ children }, ReportPageShell: ( { actions, children }: { actions?: ReactNode; children: ReactNode } ) => ( <> @@ -69,7 +68,7 @@ jest.mock( '@wordpress/route', () => ( { const useRecordsMock = jest.mocked( useVideosReportRecords ); const exporterCsvActionMock = jest.mocked( ExporterCsvAction ); const getVideosFieldsMock = jest.mocked( getVideosFields ); -const reportErrorStateMock = jest.mocked( ReportErrorState ); +const pageNoticeMock = jest.mocked( PageNotice ); const reportRecordsTableMock = jest.mocked( ReportRecordsTable ); const videoRow = { @@ -112,7 +111,7 @@ describe( 'VideosReportPage', () => { render( ); expect( reportRecordsTableMock ).toHaveBeenCalled(); - expect( reportErrorStateMock ).not.toHaveBeenCalled(); + expect( pageNoticeMock ).not.toHaveBeenCalled(); } ); it( 'draws each video poster beside its title', () => { @@ -244,9 +243,9 @@ describe( 'VideosReportPage', () => { render( ); - expect( screen.getByTestId( 'report-error-state' ) ).toHaveTextContent( - 'Unable to load videos' - ); + expect( + getNoticeText( "We couldn't load videos. Please try again in a moment." ) + ).toBeInTheDocument(); expect( reportRecordsTableMock ).not.toHaveBeenCalled(); } ); diff --git a/projects/packages/premium-analytics/routes/reports/videos/page.tsx b/projects/packages/premium-analytics/routes/reports/videos/page.tsx index ef6eed05577b..6932e56a9339 100644 --- a/projects/packages/premium-analytics/routes/reports/videos/page.tsx +++ b/projects/packages/premium-analytics/routes/reports/videos/page.tsx @@ -5,7 +5,8 @@ import { type StatsVideoPlaysComparisonItem } from '@jetpack-premium-analytics/d import { useReportDateFilters } from '@jetpack-premium-analytics/routing'; import { StatsBreadcrumbs, StatsPageIcon } from '@jetpack-premium-analytics/ui'; import { - ReportErrorState, + PageNotice, + describeError, ReportPageLayout, ReportPageShell, ReportRecordsTable, @@ -81,9 +82,14 @@ function VideosReport(): JSX.Element { if ( records.isError ) { tableReplacement = ( - ); } diff --git a/projects/packages/premium-analytics/routes/video-detail/stage.test.tsx b/projects/packages/premium-analytics/routes/video-detail/stage.test.tsx index 9640a26eaaab..d77d509942bb 100644 --- a/projects/packages/premium-analytics/routes/video-detail/stage.test.tsx +++ b/projects/packages/premium-analytics/routes/video-detail/stage.test.tsx @@ -271,7 +271,7 @@ describe( 'video detail stage', () => { expect( getNoticeText( "You don't have access to this data." ) ).toBeInTheDocument(); expect( - getNoticeAnnouncement( "You don't have access to this data.", 'polite' ) + getNoticeAnnouncement( "You don't have access to this data.", 'assertive' ) ).toBeInTheDocument(); expect( screen.queryByRole( 'button', { name: 'Retry' } ) ).not.toBeInTheDocument(); } ); diff --git a/projects/packages/premium-analytics/routes/video-detail/stage.tsx b/projects/packages/premium-analytics/routes/video-detail/stage.tsx index ef3286d99387..044a1f55afe2 100644 --- a/projects/packages/premium-analytics/routes/video-detail/stage.tsx +++ b/projects/packages/premium-analytics/routes/video-detail/stage.tsx @@ -15,14 +15,14 @@ import { DetailPageActions, DetailPageBreadcrumbs, DetailPageLayout, - DetailPageNotice, + PageNotice, DetailPageSection, DetailPageShell, - describeDetailPageError, + describeError, useDetailPageCustomize, useStoredDetailLayout, useTrackedDateRangeApply, - type DetailPageNoticeProps, + type PageNoticeProps, } from '@jetpack-premium-analytics/widgets-toolkit'; import { useCallback } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -138,10 +138,10 @@ function VideoDetail(): JSX.Element { // The reason a video is missing goes below the header, where the widgets // would have been. - let notice: DetailPageNoticeProps | null = null; + let notice: PageNoticeProps | null = null; if ( summary.isError ) { - notice = describeDetailPageError( summary.error, { + notice = describeError( summary.error, { retryDescription: __( "We couldn't load this video. Please try again in a moment.", 'jetpack-premium-analytics-pkg' @@ -217,7 +217,7 @@ function VideoDetail(): JSX.Element { ) : null } { notice ? ( - + ) : null } diff --git a/projects/packages/premium-analytics/tests/groups/widgets-toolkit-no-mocks-part2.test.tsx b/projects/packages/premium-analytics/tests/groups/widgets-toolkit-no-mocks-part2.test.tsx index c348c92b9470..4c94da4c7ddf 100644 --- a/projects/packages/premium-analytics/tests/groups/widgets-toolkit-no-mocks-part2.test.tsx +++ b/projects/packages/premium-analytics/tests/groups/widgets-toolkit-no-mocks-part2.test.tsx @@ -2,7 +2,6 @@ import '../../packages/widgets-toolkit/src/components/metric-tile/__tests__/metric-tile-grid.test'; import '../../packages/widgets-toolkit/src/components/post-highlight-card/__tests__/post-highlight-card-skeleton.test'; -import '../../packages/widgets-toolkit/src/components/report-page/__tests__/report-error-state.test'; import '../../packages/widgets-toolkit/src/components/report-page/__tests__/report-records-table.test'; import '../../packages/widgets-toolkit/src/components/report-page/utils/build-report-metric-series.test'; import '../../packages/widgets-toolkit/src/components/subscriber-list/__tests__/subscriber-list-fit-rows.test'; diff --git a/projects/plugins/jetpack/changelog/change-uni-846-report-error-notice b/projects/plugins/jetpack/changelog/change-uni-846-report-error-notice new file mode 100644 index 000000000000..5925b8aa9159 --- /dev/null +++ b/projects/plugins/jetpack/changelog/change-uni-846-report-error-notice @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +Premium Analytics: Show load errors on reports in a notice that spans the report, stop offering Retry there when access is denied, and show access denied on author, post, and video details as an error. diff --git a/projects/plugins/premium-analytics/changelog/change-uni-846-report-error-notice b/projects/plugins/premium-analytics/changelog/change-uni-846-report-error-notice new file mode 100644 index 000000000000..4465356c316f --- /dev/null +++ b/projects/plugins/premium-analytics/changelog/change-uni-846-report-error-notice @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Show load errors on reports in a notice that spans the report, stop offering Retry there when access is denied, and show access denied on author, post, and video details as an error. diff --git a/projects/plugins/wpcomsh/changelog/change-uni-846-report-error-notice b/projects/plugins/wpcomsh/changelog/change-uni-846-report-error-notice new file mode 100644 index 000000000000..1ae053aa0f1d --- /dev/null +++ b/projects/plugins/wpcomsh/changelog/change-uni-846-report-error-notice @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Premium Analytics: Show load errors on reports in a notice that spans the report, stop offering Retry there when access is denied, and show access denied on author, post, and video details as an error. From a1d0ae563ffd1d25fda461f177ec8fdd247636df Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 5 Oct 2026 15:12:53 +0800 Subject: [PATCH 2/2] Premium Analytics: Fix report error tests and test PageNotice's default intent Update six report page tests still asserting the removed 'Unable to load' titles, add a PageNotice test for its assertive default, drop redundant mock assertions, state the intent rule instead of its history, and split the changelog entry into two sentences. --- .../change-uni-846-report-error-notice | 2 +- .../__tests__/page-notice.test.tsx | 20 +++++++++++++++++++ .../components/page-notice/page-notice.tsx | 2 +- .../reports/annual-insights/page.test.tsx | 5 ++++- .../routes/reports/authors/page.test.tsx | 5 ++++- .../reports/comment-followers/page.test.tsx | 7 +++++-- .../routes/reports/comments/page.test.tsx | 5 ++++- .../routes/reports/earnings/page.test.tsx | 5 ++++- .../routes/reports/emails/page.test.tsx | 5 ++++- .../routes/reports/locations/page.test.tsx | 7 +------ .../routes/reports/posts/page.test.tsx | 7 +------ .../widgets-toolkit-no-mocks-part2.test.tsx | 1 + .../change-uni-846-report-error-notice | 2 +- .../change-uni-846-report-error-notice | 2 +- .../change-uni-846-report-error-notice | 2 +- 15 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/__tests__/page-notice.test.tsx diff --git a/projects/packages/premium-analytics/changelog/change-uni-846-report-error-notice b/projects/packages/premium-analytics/changelog/change-uni-846-report-error-notice index 4465356c316f..470193703d3c 100644 --- a/projects/packages/premium-analytics/changelog/change-uni-846-report-error-notice +++ b/projects/packages/premium-analytics/changelog/change-uni-846-report-error-notice @@ -1,4 +1,4 @@ Significance: patch Type: fixed -Show load errors on reports in a notice that spans the report, stop offering Retry there when access is denied, and show access denied on author, post, and video details as an error. +Show report load errors in a notice across the report, without Retry when access is denied. Show access denied on author, post, and video details as an error. diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/__tests__/page-notice.test.tsx b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/__tests__/page-notice.test.tsx new file mode 100644 index 000000000000..417591bddec4 --- /dev/null +++ b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/__tests__/page-notice.test.tsx @@ -0,0 +1,20 @@ +/** + * External dependencies + */ +import { render } from '@testing-library/react'; +/** + * Internal dependencies + */ +import { getNoticeAnnouncement } from '../../../../../../tests/js/notice-test-utils'; +import { PageNotice } from '../page-notice'; + +describe( 'PageNotice', () => { + it.each( [ + [ 'assertively by default', undefined, 'assertive' ], + [ 'politely for info', 'info', 'polite' ], + ] as const )( 'announces %s', ( _, intent, politeness ) => { + render( ); + + expect( getNoticeAnnouncement( 'Something happened.', politeness ) ).toBeInTheDocument(); + } ); +} ); diff --git a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/page-notice.tsx b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/page-notice.tsx index 8fb722dbb7c1..831858eb7379 100644 --- a/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/page-notice.tsx +++ b/projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-notice/page-notice.tsx @@ -9,7 +9,7 @@ import type { WidgetStateError } from '../widget-state'; import type { ReactElement } from 'react'; export interface PageNoticeProps extends Pick< WidgetStateError, 'description' | 'actions' > { - /** `info` for a fact rather than a fault, such as a missing item; access denied stays an `error`, as on the widgets. */ + /** `info` for a fact rather than a fault, such as a missing item; failures, access denied included, are an `error`. */ intent?: 'error' | 'info'; /** A way out of the page, such as back to its report; `render` is a childless router link, as its children would replace `label`. */ link?: { label: string; render: ReactElement }; diff --git a/projects/packages/premium-analytics/routes/reports/annual-insights/page.test.tsx b/projects/packages/premium-analytics/routes/reports/annual-insights/page.test.tsx index c490f3867e05..f403ad092e0e 100644 --- a/projects/packages/premium-analytics/routes/reports/annual-insights/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/annual-insights/page.test.tsx @@ -5,6 +5,7 @@ import { render, screen } from '@testing-library/react'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { useAnnualInsightsReportRecords } from './config'; import AnnualInsightsReportPage from './page'; import type { StatsInsightsYear } from '@jetpack-premium-analytics/data'; @@ -72,7 +73,9 @@ describe( 'AnnualInsightsReportPage', () => { render( ); - expect( screen.getByText( 'Unable to load annual insights' ) ).toBeInTheDocument(); + expect( + getNoticeText( "We couldn't load annual insights. Please try again in a moment." ) + ).toBeInTheDocument(); expect( screen.getByRole( 'button', { name: 'Retry' } ) ).toBeInTheDocument(); expect( screen.queryByText( '2026' ) ).not.toBeInTheDocument(); } ); diff --git a/projects/packages/premium-analytics/routes/reports/authors/page.test.tsx b/projects/packages/premium-analytics/routes/reports/authors/page.test.tsx index 707ee6d85f7e..b62069ef0a6f 100644 --- a/projects/packages/premium-analytics/routes/reports/authors/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/authors/page.test.tsx @@ -9,6 +9,7 @@ import { render, screen } from '@testing-library/react'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { useAuthorsReportRecords } from './config'; import AuthorsReportPage from './page'; import type { AuthorRow } from '@jetpack-premium-analytics/widgets-toolkit'; @@ -114,7 +115,9 @@ describe( 'AuthorsReportPage', () => { render( ); - expect( screen.getByText( 'Unable to load authors' ) ).toBeInTheDocument(); + expect( + getNoticeText( "We couldn't load authors. Please try again in a moment." ) + ).toBeInTheDocument(); expect( screen.getByRole( 'button', { name: 'Retry' } ) ).toBeInTheDocument(); expect( screen.queryByText( 'Ada Lovelace' ) ).not.toBeInTheDocument(); } ); diff --git a/projects/packages/premium-analytics/routes/reports/comment-followers/page.test.tsx b/projects/packages/premium-analytics/routes/reports/comment-followers/page.test.tsx index 22b1a88e946f..5234799dce5f 100644 --- a/projects/packages/premium-analytics/routes/reports/comment-followers/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/comment-followers/page.test.tsx @@ -5,6 +5,7 @@ import { render, screen } from '@testing-library/react'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { useCommentFollowersReportRecords } from './config'; import CommentFollowersReportPage from './page'; import type { ReactNode } from 'react'; @@ -65,7 +66,9 @@ describe( 'CommentFollowersReportPage', () => { render( ); - expect( screen.getByText( 'Unable to load subscribers' ) ).toBeInTheDocument(); + expect( + getNoticeText( "We couldn't load subscribers. Please try again in a moment." ) + ).toBeInTheDocument(); expect( screen.getByRole( 'button', { name: 'Retry' } ) ).toBeInTheDocument(); // The stale row must not stay on screen behind the error. expect( screen.queryByText( 'Hello world' ) ).not.toBeInTheDocument(); @@ -82,7 +85,7 @@ describe( 'CommentFollowersReportPage', () => { render( ); expect( screen.getByText( 'All Posts' ) ).toBeInTheDocument(); - expect( screen.queryByText( 'Unable to load subscribers' ) ).not.toBeInTheDocument(); + expect( screen.queryByRole( 'button', { name: 'Retry' } ) ).not.toBeInTheDocument(); } ); it( 'hides the All Posts summary when there are no subscribers', () => { diff --git a/projects/packages/premium-analytics/routes/reports/comments/page.test.tsx b/projects/packages/premium-analytics/routes/reports/comments/page.test.tsx index 2ab033e1d921..e858b2126b12 100644 --- a/projects/packages/premium-analytics/routes/reports/comments/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/comments/page.test.tsx @@ -5,6 +5,7 @@ import { render, screen } from '@testing-library/react'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { useCommentsReportRecords } from './config'; import CommentsReportPage from './page'; @@ -73,7 +74,9 @@ describe( 'CommentsReportPage', () => { render( ); - expect( screen.getByText( 'Unable to load comments' ) ).toBeInTheDocument(); + expect( + getNoticeText( "We couldn't load comments. Please try again in a moment." ) + ).toBeInTheDocument(); expect( screen.getByRole( 'button', { name: 'Retry' } ) ).toBeInTheDocument(); expect( screen.queryByText( 'Hello world' ) ).not.toBeInTheDocument(); } ); diff --git a/projects/packages/premium-analytics/routes/reports/earnings/page.test.tsx b/projects/packages/premium-analytics/routes/reports/earnings/page.test.tsx index 4a8ba99811a5..a96542c01941 100644 --- a/projects/packages/premium-analytics/routes/reports/earnings/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/earnings/page.test.tsx @@ -7,6 +7,7 @@ import { render, screen } from '@testing-library/react'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { useEarningsReportRecords } from './config'; import EarningsReportPage from './page'; import type { EarningsReportTabId } from './config'; @@ -152,7 +153,9 @@ describe( 'EarningsReportPage', () => { render( ); - expect( screen.getByText( 'Unable to load earnings' ) ).toBeInTheDocument(); + expect( + getNoticeText( "We couldn't load earnings. Please try again in a moment." ) + ).toBeInTheDocument(); expect( screen.getByRole( 'button', { name: 'Retry' } ) ).toBeInTheDocument(); expect( screen.queryByText( 'September 2026' ) ).not.toBeInTheDocument(); } ); diff --git a/projects/packages/premium-analytics/routes/reports/emails/page.test.tsx b/projects/packages/premium-analytics/routes/reports/emails/page.test.tsx index c1c0f8fd8535..db82ef6487b1 100644 --- a/projects/packages/premium-analytics/routes/reports/emails/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/emails/page.test.tsx @@ -6,6 +6,7 @@ import userEvent from '@testing-library/user-event'; /** * Internal dependencies */ +import { getNoticeText } from '../../../tests/js/notice-test-utils'; import { useEmailsReportRecords } from './config'; import EmailsReportPage from './page'; import type { StatsEmailSummaryItem } from '@jetpack-premium-analytics/data'; @@ -113,7 +114,9 @@ describe( 'EmailsReportPage', () => { render( ); - expect( screen.getByText( 'Unable to load emails' ) ).toBeInTheDocument(); + expect( + getNoticeText( "We couldn't load emails. Please try again in a moment." ) + ).toBeInTheDocument(); await userEvent.setup().click( screen.getByRole( 'button', { name: 'Retry' } ) ); expect( refetch ).toHaveBeenCalled(); } ); diff --git a/projects/packages/premium-analytics/routes/reports/locations/page.test.tsx b/projects/packages/premium-analytics/routes/reports/locations/page.test.tsx index f5bbe80a540d..f023342c6656 100644 --- a/projects/packages/premium-analytics/routes/reports/locations/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/locations/page.test.tsx @@ -5,7 +5,6 @@ import { usePrefetchViewerCountry } from '@jetpack-premium-analytics/data'; import { useSectionTab } from '@jetpack-premium-analytics/routing'; import { ExporterCsvAction, - PageNotice, ReportLocationsMap, ReportPageTabs, ReportRecordsTable, @@ -62,9 +61,7 @@ jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { supportsLocationsCountryFilter: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ) .supportsLocationsCountryFilter, flagUrl: ( countryCode: string ) => `https://example.com/${ countryCode }.svg`, - PageNotice: jest.fn( - jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).PageNotice - ), + PageNotice: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).PageNotice, describeError: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).describeError, ReportPageLayout: ( { tabs, children }: { tabs: ReactNode; children: ReactNode } ) => ( <> @@ -102,7 +99,6 @@ jest.mock( '@wordpress/route', () => ( { const useRecordsMock = jest.mocked( useLocationsReportRecords ); const useSectionTabMock = jest.mocked( useSectionTab ); -const pageNoticeMock = jest.mocked( PageNotice ); const reportPageTabsMock = jest.mocked( ReportPageTabs ); const reportRecordsTableMock = jest.mocked( ReportRecordsTable ); const reportLocationsMapMock = jest.mocked( ReportLocationsMap ); @@ -284,7 +280,6 @@ describe( 'LocationsReportPage', () => { expect( getNoticeText( "We couldn't load locations. Please try again in a moment." ) ).toBeInTheDocument(); - expect( pageNoticeMock ).toHaveBeenCalled(); expect( reportRecordsTableMock ).not.toHaveBeenCalled(); } ); diff --git a/projects/packages/premium-analytics/routes/reports/posts/page.test.tsx b/projects/packages/premium-analytics/routes/reports/posts/page.test.tsx index 10563ec59903..9d2d9b839953 100644 --- a/projects/packages/premium-analytics/routes/reports/posts/page.test.tsx +++ b/projects/packages/premium-analytics/routes/reports/posts/page.test.tsx @@ -6,7 +6,6 @@ import { useSectionTab } from '@jetpack-premium-analytics/routing'; import { ExporterCsvAction, ReportDrilldownTable, - PageNotice, ReportRecordsTable, archivesCsvExporter, postsPagesCsvExporter, @@ -53,9 +52,7 @@ jest.mock( '@jetpack-premium-analytics/widgets-toolkit', () => ( { }; } )(), formatLegendLabels: () => [], - PageNotice: jest.fn( - jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).PageNotice - ), + PageNotice: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).PageNotice, describeError: jest.requireActual( '@jetpack-premium-analytics/widgets-toolkit' ).describeError, ReportPageLayout: ( { children }: { children: ReactNode } ) => <>{ children }, ReportPageShell: ( { actions, children }: { actions?: ReactNode; children: ReactNode } ) => ( @@ -92,7 +89,6 @@ const usePostThumbnailsMock = jest.mocked( usePostThumbnails ); const useSectionTabMock = jest.mocked( useSectionTab ); const exporterCsvActionMock = jest.mocked( ExporterCsvAction ); const reportDrilldownTableMock = jest.mocked( ReportDrilldownTable ); -const pageNoticeMock = jest.mocked( PageNotice ); const reportRecordsTableMock = jest.mocked( ReportRecordsTable ); /** @@ -272,7 +268,6 @@ describe( 'PostsReportPage', () => { expect( getNoticeText( "We couldn't load posts. Please try again in a moment." ) ).toBeInTheDocument(); - expect( pageNoticeMock ).toHaveBeenCalled(); expect( reportRecordsTableMock ).not.toHaveBeenCalled(); expect( reportDrilldownTableMock ).not.toHaveBeenCalled(); } ); diff --git a/projects/packages/premium-analytics/tests/groups/widgets-toolkit-no-mocks-part2.test.tsx b/projects/packages/premium-analytics/tests/groups/widgets-toolkit-no-mocks-part2.test.tsx index 4c94da4c7ddf..f114932df502 100644 --- a/projects/packages/premium-analytics/tests/groups/widgets-toolkit-no-mocks-part2.test.tsx +++ b/projects/packages/premium-analytics/tests/groups/widgets-toolkit-no-mocks-part2.test.tsx @@ -1,6 +1,7 @@ // See README.md before adding a suite to this group. import '../../packages/widgets-toolkit/src/components/metric-tile/__tests__/metric-tile-grid.test'; +import '../../packages/widgets-toolkit/src/components/page-notice/__tests__/page-notice.test'; import '../../packages/widgets-toolkit/src/components/post-highlight-card/__tests__/post-highlight-card-skeleton.test'; import '../../packages/widgets-toolkit/src/components/report-page/__tests__/report-records-table.test'; import '../../packages/widgets-toolkit/src/components/report-page/utils/build-report-metric-series.test'; diff --git a/projects/plugins/jetpack/changelog/change-uni-846-report-error-notice b/projects/plugins/jetpack/changelog/change-uni-846-report-error-notice index 5925b8aa9159..c22f0741286a 100644 --- a/projects/plugins/jetpack/changelog/change-uni-846-report-error-notice +++ b/projects/plugins/jetpack/changelog/change-uni-846-report-error-notice @@ -1,4 +1,4 @@ Significance: patch Type: bugfix -Premium Analytics: Show load errors on reports in a notice that spans the report, stop offering Retry there when access is denied, and show access denied on author, post, and video details as an error. +Premium Analytics: Show report load errors in a notice across the report, without Retry when access is denied. Show access denied on author, post, and video details as an error. diff --git a/projects/plugins/premium-analytics/changelog/change-uni-846-report-error-notice b/projects/plugins/premium-analytics/changelog/change-uni-846-report-error-notice index 4465356c316f..470193703d3c 100644 --- a/projects/plugins/premium-analytics/changelog/change-uni-846-report-error-notice +++ b/projects/plugins/premium-analytics/changelog/change-uni-846-report-error-notice @@ -1,4 +1,4 @@ Significance: patch Type: fixed -Show load errors on reports in a notice that spans the report, stop offering Retry there when access is denied, and show access denied on author, post, and video details as an error. +Show report load errors in a notice across the report, without Retry when access is denied. Show access denied on author, post, and video details as an error. diff --git a/projects/plugins/wpcomsh/changelog/change-uni-846-report-error-notice b/projects/plugins/wpcomsh/changelog/change-uni-846-report-error-notice index 1ae053aa0f1d..6213092cb95a 100644 --- a/projects/plugins/wpcomsh/changelog/change-uni-846-report-error-notice +++ b/projects/plugins/wpcomsh/changelog/change-uni-846-report-error-notice @@ -1,4 +1,4 @@ Significance: patch Type: fixed -Premium Analytics: Show load errors on reports in a notice that spans the report, stop offering Retry there when access is denied, and show access denied on author, post, and video details as an error. +Premium Analytics: Show report load errors in a notice across the report, without Retry when access is denied. Show access denied on author, post, and video details as an error.