Premium Analytics: migrate widgets to WidgetState for loading/error/empty states#50383
Premium Analytics: migrate widgets to WidgetState for loading/error/empty states#50383chihsuan wants to merge 22 commits into
Conversation
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
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:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
e5462cd to
37a3f9a
Compare
Code Coverage SummaryThis 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. 🤷 |
37a3f9a to
39bfd2d
Compare
39bfd2d to
154e20b
Compare
154e20b to
e4c7536
Compare
Revert the incidental Stack->div swap so the ready-state layout stays on the inner Stack (matching most-popular-time). Drops the hand-written .body and .yearNav classes; only a minimal .content wrapper remains for WidgetState's host box.
| 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 ), |
There was a problem hiding this comment.
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 }> |
There was a problem hiding this comment.
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.
# 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
There was a problem hiding this comment.
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
NaNvisuals by usingsafeParseFloat/safeParseIntfor 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. |
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 passessetError.How: Each widget maps its data hook to
<WidgetState>and passes error/empty descriptors.isErroronly fires when there's nothing to show (placeholder data survives transient refetch failures); Retry is wired torefetch, dropped for non-retriable cases (plan-upgrade, file-downloads). Woo/Bookings widgets migrate via their shared toolkit components.What:
sanitizeReportCustomersusessafeParseFloat/Int, so missing summary fields yield the empty state instead ofNaNbars.ReportMetricWidgettreats a report with no time-series rows as empty (not a$0.00ready state).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.tsxis 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, thenpnpm run storybookin the package.emails/top-postsforced-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.Errorstate, confirm widget-specific copy shows and Retry re-runs the query.Default/WithComparisonstill render as before.build-module, not source).