Skip to content

WOOA7S-2211: Premium Analytics: Show the report empty state on every report - #52900

Merged
Nikschavan merged 7 commits into
trunkfrom
fix/pa-report-empty-state-all-reports
Oct 1, 2026
Merged

Nikschavan merged 7 commits into
trunkfrom
fix/pa-report-empty-state-all-reports

Conversation

@Nikschavan

@Nikschavan Nikschavan commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Fixes WOOA7S-2211

Proposed changes

  • WOOA7S-2211: Premium Analytics: Show a full-page empty state on reports with no data #52792 added the full-page "No data found" state to nine reports but left out Earnings, Tags & categories, Comments, Comment followers, Emails and Annual insights, because they have no period picker and the copy says "for this time period". Those six still show a search box over a small "No results" line when they have no rows. The check now lives in ReportRecordsTable and ReportDrilldownTable, so every report gets the empty state from the table it already renders, and a new report cannot miss it.
  • The copy says "this time period" only when the surrounding ReportPageLayout has date filters. The reports without a period picker get "We couldn’t find any results." instead.
  • On the nine reports WOOA7S-2211: Premium Analytics: Show a full-page empty state on reports with no data #52792 did cover, the search box and table settings showed while the rows loaded and then disappeared when the period turned out to be empty. The tables now show a centred spinner with no toolbar whenever the rows for the selected period are loading, including after switching away from an empty period. A refresh of the same period keeps what is on screen, so an empty report does not flash the spinner, and every report now shows the table's loading state while rows on screen refresh.
  • The check still reads the rows before the table's own search, so a search that matches nothing keeps the table and its "No results".
  • Locations keeps its table when a picked country has no rows, because the country filter lives in the table and hiding it would leave no way to clear it. Otherwise the map hides along with the table, as before, but it stays mounted while the rows load, so a map someone hid stays hidden.
  • Comment followers hides its "All Posts" card when there are no rows. Its in-table "No subscribers" text is gone, because it only showed when a search matched no posts, where "No results" is what the other reports say.
Before After
Tags & categories report with no rows, showing a search box, the settings button and a small "No results" line Tags & categories report with no rows, showing a centred magnifier, "No data found" and "We couldn’t find any results."
Tags & categories with no rows on trunk. The same report on this branch, with copy that does not mention a period.
Referrers report for January 2005 while the rows load, showing the search box and settings above a spinner Referrers report for January 2005 while the rows load, showing only a centred spinner
Referrers while an empty period loads on trunk. The same moment on this branch.

Related product discussion/links

Does this pull request change what data or activity we track or use?

No.

Testing instructions

  • Get this branch onto a site with the Premium Analytics dashboard enabled, either a Jurassic Ninja site with the branch synced or the Jetpack Beta Tester plugin pointed at fix/pa-report-empty-state-all-reports. A new site with no traffic is the easiest way to see the reports without a period picker empty.
  • Open the Tags & categories, Comments, Comment followers and Emails reports on a site with no data for them. Each should show the magnifier, "No data found" and "We couldn’t find any results.", with no search box. Comment followers should not show the "All Posts" card.
  • Open the Referrers report and set a custom period from before the site had traffic, for example January 2005. With network throttling on in the browser's developer tools, the page should show only a centred spinner while it loads, then "No data found" and "We couldn’t find results for this time period." The search box and settings should not appear at any point. Then switch to a period with traffic that you have not opened yet: the spinner should replace "No data found" until the rows arrive.
  • On Locations, open the Regions tab for a period with data and pick a country in the table filter that has no rows. The table should stay, with the filter that clears it.
  • Switch any report to a period with data and type a search that matches nothing. The table should stay, with its search box and "No results".

I checked the Referrers loading and empty states, and the Tags & categories empty state, on my local site. Earnings is not available on my site, so unit tests cover it.

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!


Jetpack plugin:

The Jetpack plugin has different release cadences depending on the platform:

  • WordPress.com Simple releases happen as soon as you deploy your changes after merging this PR (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly:
    • Scheduled release: October 6, 2026

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Wpcomsh plugin:

  • Next scheduled release: Atomic deploys happen twice daily on weekdays (p9o2xV-2EN-p2)

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Premium Analytics plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack or WordPress.com Site Helper), and enable the fix/pa-report-empty-state-all-reports branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack fix/pa-report-empty-state-all-reports

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@jp-launch-control

jp-launch-control Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Code Coverage Summary

Coverage changed in 14 files. Only the first 5 are listed here.

File Coverage Δ% Δ Uncovered
projects/packages/premium-analytics/routes/reports/search-terms/page.tsx 17/19 (89.47%) -5.98% 1 ❤️‍🩹
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/report-empty-state.tsx 5/5 (100.00%) 0.00% 0 💚
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/report-page-layout.tsx 4/4 (100.00%) 0.00% 0 💚
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/report-records-table.tsx 16/16 (100.00%) 0.00% 0 💚
projects/packages/premium-analytics/routes/reports/authors/page.tsx 18/20 (90.00%) -0.91% 0 💚

