Skip to content

WOOA7S-1495: add Authors widget backed by Jetpack Stats#49570

Open
Nikschavan wants to merge 10 commits into
trunkfrom
wooa7s-1495-port-jetpack-stats-module-authors
Open

WOOA7S-1495: add Authors widget backed by Jetpack Stats#49570
Nikschavan wants to merge 10 commits into
trunkfrom
wooa7s-1495-port-jetpack-stats-module-authors

Conversation

@Nikschavan

@Nikschavan Nikschavan commented Jun 12, 2026

Copy link
Copy Markdown
Member

Proposed changes

Google Chrome -2026-06-12 at 11 15 10@2x

Ports the Authors widget from the Jetpack Stats module into the Premium Analytics dashboard, backed by the Jetpack Stats API.

  • data package: adds a stats-fetch API client that goes through the stats-admin proxy (/jetpack/v4/stats-app/sites/{blog_id}/stats/{resource}), plus statsQuery/statsTopAuthorsQuery query builders and useStatsTopAuthors/useStatsBlogId hooks. The blog ID is resolved once from /jetpack/v4/connection/data and cached for the page lifetime; queries use skipToken while the blog ID or report date is unresolved.
  • widgets-toolkit: adds AuthorsWidget (leaderboard chart of top authors by views with comparison support) and a buildTopAuthorsData helper, with loading/refetching overlays and empty/error states.
  • premium-analytics plugin: registers the jpa/authors dashboard widget (render entry, widget.json, widget.ts with a configurable max attribute). The render entry defaults to an all-time report range until the host can pass the site launch date.
  • query client provider: adds a withDevtools prop so per-widget providers don't each render React Query devtools.
  • widget-dashboard: insets picker previews with padding instead of scaling, so preview content clears the selection checkbox.

Related product discussion/links

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

No.

Testing instructions

  • Set up a Jetpack-connected site with the Premium Analytics plugin active.
  • Go to the Premium Analytics dashboard and open the widget picker.
  • Verify the picker previews are inset with padding and the content clears the selection checkbox.
  • Add the Authors widget.
  • Verify it renders a leaderboard of top authors by views, sourced from Jetpack Stats (check the network tab for requests to /jetpack/v4/stats-app/sites/{blog_id}/stats/top-authors).
  • Change the Maximum authors attribute and verify the list respects the limit.
  • On a site with no traffic, verify the empty state copy renders instead of the chart.

@github-actions

github-actions Bot commented Jun 12, 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.

