Newsletter: scaffold the wp-build dashboard chassis behind the flag#48574
Merged
Newsletter: scaffold the wp-build dashboard chassis behind the flag#48574
Conversation
Contributor
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
6 tasks
Contributor
|
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! |
Code Coverage SummaryCoverage changed in 1 file.
|
keoshi
added a commit
that referenced
this pull request
May 6, 2026
…ready fired `WP_Build_Polyfills::register()` adds its registration callback to `wp_default_scripts`, but that action only fires once — when `wp_scripts()` is first instantiated. On admin requests where any earlier-loading plugin touches `wp_scripts()` before `admin_menu` priority 1 (where consumers like Newsletter call `register()`), the hook arrives too late and the polyfills never register: dependent script modules like `@wordpress/boot` and classic scripts like `wp-theme` go missing, the boot loader's `<script type="module">` tag is never printed, and the wp-build chassis fails to mount on a blank page. Detect that case via `did_action( 'wp_default_scripts' )` and run `register_scripts()` + `register_modules()` synchronously on the live `wp_scripts()` instance, so the polyfills land regardless of init order. Surfaced while testing Newsletter's modernization chassis (PR #48574) on a Docker site where Jetpack JITM enqueues scripts during `plugins_loaded`, instantiating `wp_scripts()` before `admin_menu`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
keoshi
added a commit
that referenced
this pull request
May 6, 2026
…ready fired `WP_Build_Polyfills::register()` adds its registration callback to `wp_default_scripts`, but that action only fires once — when `wp_scripts()` is first instantiated. On admin requests where any earlier-loading plugin touches `wp_scripts()` before `admin_menu` priority 1 (where consumers like Newsletter call `register()`), the hook arrives too late and the polyfills never register: dependent script modules like `@wordpress/boot` and classic scripts like `wp-theme` go missing, the boot loader's `<script type="module">` tag is never printed, and the wp-build chassis fails to mount on a blank page. Detect that case via `did_action( 'wp_default_scripts' )` and run `register_scripts()` + `register_modules()` synchronously on the live `wp_scripts()` instance, so the polyfills land regardless of init order. Surfaced while testing Newsletter's modernization chassis (PR #48574) on a Docker site where Jetpack JITM enqueues scripts during `plugins_loaded`, instantiating `wp_scripts()` before `admin_menu`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
432598e to
126ad0b
Compare
…ready fired `WP_Build_Polyfills::register()` adds its registration callback to `wp_default_scripts`, but that action only fires once — when `wp_scripts()` is first instantiated. On admin requests where any earlier-loading plugin touches `wp_scripts()` before `admin_menu` priority 1 (where consumers like Newsletter call `register()`), the hook arrives too late and the polyfills never register: dependent script modules like `@wordpress/boot` and classic scripts like `wp-theme` go missing, the boot loader's `<script type="module">` tag is never printed, and the wp-build chassis fails to mount on a blank page. Detect that case via `did_action( 'wp_default_scripts' )` and run `register_scripts()` + `register_modules()` synchronously on the live `wp_scripts()` instance, so the polyfills land regardless of init order. Surfaced while testing Newsletter's modernization chassis (PR #48574) on a Docker site where Jetpack JITM enqueues scripts during `plugins_loaded`, instantiating `wp_scripts()` before `admin_menu`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
126ad0b to
1eb4e7c
Compare
dhasilva
approved these changes
May 6, 2026
dhasilva
pushed a commit
that referenced
this pull request
May 7, 2026
The page-shell scaffolded in #48574 now renders the Subscribers list: the Stage wraps `<SubscribersBody>` (selection, modals, header actions) inside the existing `<Page>` chrome and `QueryClientProvider`, adds the page subtitle, and pulls in `route.scss` for the identity cell, inspector chrome, and detail layout styles. The route still has no tabs — Settings stays on its legacy surface behind the modernization filter until a later PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dhasilva
pushed a commit
that referenced
this pull request
May 7, 2026
Replaces the empty inspector with `<SubscriberDetailContent>` reading `?subscriber=…&u=…` from the URL — boot's router already gates the mount on those params via `route.inspector` (added in #48574). Wraps the slot in its own `QueryClientProvider` so subscriber-detail queries share the cache the stage has already warmed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dhasilva
added a commit
that referenced
this pull request
May 7, 2026
…#48581) * Newsletter + Forms: lift Gravatar into @automattic/jetpack-components The Gravatar component (with hovercard support, SHA-256 hashing, and a configurable defaultImage) moves into @automattic/jetpack-components exposed at the `./gravatar` subpath. Forms switches to it; the local copy in `projects/packages/forms/src/dashboard/components/gravatar/` is removed and the `@gravatar-com/hovercards` and `js-sha256` deps move with it. Sets up Newsletter's Subscribers port to consume the same shared component. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Newsletter: add REST proxy for the Subscribers admin page Adds `WPCOM_REST_API_V2_Endpoint_Subscribers_List` registering `/wpcom/v2/subscribers/{list,individual,totals,stats,add,remove,products,comp,remove-comp}`. Each route proxies to the corresponding WP.com endpoint via `Automattic\Jetpack\Connection\Client::wpcom_json_api_request_as_user`. The wp-admin Newsletter page (next commit) consumes these endpoints to render the Subscribers DataViews table and per-subscriber actions on Jetpack-connected self-hosted sites. Glob-loaded by `load-wpcom-endpoints.php`, so no loader changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Newsletter: add the Subscribers data layer + library helpers Ports the non-rendering plumbing from #48420's Phase 2 into `_inc/subscribers/`: - `data/api.ts` + `data/types.ts` — REST client + DataViews/API types. - React Query hooks for list, individual, totals, stats, add, remove, comp / remove-comp, and memberships products. - `lib/` helpers: CSV parsing, DataViews i18n, site context, subscriber helpers, subscription plans + status, Tracks events, and the DataViews `View` <-> URL state hook. No UI yet. The list, cells, modals, and detail pane (next commit) consume these hooks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Newsletter: add the Subscribers DataViews UI Drops in the rendering layer ported from #48420: - `subscribers-data-views.tsx` — the table/list with fields, filters, pagination, row actions, and bulk actions. - `subscribers-body.tsx` — selection, modal orchestration, and the `<SubscribersDataViews>` mount. - `cells/{subscriber-identity,subscription-status-cell,subscription-type-cell}` — column renderers (identity uses the shared `Gravatar`). - `detail/subscriber-detail-content.tsx` — sliding inspector content with joined date, status, and email engagement stats. - `modals/{add-subscribers,comp,remove-comp,unsubscribe}-modal` — row + bulk action dialogs. - `header-actions.tsx`, `empty-state.tsx`, `jetpack-logo.tsx` — page chrome accents. Not yet mounted; the next commit wires `<SubscribersBody />` into the route Stage. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Newsletter: mount the Subscribers DataViews in the dashboard Stage The page-shell scaffolded in #48574 now renders the Subscribers list: the Stage wraps `<SubscribersBody>` (selection, modals, header actions) inside the existing `<Page>` chrome and `QueryClientProvider`, adds the page subtitle, and pulls in `route.scss` for the identity cell, inspector chrome, and detail layout styles. The route still has no tabs — Settings stays on its legacy surface behind the modernization filter until a later PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Newsletter: render the subscriber detail in the inspector slot Replaces the empty inspector with `<SubscriberDetailContent>` reading `?subscriber=…&u=…` from the URL — boot's router already gates the mount on those params via `route.inspector` (added in #48574). Wraps the slot in its own `QueryClientProvider` so subscriber-detail queries share the cache the stage has already warmed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Newsletter: changelog for the Subscribers DataViews port Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Newsletter Subscribers REST: gate routes behind the modernization filter + add tests Until the dashboard UI ships, register no /wpcom/v2/subscribers/* routes unless `rsm_jetpack_ui_modernization_newsletter` is on — same gate the wp-build page already uses. The check lives inside `register_routes()` (which fires on `rest_api_init`) so theme- or plugin-added filters are applied before it evaluates, instead of at file-load time when only mu-plugins have run. Adds a focused test class covering the new gate, the cap check (anon and under-privileged users), and the handler-level input validation paths in `add` / `remove` / `individual` / `stats` — the layers most likely to silently regress without coverage. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Newsletter Subscribers: harden CSV download link + cover remove-mutation Pass `noopener,noreferrer` to `window.open` for the page-header CSV download — matches the pattern used everywhere else we open a new tab. Adds Jest coverage for `useSubscriberRemoveMutation`: singular vs bulk snackbar copy, the partial-failure path that emits both success and error notices, and the MAX_BULK_REMOVE cap that protects WP.com from a runaway selection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix changelog * Jest configs: allow @gravatar-com CSS through transformIgnorePatterns Lifting `Gravatar` into `@automattic/jetpack-components` made every package whose tests touch that components index transitively evaluate `gravatar/index.tsx`, which side-effect imports `@gravatar-com/hovercards/dist/style.css`. Jest's default behavior is to skip `node_modules` for transformation, so the CSS reached the JS parser and blew up with `SyntaxError: Unexpected token '.'` across publicize, social, and the plugins/jetpack admin-page tests. Two configs maintain their own allowlists: the shared base config in `tools/js-tools/jest/config.base.js`, and the plugins/jetpack gui runner at `projects/plugins/jetpack/tests/jest.config.gui.js`. The gui runner's local pattern overrides the base via `transformIgnorePatterns` set semantics (union — any matching pattern wins, so a local "ignore" beats a base "unignore"), so both need the same allowlist entry. Adds `@gravatar-com` to both alongside `uplot` and `@wordpress/admin-ui`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Douglas <douglas.henri@automattic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #48530 — second of six PRs to modernize Jetpack's Newsletter into a unified wp-admin product. Builds on #48532 (the empty wp-build scaffold + feature flag) by establishing the runtime chassis the next four PRs sit inside.
Proposed changes
<h1>Newsletter</h1>stage withPagechrome from@wordpress/admin-ui. Body is still empty — Subscribers DataView lands in PR 3, the Settings tab in PR 4.@tanstack/react-queryQueryClientProviderwith a sharedqueryClient(30 sstaleTime, no refetch-on-focus). Picking React Query here, not@wordpress/datacore-data, matches the prototype (#48420) so PR 3 can drop the existing Subscribers data hooks in unchanged.analytics.initialize()fromNewsletterSettingsAppinto the route Stage, so future Tracks events (jetpack_newsletter_tab_viewetc.) fire regardless of which tab a visitor lands on.Inspectorslot atroutes/dashboard/inspector.tsxso the route hook can target a stable surface. The subscriber detail panel lands inside it in PR 3.route.inspectorhook to asubscriber/uURL param (Boolean( search?.subscriber || search?.u )) — boot's router calls this on every navigation and uses the boolean to decide whether to render the inspector. Returnsfalseat runtime today (no subscriber selection UI yet); PR 3 enables it.All gated behind
rsm_jetpack_ui_modernization_newsletter. With the flag off, the legacy Newsletter Settings page renders unchanged.Related product discussion/links
Does this pull request change what data or activity we track or use?
No. The analytics initializer is moved, not changed; the only Tracks-related delta is where
analytics.initializeruns — same call, same args. No new events fire.Testing instructions
Pull this branch, run
pnpm installandpnpm run buildfromprojects/packages/newsletter/. Then verify both flag states:Flag OFF (regression check)
add_filter( 'rsm_jetpack_ui_modernization_newsletter', ... )exists in any mu-plugin /wp-config.php/ theme.wp-admin/admin.php?page=jetpack-newsletter.analytics.initializestill fires fromNewsletterSettingsApp(untouched by this PR),<div id="newsletter-settings-root">mount point is present.Flag ON (new chassis)
wp-config.php, or use the Code Snippet plugin:wp-admin/admin.php?page=jetpack-newsletter.build/scripts/orbuild/pages/jetpack-newsletter-dashboard/) loads, not the legacynewsletter.js.analytics.initializefires exactly once per page load (checkTracksin DevTools → Network orwindow._tkqto confirm a singlerecordpayload on init).Toggle round-trip
add_filterline and refresh — legacy Newsletter Settings page should return immediately.