1 file is newly checked for coverage.

File Coverage
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-page/use-table-revalidating.ts 4/4 (100.00%) 💚

Full summary · PHP report · JS report

@Nikschavan
Nikschavan force-pushed the fix/pa-report-empty-state-all-reports branch from 5ff413d to 36d1687 Compare September 29, 2026 05:07
@Nikschavan
Nikschavan marked this pull request as ready for review September 29, 2026 05:10
@Nikschavan
Nikschavan requested a review from a team as a code owner September 29, 2026 05:10
Copilot AI balanced review requested due to automatic review settings September 29, 2026 05:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Nikschavan Nikschavan added [Status] Needs Review This PR is ready for review. and removed [Status] In Progress labels Sep 29, 2026

@chihsuan chihsuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch on the reports missing the empty state! @Nikschavan I left a few inline notes.

@Nikschavan
Nikschavan requested a review from chihsuan September 29, 2026 06:41
chihsuan
chihsuan previously approved these changes Sep 29, 2026

@chihsuan chihsuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates! @Nikschavan Tested well. 👍 I left a few minor inline notes, none of them blocking.

const dateFilters = useReportDateFilters( ROUTE_FROM );
const tableIsLoading = records.table.isLoading || records.table.isFetching;
const { getLabel } = REPORTS.locations;
const showMap = !! countryFilter || records.table.rows.length > 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the map stay mounted while the table loads? I noticed that opening Regions for the first time unmounts it, so a Hide map the user picked comes back expanded.

Suggested change
const showMap = !! countryFilter || records.table.rows.length > 0;
const showMap = !! countryFilter || records.table.rows.length > 0 || records.table.isLoading;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, the map now stays mounted while the rows load, so a hidden map stays hidden when Regions opens for the first time. I used your suggestion as it was and added a test for the first load.

