Skip to content

Commit 785398b

Browse files
louwie17matticbot
authored andcommitted
Premium Analytics: add a Popular post widget and share the post highlight card (#50931)
* Premium Analytics: add a Popular post widget and share the post highlight card Adds the Insights "Most popular post" module as `jpa/popular-post`: the site's most-viewed post for the dashboard's selected date range, with its publish date and the same three metric tiles as Latest post. Unlike Latest post it is period-scoped, reading `reportParams` from the widget root context, so changing the date range changes both the winning post and its view count. Views come from `stats/top-posts` for the period; likes and comments come from `stats/post/{id}`, which takes no date range, so those two tiles carry an all-time aggregation note instead of implying a period number. Latest post's presentational card moves to `PostHighlightCard` in widgets-toolkit so both widgets render one card shape, and the card now adapts to the dashboard cell with a single container-query breakpoint: width-2 and wider cells get the two-column layout from the design (text left, metric row anchored to the bottom, rounded near-square featured image right), while width-1 cells drop the image and wrap the metric row. The publish line copy now matches the prototype ("Post published on <date>"). WOOA7S-1787 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: make the post highlight card height-aware The card's size ladder branched on width only, so a cell that was wide enough for the two-column layout but short could not fit its content. The metric row was pinned to the end of an over-tall column and `overflow: hidden` on the root silently clipped it: at a 736x86 body the row's values sat 38px below the clip boundary, leaving the labels cut mid-line, and at 331x86 the labels rendered cleanly with no values under them at all. Rebuilt against the design prototype, which resolves `wide = inline-size >= 520px` and `tall = block-size >= 300px` and ships `--wide-tall` / `--wide-short` / `--narrow-short` treatments. The card now declares its own named size container -- the shared `widget` container is `container-type: inline-size` and cannot answer block-axis queries -- and queries both axes, following the precedent in `widget-state.module.scss`. The clipping is fixed structurally rather than by moving a breakpoint: `.metrics` is `flex-shrink: 0` and `.header` is the flexible, clipping box, so the headline gives up space while the metric row keeps its full size. A label can no longer be shown without its value at any geometry. Below the height where the publish line also fits, it is dropped outright instead of being clipped to a sliver. Both widgets gain ShortCell and ShortNarrowCell dashboard stories so a height regression is visible in review. WOOA7S-1787 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: address Popular post review feedback Read every displayed metric from `stats/post`, so the three tiles share one all-time window instead of pairing range-scoped views with lifetime likes and comments. The date range's only job is now picking the winner, which also matches the sibling Latest post widget that shares this card. Consume metrics only from a response that identifies the current post. The Stats query carries the previous key's payload over through `placeholderData` while the content query deliberately does not, so a winner change could render the new post's title beside the previous post's engagement. Drop the comparison fields from the top-posts request: they triggered a second report fetch for a window this widget never renders. Document the `max` trade-off that can hide a qualifying post on a page-heavy site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: address Chi's post widget review feedback Show an unread metric as a dash rather than as zero. The Stats post endpoint can fail on its own — it 403s on a private site — and zeroing there rendered a post with 12 likes as "Likes 0", which is a wrong number rather than a missing one. Applies to Latest post too, which shares the card and had the same gap. Stop hiding a report failure behind stale rows. `placeholderData` only applies while a query is pending, so rows that survive an error are the last successful data — a failed background refetch. Gating on `! topRow` / `! latestPost` hid exactly that: stale numbers, no error, no Retry. Correct the story docblocks that still described a 432px breakpoint; the card resolves wide at 520px and tall at 300px. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: trim the post widget doc blocks Compress the comments added over the review rounds: keep the non-obvious rationale, drop the restated-code prose and the multi-paragraph explanations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: import post highlight card UI from externals Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: fix the post widget Storybook states The stats/post mock returned a fixture with a hardcoded post ID for every requested post, so the card could never attribute the metrics to the current winner and sat in its skeleton forever. The mock now echoes the ID it was asked for, which is what the real endpoint does. Move the Empty story off `last-365-days`: it resolves to the same dates as ErrorRetryable's `last-12-months` most years, so the two shared a query key and Empty's cached result won. Normalize `comment_count` in `sanitizeStatsPostResponse` instead of in each consumer, removing three copies of the same coercion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: link the Popular post title to the post detail page Matches Latest post, which trunk moved onto the internal detail route. Both widgets now carry the dashboard's window into the detail page, and the card still falls back to the published post when there is no post ID. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: give the Popular post stories a router The card's title now renders a router-backed Link when it has a post ID, so the close-up stories need the same `withStoryRouter` decorator the sibling post widgets already use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: declare the post widgets' real dependencies Popular post imports `pickReportDateParams` and `useMemo` but declared neither, building only because hoisting happened to provide them. Latest post gained the same `useMemo` import, and no longer uses `date-fns` or `externals` since the shared card took over its rendering. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: sync the Popular post docs with its behaviour The widget reads every metric from `stats/post`, so the date range picks the winner rather than scoping the view count. The widget definition and the Storybook descriptions still described the earlier period-scoped views. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 * Premium Analytics: move the post highlight card onto the theme 1.0 tokens Trunk's `@wordpress/*` update renamed the font-weight tokens (medium -> emphasis, regular -> default). The shared card still used the old names, which resolve to nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAX3YkQxDLLXhKXjft6qU2 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Committed via a GitHub action: https://github.com/Automattic/jetpack/actions/runs/31017284317 Upstream-Ref: Automattic/jetpack@591670e
1 parent 3cb07a1 commit 785398b

20 files changed

Lines changed: 47 additions & 13 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This is an alpha version! The changes listed here are not final.
1212
### Added
1313
- Add translated DateFiltersPanel stories (Russian, Dutch, German) that render the date bar at several container widths, so the layout is reviewed against real language packs rather than English only.
1414
- Insights: add an all-time and yearly date selection, configurable per dashboard section.
15+
- Insights: add a Popular post widget, and adapt the single-post highlight card to both the width and the height of its dashboard cell.
1516

1617
### Changed
1718
- Date range presets now shorten their labels before collapsing into a select, and the switch is measured from the active language's own labels instead of a fixed width. The panel also measures itself, so callers no longer pass a container element.

build/i18n-manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"build/widgets/orders-over-time/render.js",
5757
"build/widgets/payment-status/render.js",
5858
"build/widgets/plan-usage/render.js",
59+
"build/widgets/popular-post/render.js",
5960
"build/widgets/post-comments/render.js",
6061
"build/widgets/post-detail-highlights/render.js",
6162
"build/widgets/post-likes/render.js",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<?php return array('dependencies' => array('react', 'react-jsx-runtime', 'wp-api-fetch', 'wp-core-data', 'wp-data', 'wp-date', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => 'ba2b8baa84cc4f433707');
1+
<?php return array('dependencies' => array('react', 'react-jsx-runtime', 'wp-api-fetch', 'wp-core-data', 'wp-data', 'wp-date', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => 'a954782c54fce30c6496');

build/modules/data/index.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/modules/data/index.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/modules/widgets-toolkit/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ __( "Avatar of %s", "jetpack-premium-analytics-pkg" );
4343
/* translators: %s is a country name. */
4444
__( "Flag of %s", "jetpack-premium-analytics-pkg" );
4545
__( "See report", "jetpack-premium-analytics-pkg" );
46+
/* translators: %s: the post's publish date, e.g. "Jun 5, 2026". */
47+
__( "Post published on %s", "jetpack-premium-analytics-pkg" );
48+
__( "Not available", "jetpack-premium-analytics-pkg" );
4649
/* translators: %d is the number of additional subscribers not shown. */
4750
_n( "%d more", "%d more", 1, "jetpack-premium-analytics-pkg" );
4851
__( "Could not download report.", "jetpack-premium-analytics-pkg" );
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-private-apis', 'wp-theme'), 'module_dependencies' => array(array('id' => '@jetpack-premium-analytics/data', 'import' => 'static'), array('id' => '@jetpack-premium-analytics/externals', 'import' => 'static'), array('id' => '@jetpack-premium-analytics/ui', 'import' => 'static'), array('id' => '@wordpress/a11y', 'import' => 'static'), array('id' => '@wordpress/route', 'import' => 'static')), 'version' => 'a1a41a0d3087c999021a');
1+
<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-private-apis', 'wp-theme'), 'module_dependencies' => array(array('id' => '@jetpack-premium-analytics/data', 'import' => 'static'), array('id' => '@jetpack-premium-analytics/externals', 'import' => 'static'), array('id' => '@jetpack-premium-analytics/ui', 'import' => 'static'), array('id' => '@wordpress/a11y', 'import' => 'static'), array('id' => '@wordpress/route', 'import' => 'static')), 'version' => '053c0a175e24f2ddb088');

build/modules/widgets-toolkit/index.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/modules/widgets-toolkit/index.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/widgets/latest-post/render.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
// translation files resolve. Never loaded at runtime: the generated PHP
77
// loaders are pinned to the .min.js sibling.
88

9-
/* translators: %s: the post's publish date, e.g. "Jun 5, 2026". */
10-
__( "Published %s", "jetpack-premium-analytics-pkg" );
119
__( "Views", "jetpack-premium-analytics-pkg" );
1210
__( "Likes", "jetpack-premium-analytics-pkg" );
1311
__( "Comments", "jetpack-premium-analytics-pkg" );

0 commit comments

Comments
 (0)