@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 Jun 12, 2026
Comment thread projects/packages/premium-analytics/packages/data/src/api/constants.ts Outdated
* TODO: Remove the default range once we have a way to pass the launched date to the widget.
*/
const getDefaultReportParams = () => ( {
from: '2000-01-01T00:00:00',

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.

Temporary hardcoded long date range to view the data in the widget, until we have date range picker in the dashboard

@jp-launch-control

jp-launch-control Bot commented Jun 12, 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

@louwie17 louwie17 force-pushed the update/pa-introduce-customize-dashboard branch from 909996e to d060b2d Compare June 12, 2026 02:39
@nerrad nerrad force-pushed the update/pa-introduce-customize-dashboard branch 2 times, most recently from 5bb6dd9 to 7ff7f71 Compare June 17, 2026 11:19
@louwie17 louwie17 force-pushed the update/pa-introduce-customize-dashboard branch from 7ff7f71 to 22e10a7 Compare June 19, 2026 12:21
@Nikschavan Nikschavan force-pushed the wooa7s-1495-port-jetpack-stats-module-authors branch from e1f31ef to 49f10a6 Compare June 23, 2026 15:03
@Nikschavan Nikschavan changed the base branch from update/pa-introduce-customize-dashboard to trunk June 23, 2026 15:04
@Nikschavan Nikschavan requested a review from Copilot June 24, 2026 06:02
@Nikschavan

Copy link
Copy Markdown
Member Author

@claude Review the PR please

@claude

claude Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Nikschavan's task in 3m 43s —— View job


PR Review — Authors widget

Reviewed the 11 files in this commit. Note: the data package (stats-fetch, statsTopAuthorsQuery, useStatsTopAuthors), the withDevtools provider prop, and the widget-picker CSS changes described in the PR body are not in this commit's diff (they already exist in trunk). So this review covers the toolkit widget + helper + render entry only. Overall this is clean, well-documented, and closely follows the established widget conventions (loading/refetch/error/empty handling, LeaderboardChart with emptyState* props, render/widget.json/widget.ts trio). A few things worth addressing:

🐛 Correctness / i18n

1. "Untracked authors" never shows the translated stringgetAuthorLabel in build-top-authors-data.ts:23-27 falls back to __( 'Untracked authors' ) only when author.label is empty. But the processing layer already substitutes a value at processing/stats/top-authors.ts:26:

label: item.name || 'Untracked Authors',

So by the time data reaches buildTopAuthorsData, label is always a non-empty string, and authors with no name render the untranslated 'Untracked Authors' (capital A) — the translated fallback in the helper is effectively dead code. Make the untracked label translatable and consistent in one place (probably the processing layer, or normalize in the helper by treating the sentinel as "untracked"). Fix this →

2. Authors are merged by display labelsummarizeAuthors keys the Map by label and sets id: existing?.id ?? label. Two distinct authors sharing a display name will be collapsed into one leaderboard row with summed views. Likely rare, but if the API exposes a stable author id, prefer keying on that for both dedup and id. Low severity — worth a comment acknowledging the trade-off if you keep it.

🔧 Type safety

3. reportParams as StatsReportParams cast (authors-widget.tsx:54) — this is the only widget in the toolkit that casts reportParams (others pass it straight to their hook). The context types it as ReportParams, while StatsReportParams = ReportParams & StatsQueryParams. The extra StatsQueryParams fields are all optional so it's not unsafe at runtime today, but the cast suppresses checking. Cleaner would be to type useStatsTopAuthors's param as ReportParams (since it only adds optional fields) so call sites don't need the assertion. Same goes for the primary.data as StatsTopAuthorsReport casts — if the hook can be made generic it avoids the assertions.

📝 Minor / consistency

4. widget.json description over-promises"Top authors by views, with their most viewed posts." The widget only renders the authors leaderboard; the nested children (posts) that the processing layer builds are never displayed. Either drop "with their most viewed posts" or wire up the posts UI.

5. max is a free-text attribute (widget.ts:18, type: 'text') — toPositiveInt safely falls back to 7 for non-numeric/negative input, so no breakage, but the user gets silent correction with no feedback. If the attribute framework supports a number/min input, that'd be a nicer UX. Non-blocking.

💬 Answers to your inline questions

  • constants.ts temporary stats endpoint → proxy: Agreed, and this matches the documented migration path — AGENTS.md shows stats-app/* maps onto the stats/jetpack-stats proxy prefixes already allowlisted in PREFIX_CONFIG, so the switch should be a config-only change. Good to flag.
  • Should conversion be backend or here? (processing/stats-top-authors): Keeping normalization client-side is consistent with every other stats widget (processing/stats/* all normalize on the frontend). I'd keep it here for consistency unless payload size becomes a concern — splitting one widget to the backend would fragment the pattern.
  • Hardcoded date range (render.tsx): Fine as a clearly-marked TODO. One note: getDefaultReportParams builds to from new Date() in local time, which is what you want for an all-time view.

✅ Nice touches

  • Division-by-zero guard (Math.max( …, 1 )) and comparison alignment by label in buildTopAuthorsData.
  • The isInitialLoading = ( isLoading || primary.isPending ) && ! hasData comment explaining the skipToken disabled window.
  • Changelog entry present and correctly typed.

  • Read changed files & diff
  • Review widgets-toolkit (AuthorsWidget, build-top-authors-data)
  • Review premium-analytics widget (render, widget.json/ts)
  • Answer inline questions
  • Post feedback

Note: the widget-dashboard CSS / data-package / devtools changes from the PR description aren't in this commit, so they weren't reviewed here.

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

Adds a new Authors widget to the Jetpack Premium Analytics dashboard, using the Jetpack Stats “top-authors” endpoint and a new widget/toolkit implementation to render a leaderboard with comparison support.

Changes:

  • Register new jpa/authors widget metadata (type/module JSON) with a configurable max attribute and a render entry using WidgetRoot.
  • Add AuthorsWidget to the widgets-toolkit plus a buildTopAuthorsData helper to transform Stats responses into leaderboard chart data.
  • Export the new widget/helper from the widgets-toolkit public entrypoints and add a changelog entry.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
projects/packages/premium-analytics/widgets/authors/widget.ts Defines the widget type (name/title/icon) and its max attribute.
projects/packages/premium-analytics/widgets/authors/widget.json Widget picker metadata (title/description/category).
projects/packages/premium-analytics/widgets/authors/render.tsx Render entry wiring WidgetRoot + AuthorsWidget, including a wide default report range.
projects/packages/premium-analytics/widgets/authors/package.json Declares per-widget package dependencies.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/index.ts Exports AuthorsWidget from the widgets toolkit widget barrel.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/authors/index.ts Authors widget module re-export.
projects/packages/premium-analytics/packages/widgets-toolkit/src/widgets/authors/authors-widget.tsx Main widget component: fetches top authors and renders a leaderboard with comparison/empty/loading/error handling.
projects/packages/premium-analytics/packages/widgets-toolkit/src/index.ts Exposes AuthorsWidget from the toolkit public API.
projects/packages/premium-analytics/packages/widgets-toolkit/src/helpers/index.ts Exports buildTopAuthorsData helper.
projects/packages/premium-analytics/packages/widgets-toolkit/src/helpers/build-top-authors-data.ts Converts Stats “top-authors” report(s) into LeaderboardChart-ready data with deltas/shares.
projects/packages/premium-analytics/changelog/update-pa-authors-widget Changelog entry for introducing the Authors widget.

Comment thread projects/packages/premium-analytics/widgets/authors/widget.json
Comment thread projects/packages/premium-analytics/widgets/authors/build-top-authors-data.ts Outdated
Comment thread projects/packages/premium-analytics/widgets/authors/package.json
@Nikschavan Nikschavan force-pushed the wooa7s-1495-port-jetpack-stats-module-authors branch 2 times, most recently from 4eb1e79 to 36d5586 Compare June 24, 2026 07:11
@Nikschavan Nikschavan marked this pull request as ready for review June 24, 2026 08:46
@Nikschavan Nikschavan requested review from a team as code owners June 24, 2026 08:46
@Nikschavan Nikschavan force-pushed the wooa7s-1495-port-jetpack-stats-module-authors branch from fe70374 to 017a5ac Compare June 24, 2026 08:47

@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 work on the port! @Nikschavan

A couple of things before merge:

  • This PR doesn't include a Storybook story — AGENTS.md asks every widget to ship one. Could you add it?
  • The description mentions a withDevtools prop and a picker-preview change, but I don't see those in the diff. Did they land separately on rebase? Mind updating the description to match?

Also left a couple of inline comments.

* <AuthorsWidget />
* </WidgetRoot>
*/
export function AuthorsWidget( { max }: AuthorsWidgetProps ) {

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.

Could we move AuthorsWidget into widgets/authors/render.tsx? widgets-toolkit/src/widgets/* is for the legacy (unmigrated) widgets and not to use them as templates. The newer stats ports keep the body in widgets/<name>/render.tsx e.g. locations defines a LocationsInner there or #49568 we defines a TopPostsLeaderboard.

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.

Moved it. The body now lives in widgets/authors/render.tsx as a local AuthorsLeaderboard component, mirroring LocationsInner and the TopPostsLeaderboard in #49568, and I dropped the old widgets-toolkit/src/widgets/authors/ entirely. The buildTopAuthorsData helper (and its test) moved into the widget folder too, so the toolkit barrels no longer carry anything authors-specific. render.tsx#L70

Comment thread projects/packages/premium-analytics/widgets/authors/widget.ts Outdated
@Nikschavan Nikschavan force-pushed the wooa7s-1495-port-jetpack-stats-module-authors branch from afac0cf to 3f8c8f0 Compare June 25, 2026 06:07
@Nikschavan Nikschavan self-assigned this Jun 25, 2026
@@ -0,0 +1,109 @@
/**

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.

Maybe we could coordinate those changes into https://github.com/Automattic/jetpack/blob/trunk/projects/packages/premium-analytics/packages/data/src/processing/stats/top-authors.ts, so that there's no need for more mappings?

return Number.isFinite( parsed ) && parsed > 0 ? parsed : fallback;
};

const toDateString = ( date: Date ) => {

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.

Could toDateString defer to date-fns? It hand-rolls format(date, 'yyyy-MM-dd') I think date-fns or the datetime package should provide similar feature.

const hasReportParams =
!! attributes.reportParams && Object.keys( attributes.reportParams ).length > 0;

return hasReportParams ? attributes : { ...attributes, reportParams: getDefaultReportParams() };

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.

Non-blocking - since this is probably not an issue once we pass the launch date to the widget.

It looks like getDefaultReportParams returns from: 2000-01-01, to: today, interval: 'day', which maps to period=day with days≈9673 plus summarize:1. That would ask WPCOM to summarize roughly 9,600 daily buckets. It should likely be fine since our dev sites probably do not have much data, but I wanted to flag it anyway.

Nikschavan and others added 9 commits June 26, 2026 12:51
- widgets-toolkit: add AuthorsWidget (consumes the useStatsTopAuthors hook
  from the data package) and the buildTopAuthorsData leaderboard helper

- widgets/authors: register the jpa/authors dashboard widget; render.tsx
  fetches via the existing /jetpack-premium-analytics/v1/proxy Stats proxy
  (v1.1 stats/top-authors) and wraps the widget in WidgetRoot

- query client provider: add withDevtools prop so per-widget providers don't
  each render React Query devtools

- widget-dashboard: inset picker previews with padding instead of scaling so
  content clears the selection checkbox
- Forward `max` to the Stats top-authors query so the API limit matches
  the rendered leaderboard instead of relying on the endpoint default.
- Dedup and align authors by stable `author_id` (falling back to the
  display label) so distinct authors sharing a name aren't merged.
- Translate the "Untracked authors" label in a single place: the
  processing layer leaves an empty label and the widget supplies the
  translated fallback.
- Drop `StatsProxyParams`' index signature from `StatsReportParams` so
  `reportParams` no longer needs casting; make `useStatsReport` generic
  so `useStatsTopAuthors` returns a typed report (removes data casts).
- Declare the widget's runtime deps (widgets-toolkit, @wordpress/i18n,
  @wordpress/icons) in its package.json.
- Forward `setError` from the render entry to WidgetRoot.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The empty-state icon was dropped earlier but the import was left behind,
failing ESLint and the exclude-list check in CI.
- Declare presentation (framed) in widget.json.
- Type render props via WidgetRenderProps<AuthorsAttributes> from
  @wordpress/widget-primitives and default attributes to {}.
- Add @wordpress/widget-primitives as a devDependency (type-only import,
  erased at build, keeps runtime deps host-agnostic).
Relocate buildTopAuthorsData and the authors render logic out of the shared widgets-toolkit package into widgets/authors, colocating the widget's tests. Wire the widget to its own dependencies, change the max authors field to integer, and stub CSS imports in jest so widgets-toolkit CSS doesn't get parsed as JS.
Extract AuthorsLeaderboard as an exported presentational component taking pre-built rows, and rename the data-connected wrapper to AuthorsReport so Storybook can exercise loading/empty/populated states. Trust the Stats API's server-side ranking and limiting: replace summarizeAuthors' cross-interval aggregation and sorting with a flat toAuthorItems, dropping the maxEntries param. Update tests accordingly.
@Nikschavan Nikschavan force-pushed the wooa7s-1495-port-jetpack-stats-module-authors branch from 3f8c8f0 to be5e7a9 Compare June 26, 2026 07:24
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.

4 participants