{ records.isLoading ? (
<Spinner />
) : (
{ ( hasAllPostsFollowers || hasPostRows ) && (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not blocking, but would it be worth a shared helper for "is the report empty"? This page and Locations each re-derive what the table decides from its own view.filters, so they could drift.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept this PR to the empty state itself. A shared helper would need to know about the country filter that Locations keeps in the table view, so I would like to look at it in a follow-up, where it can cover both pages.

onChangePageItems?.( pageItems );
}, [ onChangePageItems, pageItems ] );

// A filter the API applies server-side can scope the rows to none, and the table carries the control that clears it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could we update the comments that still mention ReportRecordsTable's empty? These three spots now point at a prop this PR removes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I rewrote the comment in report-error-state.tsx. I also removed the page comments that still named the empty prop, in Emails and Tags and in the same text on Annual insights, Authors and Comments.

@dognose24 dognose24 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read the whole diff and ran the package's Jest suite, tsgo typecheck and ESLint on the changed files at 8ec9bdb; all pass. Moving the empty state into the two tables is the right fix, the eight table-level cases cover what the page tests used to, and I checked the loading claims against useStatsQuery: it folds isLoading into "pending, or placeholder data refetching", and the report queries set no placeholderData, so a period change does clear the rows and show the bare spinner, while a same-period revalidation only sets isFetching. No blockers; one changelog item and one consistency suggestion (inline).

Missing plugin changelog entries. This is a user-visible fix in packages/premium-analytics, so it needs entries in the plugins that surface it: plugins/jetpack (bugfix), plugins/premium-analytics (fixed) and plugins/wpcomsh (fixed, via jetpack-mu-wpcom), the same set the recent Premium Analytics PRs carried (#52714, #52747, #52690, #52870). The package entry's wording works for all three.

Checked and clean: the early return sits after every hook so view survives the empty state; view.filters correctly keeps the table for Locations' country filter; the four Comment followers combinations (rows/no rows × site-wide followers/none) each render sensibly; ReportEmptyState has no consumer outside this package, so dropping the export is safe; the empty prop has no remaining caller.

/** Whether rows for the current params are still loading. */
isLoading?: boolean;
/** Whether the rows on screen are revalidating; ignored while there are none, so an empty report keeps its empty state. */
isFetching?: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that isFetching is the way a table shows a revalidation, only six pages pass it (clicks, downloads, posts, search-terms, locations, videos). The other nine (authors, utm, referrers, comments, emails, earnings, tags, annual-insights, comment-followers) pass isLoading alone, so a same-period refetch shows no indicator there. Those nine never OR'd isFetching in on trunk either, so this is not a regression, but since this PR settles the interface it would be cheap to pass isFetching={ records.isFetching } on them too and have all fifteen reports behave the same. Fine as a follow-up if you'd rather keep this one to the empty state.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I passed isFetching from the other nine reports as well, so all fifteen now show the table's loading state while rows on screen refresh. I also added the plugin changelog entries for Jetpack, Premium Analytics and WordPress.com Site Helper.

Every report now shows the full-page empty state when it has no rows, including the reports without a period picker, which get copy that does not mention a time period. The records and drilldown tables render it themselves, with a spinner in place of the toolbar until the first rows arrive.
A settled empty state stayed on screen while the next period loaded, because the stats queries keep the previous empty rows as placeholder data. Locations counts a refetch as loading only when rows are on screen, so a same-period refetch of an empty report keeps the empty state.
…ount

The tables take isFetching apart from isLoading, so a same-period refetch keeps an empty report's empty state, while rows on screen still show the loading state. Comment followers shows the All Posts card whenever it has a count, even with no per-post rows.
…te API

The records table keeps its controls when a view filter is set, replacing the keepWhenEmpty prop. The unused empty prop and the public ReportEmptyState export go, the period context defaults to false outside a date-filtered layout, and the loading spinner uses Stack.
…rows load

Pass isFetching from the nine reports that did not, so a same-period refetch shows the table loading state on all fifteen. Keep the Locations map mounted during the first load so a hidden map stays hidden. Drop comments that named the removed empty prop, and add the plugin changelog entries.
…e case

The drilldown table had no test that failed when isFetching stopped reaching DataViews with rows on screen. The records-table case for a settled empty state going to loading failed for the same reason as the first-load case, so it is removed.
@Nikschavan
Nikschavan force-pushed the fix/pa-report-empty-state-all-reports branch from 8ec9bdb to 4c20d3c Compare September 30, 2026 07:11
@github-actions github-actions Bot added [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] Premium Analytics [Plugin] Wpcomsh labels Sep 30, 2026
@Nikschavan
Nikschavan requested a review from chihsuan September 30, 2026 09:01
dognose24
dognose24 previously approved these changes Sep 30, 2026

@dognose24 dognose24 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at 4c20d3c. Everything from the last round is in: the three plugin changelog entries, isFetching on all fifteen reports, the map staying mounted while rows load (with its test), and the comments that named the removed empty prop. Ran the package's Jest suite (including the two extra timezones) and the tsgo typecheck on this head: all pass.

Two non-blocking suggestions inline, both about what the tables do when they mount with isFetching already true. Fine as a follow-up.

data={ pageItems }
getItemId={ getItemId }
isLoading={ isLoading }
isLoading={ isLoading || isFetching }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] When the table mounts with isFetching already true, DataViews treats it as a first load: it renders no table, then a spinner after the delay, until the refetch finishes. That is what happens when someone comes back to a report whose cached rows have gone stale (stale after 5 minutes, kept for 10), so the cached rows stay hidden instead of showing while they refresh.

I checked both paths with a throwaway test. Mounting with rows and isFetching renders no rows. Rows already on screen that start refetching stay put, with the table aria-busy, and dim after the delay.

Six reports already did this on trunk by OR-ing isFetching into isLoading; passing it from the other nine, which I suggested last round, extends it to them. One way out is to leave isFetching out of the first render, so DataViews sees the rows before it sees the loading flag. ReportDrilldownTable has the same line. Happy for this to be a follow-up.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, the tables now pass isFetching to DataViews only after their first render. A report that opens with stale cached rows now shows them right away, and the rows get the busy state once the refetch starts. ReportDrilldownTable does the same through a shared hook.

Comment on lines +314 to +319
it( 'shows the table loading state while rows on screen revalidate', () => {
mountRows( rows, { isFetching: true } );

expect( screen.getByRole( 'searchbox' ) ).toBeInTheDocument();
expect( screen.queryByText( 'Maharashtra' ) ).not.toBeInTheDocument();
} );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] This mounts the table with isFetching already set, so the rows were never on screen: it pins the mount case from my other comment rather than the revalidation the name describes. For rows that are on screen, a render followed by a rerender with isFetching shows them still there with the table aria-busy, which is probably the behaviour worth pinning. The matching case in report-drilldown-table.test.tsx is written the same way.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I split the test into two cases in both table tests. One case mounts with isFetching and checks that the rows show. The other case renders the rows first, then rerenders with isFetching, and checks that the rows stay and that the table is aria-busy.

chihsuan
chihsuan previously approved these changes Oct 1, 2026

@chihsuan chihsuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM 🚢

@Nikschavan
Nikschavan dismissed stale reviews from chihsuan and dognose24 via 7ea3be6 October 1, 2026 06:51
@Nikschavan
Nikschavan merged commit 21c5995 into trunk Oct 1, 2026
84 checks passed
@Nikschavan
Nikschavan deleted the fix/pa-report-empty-state-all-reports branch October 1, 2026 07:17
@github-actions github-actions Bot added [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. and removed [Status] Needs Review This PR is ready for review. labels Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Docs [Package] Premium Analytics [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] Premium Analytics [Plugin] Wpcomsh [Status] UI Changes Add this to PRs that change the UI so documentation can be updated.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants