Repository navigation
Stats v2: rename Premium Analytics menu and stop replacing legacy Stats - #50915
Conversation
Renames the bundled dashboard's admin menu label from "Premium Analytics" (WPCOM Simple) / "Analytics" (default, used by Atomic and other flag-enabled sites) to "Stats v2", and removes the code that suppressed the legacy Stats menu, admin-bar entries, post-list column, and dashboard widget whenever the flag was on. Stats v2 now adds its own menu alongside the existing Stats UI instead of replacing it, matching how WPCOM Simple already behaved.
|
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! Jetpack 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. |
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
Code Coverage SummaryCoverage changed in 2 files.
|
Narrows scope back to the label rename. The legacy Stats menu, admin-bar entries, post-list column, and dashboard widget keep being suppressed by the existing jetpack_premium_analytics_enabled gate exactly as before — that gate and the "replace" behavior it drives are untouched. Stats v2 already registers its own separate top-level menu unconditionally when enabled; nothing about that needed to change.
Scopes the existing jetpack_premium_analytics_enabled replace behavior to exclude Atomic sites (Automattic\Jetpack\Status\Host::is_woa_site()). On Atomic the legacy Stats menu, admin-bar entries, post-list column, and dashboard widget now stay in place alongside the new Stats v2 menu instead of being suppressed. Self-hosted and other WPCOM flag-enabled sites keep the existing replace behavior unchanged.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Broadens the previous Atomic-only exception to every site: Stats v2 always adds its own admin menu alongside the existing Stats menu, admin-bar entries, post-list column, and dashboard widget instead of replacing them, regardless of host. Drops the now-unused Host::is_woa_site() check from modules/stats.php and class-jetpack-stats-dashboard-widget.php.
Retype the plugins/jetpack changelog entry as enhancement instead of bugfix — this is an intentional rollout behavior change, not a defect fix. Document why test_stats_admin_menu_kept_when_enabled() only tears down the one action it asserts on.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
🤖 Review-cycle summary —
|
| Source | Comment | Resolution |
|---|---|---|
| claude[bot] | (#5126405852) 3 non-blocking notes: duplicated replace-predicate across files, stats_load() test hygiene, changelog type bugfix vs enhancement |
Predicate concern resolved outright by f5223dec56 (the replace logic was deleted entirely, not centralized); added a hygiene doc-comment to the test and retyped the changelog to enhancement in b17bf0b4a3 |
| claude[bot] | (#5126523064) re-review — verified all three items, no blocking issues, one non-blocking observation (the per-request cache on is_premium_analytics_enabled() is now less load-bearing with fewer callers) |
No action needed — reviewer explicitly noted nothing to change |
Unaddressed (flagged for owner):
None.
CI: all required checks passing (36 pass, 8 skipping as expected for unaffected file types).
Since #50915 the Stats menu deliberately keeps rendering alongside the dashboard, so the docblocks describing it as deregistered were wrong. Reworded to the present tense: the dashboard is the analytics UI, and Stats remains available for now. The behaviour is unchanged, but its reason was wrong too. Hiding a link for a user who cannot open the dashboard is right not because there is no Stats page to fall back to, but because the dashboard capability maps to `manage_options` or `view_stats` — a user who fails it cannot open the Stats page either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB
Since #50915 the Stats menu deliberately keeps rendering alongside the dashboard, so the docblocks describing it as deregistered were wrong. Reworded to the present tense: the dashboard is the analytics UI, and Stats remains available for now. The behaviour is unchanged, but its reason was wrong too. Hiding a link for a user who cannot open the dashboard is right not because there is no Stats page to fall back to, but because the dashboard capability maps to `manage_options` or `view_stats` — a user who fails it cannot open the Stats page either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB
Reverts the plan-usage changes: render.tsx, its test, its story, and the changelog entry that announced the removal. The link was removed on the grounds that it pointed at a Stats screen that no longer existed. It does still exist — since #50915 the Stats menu keeps rendering alongside the dashboard, and following the link on a flag-on site lands on a working Stats tier picker. Whether the CTA needs a new home is a question for when the Stats page actually goes away, not for this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB
…rd (#50926) * Premium Analytics: point Jetpack's analytics links at the new dashboard Seven links across Jetpack still pointed at the legacy Stats page. Rather than spell a URL at each call site, add getAnalyticsUrl() to script-data: callers describe where they want to go and the helper resolves which analytics UI the site runs, the page slug, the site and blog identifiers, the section vocabulary, and the date encoding. Premium Analytics announces itself by publishing an `analytics` key on jetpack_admin_js_script_data. Its presence is the branch signal, so no caller has to ask Jetpack about the feature flag; when absent, the helper builds the legacy Stats URLs byte-for-byte as before. Notable details: - The dashboard keeps its whole router path in a single `p` search param, and reads from/to as offset-bearing instants in the site timezone. A bare YYYY-MM-DD would be parsed as UTC midnight and land a day early west of UTC, so the helper takes calendar days and owns the encoding, reading the zone offset at each boundary so a DST-transition day gets the right offset on each end. - At-a-Glance chart bars now open the period the bar stands for. The old link ignored the Days/Weeks/Months tab and always deep-linked one day. - getAnalyticsUrl() returns null when the user lacks the capability to open the dashboard, and every call site hides its control rather than rendering a dead link. - The plan-usage upgrade CTA is removed. It pointed at a Stats tier purchase screen that was a Calypso route inside the CDN-served Odyssey bundle, so it left with that dashboard and has no counterpart here. - My Jetpack's Stats get_purchase_url() returns null, the base-class default, which falls back to the existing /add-stats interstitial. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB * Premium Analytics: narrow the analytics URL helper to what Jetpack links to Three reductions, all from review feedback: - Drop the `report` and `video` views. Nothing constructed them; they were built from the design doc rather than from a caller, along with a 13-member report id union and their tests. - Stop hand-rolling timezone math. `toFixedOffset`, `offsetAt`, `offsetToMinutes` and the two-pass DST offset read are replaced by date-fns + @date-fns/tz, the same libraries the dashboard's own `dateToISOStringWithTZ` uses. The DST-transition test still passes, so the bespoke version was only reimplementing the library. - Stop rebuilding legacy Stats URLs. Those links already worked, so each call site keeps its original expression untouched and only routes to the dashboard when it has replaced the Stats page. That also restores `force_refresh=1` on My Jetpack's card and the prop threading this had removed, so the non-dashboard path is now unchanged from trunk rather than merely equivalent to it. `hasAnalyticsDashboard()` is the new gate. It answers a different question from a null URL: false means "keep your existing link", whereas null means the dashboard is the analytics UI but this user cannot open it. Those must not collapse — `stats_admin_menu` is not registered once the dashboard replaces Stats, so there is no page to fall back to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB * Premium Analytics: move the analytics URL helper into the Jetpack plugin The helper was in js-packages/script-data, which meant adding date-fns to a previously zero-dependency package that 23 projects depend on and that is autopublished to npm. It only ever had two consumers, and one of them did not need it at all. My Jetpack drops it entirely. Its Stats card already receives the destination as the product's `manage_url`, which `Stats::get_manage_url()` resolves server-side, so the JS was rebuilding a URL PHP had already sent. The one thing the URL cannot carry is whether to append `force_refresh=1` — that is now a `premiumAnalyticsEnabled` entry in `get_my_jetpack_flags()`, alongside the existing showFullJetpackStatsCard and isStatsModuleActive flags. That leaves the Jetpack plugin as the only consumer, so the helper moves to `_inc/shared/analytics-url.ts`. Its three call sites live in different trees built by two webpack configs, and the extensions build has no bare-specifier root, so each imports it by relative path. `_inc/shared/` is new: `_inc/client`, `modules/` and `extensions/` had no shared JS between them before this. script-data goes back to exactly trunk apart from the `analytics` key's type — the shape of the global is its job, building URLs from it is not. The jest bootstrap it needed goes away with the test, which now runs under the plugin's existing client config. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB * Premium Analytics: keep the analytics type out of script-data, simplify the helper script-data is now byte-identical to trunk. The `analytics` script-data key is declared where it is read, via module augmentation, so the package that types the global does not carry a key only the Jetpack plugin uses. Test simplification (405 -> 227 lines), mostly by table-driving repetitive cases. One test was worse than redundant: chart-bar-range's "reads the calendar day in UTC" case monkey-patched Date.prototype.getTimezoneOffset, which the implementation never calls, so it passed regardless of correctness. Removed, and timezone independence is now verified by running both date suites under UTC, Pacific/Kiritimati (+14), Pacific/Niue (-11), America/New_York and Asia/Kathmandu (+05:45). Added the DST fall-back case, so both a 23-hour and a 25-hour day are covered rather than only spring-forward. Cleanups from a review pass: - Drop the `encoded.includes( 'Invalid' )` guard. date-fns `format()` throws a RangeError for an unusable zone; it never returns an "Invalid" string, so only the catch was ever reachable. - DASHBOARD_SECTIONS was an identity map keyed by its own union. It is a list of accepted slugs, which is all it was doing. - Inline analyticsPath, whose docblock restated its signature. - Drop `export` from three types nothing imports, and the WHY repeated three times in the same file. - barLink took siteAdminUrl and siteRawUrl as parameters while already reading this.props on its first line. - format_gmt_offset used floor with a 1e-6 epsilon to guard a rounding case that cannot arise for quarter-hour offsets; round() says the same thing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB * Premium Analytics: trim the comment prose on the new code Roughly half of the new files were comment. Kept the reasoning a reader cannot infer — why a bare YYYY-MM-DD will not do, why the router's `p` param is encoded twice, why hasAnalyticsDashboard() differs from a null URL — and dropped the restatements around it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB * Update pnpm-lock.yaml for the date-fns dependency Adding date-fns and @date-fns/tz to plugins/jetpack changes peer resolution for the @wordpress/* packages it shares them with, which pnpm records as new snapshot keys. Earlier commits added only the importer entries, so the resolution section was left stale and CI's lock check failed. Regenerated with `pnpm install --resolution-only`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB * Premium Analytics: correct the docblocks about the Stats page Since #50915 the Stats menu deliberately keeps rendering alongside the dashboard, so the docblocks describing it as deregistered were wrong. Reworded to the present tense: the dashboard is the analytics UI, and Stats remains available for now. The behaviour is unchanged, but its reason was wrong too. Hiding a link for a user who cannot open the dashboard is right not because there is no Stats page to fall back to, but because the dashboard capability maps to `manage_options` or `view_stats` — a user who fails it cannot open the Stats page either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB * Premium Analytics: keep the plan-usage upgrade link Reverts the plan-usage changes: render.tsx, its test, its story, and the changelog entry that announced the removal. The link was removed on the grounds that it pointed at a Stats screen that no longer existed. It does still exist — since #50915 the Stats menu keeps rendering alongside the dashboard, and following the link on a flag-on site lands on a working Stats tier picker. Whether the CTA needs a new home is a question for when the Stats page actually goes away, not for this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DQezwoePExAo1sc1onk7fB --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Why this matters
The bundled analytics dashboard currently shows as "Premium Analytics" (WPCOM Simple) or the generic "Analytics" (Atomic and other flag-enabled sites), and it swaps out the existing Stats menu entirely the moment it's enabled — no way back to the surface people are used to mid-rollout. This renames the dashboard to "Stats v2" and adds it as a second menu alongside the existing Stats menu, on every site, instead of replacing it.
Fixes #
Proposed changes
Related product discussion/links
jetpack_premium_analytics_replaces_statsflag, so there's currently no way to restore replacement behavior without a follow-up code change if that's ever neededDoes this pull request change what data or activity we track or use?
Testing instructions
jetpack_premium_analytics_enabledflag on (or the WPCOM Simple / Atomic rollout gates), confirm both "Stats" and "Stats v2" appear as separate top-level admin menu items, and that the Stats admin-bar entry, post-list column, and WP dashboard widget are still present.jp docker phpunit jetpack --php 8.3 -- --filter "Jetpack_Premium_Analytics_Test|Stats_Admin_Bar_Test"— 15/15 pass.jp test php packages/jetpack-mu-wpcomand theAnalytics_Testlabel assertions inpackages/premium-analytics— pass.