Skip to content

Premium Analytics: migrate widgets to WidgetState for loading/error/empty states#50383

Open
chihsuan wants to merge 22 commits into
trunkfrom
wooa7s-1675-migrate-existing-widgets-to-widgetstate-for
Open

Premium Analytics: migrate widgets to WidgetState for loading/error/empty states#50383
chihsuan wants to merge 22 commits into
trunkfrom
wooa7s-1675-migrate-existing-widgets-to-widgetstate-for

Conversation

@chihsuan

@chihsuan chihsuan commented Jul 10, 2026

Copy link
Copy Markdown
Member

Proposed changes

Why: Widgets still hand-roll loading/error/empty states instead of using the shared <WidgetState> (#50252). Worst case: the Woo/Bookings widgets rendered nothing on a failed fetch, since this dashboard never passes setError.

How: Each widget maps its data hook to <WidgetState> and passes error/empty descriptors. isError only fires when there's nothing to show (placeholder data survives transient refetch failures); Retry is wired to refetch, dropped for non-retriable cases (plan-upgrade, file-downloads). Woo/Bookings widgets migrate via their shared toolkit components.

What:

  • 23 pre-existing + 28 Woo/Bookings widgets migrated; ready-state visuals unchanged.
  • Bug fix: sanitizeReportCustomers uses safeParseFloat/Int, so missing summary fields yield the empty state instead of NaN bars.
  • ReportMetricWidget treats a report with no time-series rows as empty (not a $0.00 ready state).
  • Empty-state copy: date-range-scoped Stats widgets use the terse "No X in this period."; range-ignoring widgets keep upstream's educational copy.
  • Forced Loading/Error/Empty stories (50 files) are split into Premium Analytics: add forced Loading/Error/Empty Storybook stories for migrated widgets #50391.

How to review

Same pattern repeated 51×. The decisions live in 3 files: report-metric.tsx (empty/error semantics), data/.../customers/index.ts (the NaN fix), widget-state.tsx. sales-by-coupon-widget.tsx is the canonical widget — the rest differ only in hook/icon/copy. Deviations: visitors-by-location (region toggle kept as sibling), devices/top-platforms/file-downloads (no Retry).

Related product discussion/links

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

No.

Testing instructions

jetpack build --deps packages/premium-analytics, then pnpm run storybook in the package.

  • This PR only carries the emails/top-posts forced-state stories. To exercise the migration across all widgets, it's easier to test the stacked Premium Analytics: add forced Loading/Error/Empty Storybook stories for migrated widgets #50391 directly — it adds the Loading/Error/Empty stories for every widget.
  • On a story's Error state, confirm widget-specific copy shows and Retry re-runs the query.
  • Confirm Default/WithComparison still render as before.
  • Rebuild if states look stale (Storybook serves build-module, not source).

@github-actions

github-actions Bot commented Jul 10, 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!

@github-actions github-actions Bot added the [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. label Jul 10, 2026
@chihsuan chihsuan self-assigned this Jul 10, 2026
@chihsuan chihsuan force-pushed the wooa7s-1675-migrate-existing-widgets-to-widgetstate-for branch from e5462cd to 37a3f9a Compare July 10, 2026 03:19
@jp-launch-control

jp-launch-control Bot commented Jul 10, 2026

Copy link
Copy Markdown

Code Coverage Summary

This PR did not change code coverage!

That could be good or bad, depending on the situation. Everything covered before, and still is? Great! Nothing was covered before? Not so great. 🤷

Full summary · PHP report

@chihsuan chihsuan force-pushed the wooa7s-1675-migrate-existing-widgets-to-widgetstate-for branch from 37a3f9a to 39bfd2d Compare July 10, 2026 07:58
@chihsuan chihsuan force-pushed the wooa7s-1675-migrate-existing-widgets-to-widgetstate-for branch from 39bfd2d to 154e20b Compare July 10, 2026 08:22
@chihsuan chihsuan force-pushed the wooa7s-1675-migrate-existing-widgets-to-widgetstate-for branch from 154e20b to e4c7536 Compare July 10, 2026 08:42
net_sales: parseFloat( item.net_sales ),
orders_count: parseInt( item.orders_count, 10 ),
net_sales: safeParseFloat( item.net_sales ),
orders_count: safeParseInt( item.orders_count ),

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.

This is the only behavioral fix outside the state migration: missing summary fields now fall back to 0 → empty state, instead of charting NaN bars.

<div className={ styles.container }>
{ /* The region toggle drives the query, so it stays available in
every state (a sibling of WidgetState, per the widget contract). */ }
<div className={ styles.toggleControl }>

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.

Deviates from the canonical shape on purpose: the region toggle drives the query, so it lives as a WidgetState sibling and stays available in the error/empty states.

We can migrate this widget to move the control to widget header in a follow-up to avoid this.

chihsuan added 7 commits July 13, 2026 17:05
# Conflicts:
#	projects/packages/premium-analytics/widgets/all-time-stats/render.tsx
#	projects/packages/premium-analytics/widgets/annual-highlights/render.tsx
#	projects/packages/premium-analytics/widgets/authors/render.tsx
#	projects/packages/premium-analytics/widgets/clicks/render.tsx
#	projects/packages/premium-analytics/widgets/devices/render.tsx
#	projects/packages/premium-analytics/widgets/devices/use-device-views.ts
#	projects/packages/premium-analytics/widgets/file-downloads/render.tsx
#	projects/packages/premium-analytics/widgets/locations/render.tsx
#	projects/packages/premium-analytics/widgets/locations/use-location-views.ts
#	projects/packages/premium-analytics/widgets/subscriber-highlights/render.tsx
#	projects/packages/premium-analytics/widgets/top-platforms/render.tsx
#	projects/packages/premium-analytics/widgets/top-platforms/use-platform-views.ts
#	projects/packages/premium-analytics/widgets/top-posts/render.tsx
#	projects/packages/premium-analytics/widgets/utm-insights/render.tsx
#	projects/packages/premium-analytics/widgets/utm-insights/use-utm-insights.ts
#	projects/packages/premium-analytics/widgets/videos/render.tsx
# Conflicts:
#	projects/packages/premium-analytics/widgets/clicks/style.module.css
#	projects/packages/premium-analytics/widgets/referrers/style.module.css
#	projects/packages/premium-analytics/widgets/subscribers-list/render.tsx

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.

Pull request overview

This PR migrates Premium Analytics widgets (both Stats-ported and widgets-toolkit-based) to render consistent loading/error/empty UX via the shared <WidgetState> component, replacing per-widget hand-rolled state branching and fixing a couple of empty/error edge cases.

Changes:

  • Migrate many widgets to wrap their ready UI in <WidgetState> and standardize retry/error/empty semantics (including “only show error when there’s nothing to show” with placeholder data).
  • Fix report sanitization to avoid NaN visuals by using safeParseFloat/safeParseInt for missing summary fields.
  • Update widgets-toolkit primitives (WidgetState, ChartEmptyState, ReportMetricWidget), Storybook forced-state stories (Top posts, Emails), and some widget icons/copy.

Reviewed changes

Copilot reviewed 82 out of 82 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
projects/packages/premium-analytics/widgets/videos/render.tsx Move Videos widget loading/error/empty handling into <WidgetState>.
projects/packages/premium-analytics/widgets/videopress/render.tsx Update empty-state copy for VideoPress plays to period-scoped wording.
projects/packages/premium-analytics/widgets/utm-insights/use-utm-insights.ts Expose refetch, gate isError to “no rows” to preserve stale data on transient failures.
projects/packages/premium-analytics/widgets/utm-insights/style.module.css Replace placeholder styling with a state-area wrapper for <WidgetState>.
projects/packages/premium-analytics/widgets/utm-insights/render.tsx Wrap UTM insights leaderboard in <WidgetState> with retry + empty descriptors.
projects/packages/premium-analytics/widgets/utm-insights/package.json Add icons package dependency for empty-state glyphs.
projects/packages/premium-analytics/widgets/traffic-chart/use-traffic-chart.ts Add isLoading, refine isError to only surface when there are no rows to show.
projects/packages/premium-analytics/widgets/traffic-chart/render.tsx Convert TrafficChart to <WidgetState> (custom loading skeleton + busy overlay semantics).
projects/packages/premium-analytics/widgets/traffic-chart/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/top-posts/widget.ts Swap widget icon to a more topical glyph.
projects/packages/premium-analytics/widgets/top-posts/stories/top-posts.stories.tsx Replace prop-driven forced states with mock-forced <WidgetState> stories.
projects/packages/premium-analytics/widgets/top-posts/render.tsx Migrate TopPosts report to <WidgetState> and simplify presentational leaderboard to ready-only.
projects/packages/premium-analytics/widgets/top-platforms/use-platform-views.ts Add isFetching/refetch, gate isError to empty data, keep plan-upgrade reason.
projects/packages/premium-analytics/widgets/top-platforms/style.module.css Add state wrapper for <WidgetState> layout.
projects/packages/premium-analytics/widgets/top-platforms/render.tsx Migrate TopPlatforms to <WidgetState>, drop Retry for plan-upgrade errors.
projects/packages/premium-analytics/widgets/top-platforms/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/subscribers-list/render.tsx Convert Subscribers list to <WidgetState> and simplify roster to ready-only.
projects/packages/premium-analytics/widgets/subscribers-list/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/subscribers-chart/use-subscribers-chart.ts Gate isError to empty data so transient failures don’t blank populated charts.
projects/packages/premium-analytics/widgets/subscribers-chart/render.tsx Convert Subscribers chart to <WidgetState> with custom loading skeleton.
projects/packages/premium-analytics/widgets/subscribers-chart/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/subscriber-highlights/render.tsx Convert subscriber highlights tiles to <WidgetState> + treat all-undefined counts as empty.
projects/packages/premium-analytics/widgets/subscriber-highlights/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/subscriber-highlights/tests/subscriber-highlights.test.tsx Update tests for WidgetState loading/error/empty rendering (incl. Retry presence).
projects/packages/premium-analytics/widgets/store-performance/styles.module.scss Remove old empty-state styling (handled by <WidgetState> now).
projects/packages/premium-analytics/widgets/store-performance/render.tsx Convert store performance tabs to <WidgetState> (retry refetches all active metrics).
projects/packages/premium-analytics/widgets/referrers/render.tsx Convert Referrers leaderboard to <WidgetState> with new empty/error icon/copy.
projects/packages/premium-analytics/widgets/posting-activity/style.module.css Remove placeholder styles now that empty/error are handled by <WidgetState>.
projects/packages/premium-analytics/widgets/posting-activity/render.tsx Convert Posting activity heatmap to <WidgetState> with calendar empty icon.
projects/packages/premium-analytics/widgets/posting-activity/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/payment-status/render.tsx Remove unused setError plumbing; widget body handles state via <WidgetState>.
projects/packages/premium-analytics/widgets/payment-status/payment-status-widget.tsx Convert payment donut widget to <WidgetState>, remove per-chart empty icon wiring.
projects/packages/premium-analytics/widgets/most-popular-time/style.module.css Add state wrapper for <WidgetState> layout.
projects/packages/premium-analytics/widgets/most-popular-time/render.tsx Convert “most popular time” insights to <WidgetState> with retry + empty handling.
projects/packages/premium-analytics/widgets/most-popular-day/style.module.css Add <WidgetState> layout wrapper and centering helper for ready state.
projects/packages/premium-analytics/widgets/most-popular-day/render.tsx Convert “most popular day” to <WidgetState> and simplify highlight to ready-only.
projects/packages/premium-analytics/widgets/most-popular-day/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/locations/use-location-views.ts Add refetch, gate isError to empty data to preserve stale rows.
projects/packages/premium-analytics/widgets/locations/style.module.css Rework layout to keep body chrome outside <WidgetState> while anchoring state area.
projects/packages/premium-analytics/widgets/locations/render.tsx Convert Locations (leaderboard + geo chart) to <WidgetState> while keeping header/backlink sibling.
projects/packages/premium-analytics/widgets/locations/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/latest-post/use-latest-post.ts Add combined isFetching + refetch and only surface error when content is missing.
projects/packages/premium-analytics/widgets/latest-post/style.module.css Remove placeholder styling now that state is handled by <WidgetState>.
projects/packages/premium-analytics/widgets/latest-post/render.tsx Convert Latest post widget to <WidgetState> and make presentational card ready-only.
projects/packages/premium-analytics/widgets/file-downloads/style.module.css Remove placeholder styling now that state is handled by <WidgetState>.
projects/packages/premium-analytics/widgets/file-downloads/render.tsx Convert File downloads to <WidgetState>, omit Retry for known-unavailable message cases.
projects/packages/premium-analytics/widgets/emails/style.module.css Remove placeholder styling now that state is handled by <WidgetState>.
projects/packages/premium-analytics/widgets/emails/stories/emails-widget.stories.tsx Convert forced states to mock-driven <WidgetState> stories via setReportMockState.
projects/packages/premium-analytics/widgets/emails/render.tsx Convert Emails leaderboard widget to <WidgetState> and simplify presentational leaderboard to ready-only.
projects/packages/premium-analytics/widgets/devices/use-device-views.ts Add isFetching/refetch, gate isError to empty data, preserve plan-upgrade reason.
projects/packages/premium-analytics/widgets/devices/style.module.css Add wrapper to keep donut/legend centered inside <WidgetState> ready area.
projects/packages/premium-analytics/widgets/devices/render.tsx Convert Devices semicircle widget to <WidgetState>, omit Retry for plan-upgrade errors.
projects/packages/premium-analytics/widgets/devices/package.json Add icons package dependency.
projects/packages/premium-analytics/widgets/clicks/widget.ts Swap widget icon to a more topical glyph.
projects/packages/premium-analytics/widgets/clicks/render.tsx Convert Clicks leaderboard to <WidgetState> (retry + empty icon/copy).
projects/packages/premium-analytics/widgets/all-time-stats/style.module.css Remove old dedicated state styling (now handled by <WidgetState>).
projects/packages/premium-analytics/widgets/all-time-stats/render.tsx Convert All-time stats list to <WidgetState> with retry + empty descriptors.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/visitors-by-location/visitors-by-location-widget.tsx Convert toolkit Visitors-by-location widget to <WidgetState> while keeping region toggle sibling.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/total-returns/total-returns-widget.tsx Convert Total returns to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/sessions-by-device/sessions-by-device-widget.tsx Convert Sessions-by-device to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/sales-by-utm/sales-by-utm-widget.tsx Convert Sales-by-UTM to <WidgetState> with shared empty copy/icon.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/sales-by-device/sales-by-device-widget.tsx Convert Sales-by-device to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/sales-by-coupon/sales-by-coupon-widget.tsx Convert Sales-by-coupon to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/revenue-by-customer-type/revenue-by-customer-type-widget.tsx Convert Revenue-by-customer-type to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/product-leaderboard/top-performing-product-leaderboard-widget.tsx Convert top product leaderboard to <WidgetState> and add configurable empty/error copy props.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/product-leaderboard/top-performing-bookings-widget.tsx Provide bookings-specific empty/error copy via the new product leaderboard props.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/orders-fulfillment/orders-fulfillment-widget.tsx Convert orders fulfillment donut to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/new-vs-returning-customer/new-vs-returning-customer-widget.tsx Convert new-vs-returning customer donut to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/coupon-use/coupon-use-widget.tsx Convert coupon use donut to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/conversion-rate/conversion-rate-widget.tsx Convert conversion funnel widget to <WidgetState> and unify empty/error behavior.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/bookings-by-attendance/bookings-by-attendance-widget.tsx Convert bookings attendance donut to <WidgetState> and use shared empty detection helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/widget-state/widget-state.tsx Make empty description optional and rely on ChartEmptyState default copy.
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/report-metric/report-metric.tsx Convert metric-over-time widget primitive to <WidgetState> and treat “no series rows” as empty.
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/chart-empty-state/chart-empty-state.tsx Update default empty copy to “No data in this period.”.
projects/packages/premium-analytics/packages/icons/src/reports/index.tsx Adjust reports icon fill to use WPDS neutral stroke variable.
projects/packages/premium-analytics/packages/data/src/processing/customers/index.ts Use safe numeric parsing to avoid NaN when summary fields are missing.
projects/packages/premium-analytics/packages/data/src/processing/customers-by-date/index.ts Use safe numeric parsing to avoid NaN for empty summaries/items.
projects/packages/premium-analytics/packages/data/src/processing/coupons-by-date/index.ts Use safe numeric parsing to avoid NaN for empty summaries/items.
projects/packages/premium-analytics/changelog/update-widgets-widgetstate-migration Changelog entry describing the WidgetState migration and related copy/icon tweaks.

@chihsuan chihsuan marked this pull request as ready for review July 13, 2026 10:21
@chihsuan chihsuan requested review from a team as code owners July 13, 2026 10:21
@chihsuan chihsuan added [Status] Needs Review This PR is ready for review. and removed [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. [Status] In Progress labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants