Skip to content

Premium Analytics: port the Average items per order widget#49505

Draft
nerrad wants to merge 2 commits into
update/pa-introduce-customize-dashboardfrom
add/wooa7s-1460-port-woo-widget-average-items-per-order
Draft

Premium Analytics: port the Average items per order widget#49505
nerrad wants to merge 2 commits into
update/pa-introduce-customize-dashboardfrom
add/wooa7s-1460-port-woo-widget-average-items-per-order

Conversation

@nerrad

@nerrad nerrad commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #

Proposed changes

Ports the Average items per order widget from next-woocommerce-analytics (woocommerce/woocommerce-analytics@develop) into the Premium Analytics customizable dashboard — the first real analytics widget on the dashboard introduced by #49502, and the first consumer of the internal package ports.

The widget is a thin composition over the merged internal packages (~200 lines total):

Deviations from upstream (documented inline)

  • The host widget contract (WidgetRenderProps) has no setError channel, so render.tsx holds the toolkit's setError state and renders an inline WidgetErrorNotice (message + retry action from the toolkit's error config). Clearing the error remounts the widget, which refetches.
  • Widget id is jpa/average-items-per-order (host namespace) rather than the upstream woocommerce-analytics/average-items-per-order.

Known limitations / follow-ups

Related product discussion/links

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

No.

Testing instructions

Requires pnpm ^11.5 and Node ^24.15.

  • Build: pnpm --filter @automattic/jetpack-premium-analytics build — both hello-world and average-items-per-order widgets should build, and build/widgets.php registers them.
  • Typecheck: pnpm --filter @automattic/jetpack-premium-analytics typecheck — only the pre-existing useSelect/preferences errors in widget-dashboard/widget-primitives remain (present on the base branch; verified with a clean install).
  • On a test site (WooCommerce + the wp.org woocommerce-analytics plugin active and synced, e.g. via jetpack rsync premium-analytics <site>:...):
    1. Open the Premium Analytics admin page → Customize → Add widget → insert "Average items per order" (bar-chart icon).
    2. DevTools → Network: GET /wp-json/jetpack/v4/widget-modules lists jpa/average-items-per-order; widgets/average-items-per-order/render.min.js loads as a separate chunk on mount; two proxy/reports/orders requests fire (primary + comparison).
    3. With order data: metric value + comparison delta + sparkline render; hover shows the styled tooltip; loading overlay appears during refetch.
    4. Open the widget's settings (gear icon): the Range panel renders the full date-range editor (presets, calendar, comparison dropdown); changing the range refetches the report.
    5. Without the analytics plugin/data: the widget shows the inline "We couldn't load this data" notice and the Retry button recovers.

@nerrad nerrad self-assigned this Jun 10, 2026
@github-actions

github-actions Bot commented Jun 10, 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 add/wooa7s-1460-port-woo-widget-average-items-per-order branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack add/wooa7s-1460-port-woo-widget-average-items-per-order
bin/jetpack-downloader test jetpack-mu-wpcom-plugin add/wooa7s-1460-port-woo-widget-average-items-per-order

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

@github-actions

github-actions Bot commented Jun 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!


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.

@jp-launch-control

jp-launch-control Bot commented Jun 10, 2026

Copy link
Copy Markdown

Code Coverage Summary

No summary data is available for parent commit 909996e, so cannot calculate coverage changes. 😴

If that commit is a feature branch rather than a trunk commit, this is expected. Otherwise, this should be updated once coverage for 909996e is available.

Full summary · PHP report · JS report

@nerrad nerrad force-pushed the add/wooa7s-1460-port-woo-widget-average-items-per-order branch from d922903 to 74722f2 Compare June 11, 2026 21:40
@nerrad nerrad force-pushed the update/pa-introduce-customize-dashboard branch from 91aa9fb to 909996e Compare June 11, 2026 22:51
@nerrad nerrad force-pushed the add/wooa7s-1460-port-woo-widget-average-items-per-order branch from 74722f2 to 4538b65 Compare June 11, 2026 22:53
@nerrad

nerrad commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up: the reportParams range editor, and where the toolkit pieces should land

Context for reviewers on the one fidelity gap in this PR (no user-editable date range), plus proposed next steps now that widgets-toolkit (#49422) has landed and we can plan its dissolution — it was always a transitional construct for the port.

The problem

The upstream widget declares an editable range attribute with a custom dataviews Edit component:

attributes: [ { id: 'reportParams', label: 'Range', Edit: ReportParamsField } ]

Widget attributes flow into the dashboard's settings drawer, which renders them directly through DataForm with no per-widget form wiring (widget-settings.tsx#L75-L86):

const fields = useMemo< Field< WidgetAttributes >[] >(
	() => ( widgetType?.attributes ?? [] ) as Field< WidgetAttributes >[],
	[ widgetType?.attributes ]
);

…where widgetType.attributes comes straight from the widget's metadata module per the contract (widget-primitives types.ts#L108):

attributes?: Field< Item >[];

So the Edit component must ship inside the widget's metadata module (widget.ts), which useWidgetTypes() imports eagerly for every registered widget at dashboard boot.

That's where it breaks. In @wordpress/build's buildWidget(), the two builds get different plugin sets — render.* gets style handling, widget.* doesn't:

// render.min.js build:
plugins: [
	...createStyleBundlingPlugins( widgetDir ),   // CSS modules → style runtime
	wordpressExternalsPlugin( 'render.min', 'esm', [], true ),
],
// widget.min.js (metadata) build:
plugins: [
	styleRuntimeAliasPlugin(),                    // no sass/css-modules support
	wordpressExternalsPlugin( 'widget.min', 'esm', [], true ),
],

And ReportParamsField's import graph carries .module.scss (date-report-params-field.tsx#L16):

import { DateFiltersPanel } from '@jetpack-premium-analytics/ui';

✘ [ERROR] No loader is configured for ".scss" files the moment widget.ts imports the field. So this PR ships no editable attributes; new instances bake the default last-30-days + comparison range via example.attributes.

Nobody else hits this: premium-analytics is the only consumer of wp-build's widget pipeline, and the ecosystem convention elsewhere avoids the pattern entirely — block.json metadata is pure JSON with edit components living in editor bundles, and wpScript packages keep styles out of the JS graph (separate build-style/ compilation). Our own hello-world widget already follows the safe form, a string-typed control with no component import (hello-world/widget.ts#L14-L20):

attributes: [
	{
		id: 'message',
		label: 'Message',
		type: 'text',
	},
],

Notably, the dashboard host already does exactly what ReportParamsField wants to do — a custom styled DataForm control — but defined in the host bundle, which builds with full style support (layout-model-edit-field/index.tsx#L60):

}: DataFormControlProps< WidgetGridSettings > ): React.ReactNode {

The pattern works; the problem is purely where the component code lives.

Proposed next steps (doubles as the toolkit dissolution plan)

1. Host-level providers; dissolve WidgetRoot. Today every widget instance mounts its own providers (widget-root.tsx#L120-L128):

<AnalyticsQueryClientProvider>
	<GlobalChartsProvider theme={ chartTheme }>
		<WidgetRootContext.Provider value={ contextValue }>

…and the QueryClient is a module-scope singleton per bundle (query-client-provider.tsx#L109), so each widget gets its own cache. Since react/react-dom are externalized to the WP-shared instance in widget bundles (from the generated render.min.asset.php: 'dependencies' => array('react', 'react-dom', …)), context crosses bundle boundaries — the dashboard route can mount the providers once around WidgetDashboard, widgets shrink to a useResolvedReportParams( attributes ) hook + chart composition, and identical report requests dedupe across widgets via the shared cache. Small PR, pays off before widget #2.

2. Declarative metadata; resolve Edit controls by name. Keep widget.ts to strings/icon (block.json philosophy); the attribute declares a control reference (e.g. edit: 'jpa/report-params') and the host registers implementations via a small registry in the widget-primitives contract — the field implementation moves to a bundle built by the route pipeline, which has style support (per the host precedent above). Kills this build issue permanently and the eager-load weight of bundling a date picker into N metadata modules. Needs contract discussion since widget-primitives is headed upstream.

3. Upstream chart compositions to @automattic/charts. ComparativeLineChart, ChartTooltip, empty-state helpers, metric value/delta. Charts' compositions directory is literally waiting (charts/src/compositions/index.ts):

// Compositions directory - prepared for future use
export {};

One decoupling needed first — these components import the analytics-local formatters directly (comparative-line-chart.tsx#L5):

import { formatDate, formatMetricValue } from '@jetpack-premium-analytics/formatters';

Make formatting injectable via props (formatValue/formatDate) so the analytics flavoring stays at the call site.

4. Regardless of the above: file the render/metadata style-plugin asymmetry upstream against @wordpress/build — the next widget-pipeline consumer will trip on it for something lighter than a date picker.

End state: data hooks stay in the data package, charts pieces live in charts, field controls live host-side, and widgets-toolkit deletes itself.

*left by Biff (AI agent) on behalf of Darren

nerrad added 2 commits June 12, 2026 17:17
Port the Average items per order widget from next-woocommerce-analytics
(woocommerce/woocommerce-analytics@develop) into the Premium Analytics
customizable dashboard (WOOA7S-1460).

The widget is a thin composition over the merged internal packages:
report data via @jetpack-premium-analytics/data (useReportOrders), the
metric/sparkline rendering via @jetpack-premium-analytics/widgets-toolkit
(WidgetRoot + OrderMetricWidget), with @automattic/charts underneath.

Wires the internal packages for the wp-build externals resolver via
link: deps on the parent manifest — the mechanism documented in #49189;
this widget is the first built code to import them.

Deviations from upstream, documented inline:
- The host widget contract has no setError channel, so render.tsx holds
  the toolkit's setError state and renders an inline WidgetErrorNotice.
- No reportParams range editor yet: wp-build's widget metadata build has
  no style plugins and the toolkit's ReportParamsField import graph
  carries .module.scss. New instances use the default last-30-days range
  with comparison enabled.
- No URL-search reportParams fallback (handled by toolkit WidgetRoot's
  try/catch around useSearch outside a matched route).
With #49575 in trunk, the reportParams attribute gains its real editor:
ReportParamsField from @jetpack-premium-analytics/fields, externalized
as a script-module dependency so the styled date-picker graph never
enters the metadata module's style-less build.
@nerrad nerrad force-pushed the add/wooa7s-1460-port-woo-widget-average-items-per-order branch from 4538b65 to bbf93fc Compare June 12, 2026 08:18

@nerrad nerrad left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Besides the inline comments, the package-lock.json diff seems to be larger than it should be?

@@ -0,0 +1 @@
export { WidgetErrorNotice } from './widget-error-notice';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Are there no existing widget error handling components at in either the widget toolkit or other packages? Other widgets will need something too? Also, check and see what exists for notices in the WordPress Design System.

Comment on lines +32 to +38
attributes: [
{
id: 'reportParams',
label: __( 'Range', 'jetpack-premium-analytics' ),
Edit: ReportParamsField,
},
],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Investigate a way to hide this in the analytics dashboard but make available when the widget is inserted in non-analytics dashboards. Otherwise, if there is no straightforward implementation that can apply globally to all analytics type widgets, let's just remove this for now because report params will be defined at the global level for these widgets.

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.

1 participant