Skip to content

Jetpack Beta: modernize the admin UI with React, the WordPress design system, and the Abilities API#49326

Open
enejb wants to merge 53 commits into
trunkfrom
update/modernize-jetpack-beta-ui
Open

Jetpack Beta: modernize the admin UI with React, the WordPress design system, and the Abilities API#49326
enejb wants to merge 53 commits into
trunkfrom
update/modernize-jetpack-beta-ui

Conversation

@enejb
Copy link
Copy Markdown
Member

@enejb enejb commented Jun 1, 2026

Fixes # N/A

Modernizes the Jetpack Beta Tester admin UI. The plugin was the last major Jetpack plugin with no JS build step: a set of server-rendered PHP templates, 268 lines of vanilla admin.js, and a hand-rolled 1299-line admin.css reimplementing old Calypso "dops-" styling. This replaces all of that with a React app built on the WordPress design system (@wordpress/ui + @automattic/jetpack-components AdminPage), backed by the WordPress Abilities API for all reads and actions.

Screenshots

Before After
Plugin overview
Manage a plugin
Updates available

Proposed changes

  • Backend — WordPress Abilities API (src/abilities/class-beta-abilities.php): registers seven abilities — four reads (jetpack-beta/list-plugins, get-plugin, get-settings, list-updates) and three writes (activate-branch, update-settings, update-plugin). Adds the automattic/jetpack-wp-abilities dependency. All callbacks gate on update_plugins and preserve the multisite/network-admin access control from the old admin_page_load(). meta.mcp.public = false (installing PR code shouldn't be agent-callable). Registers without the global jetpack_wp_abilities_enabled rollout filter since the UI fully depends on these abilities.
  • Frontend — React on @wordpress/ui (src/js/): a new webpack build (mirroring Protect) renders both screens — the plugin overview and the per-plugin branch picker. Uses AdminPage from @automattic/jetpack-components for the standard Jetpack header/footer + breadcrumb (the same chrome the Activity Log uses). The overview leads with a welcome/intro card and a compact, single-card plugin list (divider-separated link rows). Branch activation, search/filter, settings toggles, the pending-updates panel, and the "To Test"/"What changed" panels are all React.
  • Compact lists: both the plugin list and the branch picker are one bordered @wordpress/ui Card with divider-separated rows (shared jetpack-beta-list styles) rather than a stack of separate cards — tighter and easier to scan.
  • Running version: every plugin row (overview) and branch row (manage) shows the concrete active version — including the underlying build for dev channels like Bleeding Edge / Release Candidate, not just the channel label.
  • Pending updates: when a managed plugin has a newer build available it surfaces as a non-dismissable info (info) @wordpress/ui Notice with an in-place Update action, on both the overview and the per-plugin screen.
  • Feature-branch search: the Feature Branches box matches by branch name, PR number, or a pasted GitHub pull-request URL (surfacing each branch's PR number from the ability).
  • Removed the legacy screen templates, admin.js, updates.js, and admin.css (kept the first-run banner + exception templates). The three Admin::show_toggle* helpers are kept as _deprecated_function() shims rather than deleted.
  • UX: plugin list is preloaded from the server bootstrap (window.JetpackBeta.plugins) for an instant first paint, then revalidated against the cache-bypassing list-plugins ability (stale-while-revalidate, no client-side storage); compact-list skeleton loaders instead of a spinner; each list row is a real link (keyboard + middle/Cmd-click) with wordpress.org icons (generic fallback for unpublished plugins); pinned header/footer with the content scrolling; the footer reuses the shared JetpackFooter (new showDefaultLinks prop) without the Products/Help links; mobile horizontal-scroll fixed.
  • Global Autoupdates / Email Notifications settings now appear only on the overview screen (they are site-wide, not per-plugin).

Hardening & review follow-ups

  • Security: activate-branch validates source via a schema enum and catches InvalidArgumentException (clean WP_Error instead of a 500); update-plugin restricts the target to the Beta-managed update set so the ability can't drive arbitrary updates; server-rendered "To Test"/"What changed" HTML is run through wp_kses_post() before delivery (it is injected via dangerouslySetInnerHTML).
  • Accessibility: section/panel/settings titles render as real <h2> headings under the breadcrumb <h1>; external links announce "(opens in a new tab)".
  • i18n: the mu-plugin notice and the "Currently Running" title use createInterpolateElement / sprintf instead of concatenated fragments.
  • Self-update reload: activating a branch of — or updating — Jetpack Beta Tester itself returns a reload flag, and the UI does a full page reload rather than a soft view refresh, since the running app's own PHP/JS was just swapped.
  • Static analysis: dropped the @phan-file-suppress for the Abilities API symbols — they resolve at the minimum tested WordPress (6.9), so the suppression was unused.

Related product discussion/links

  • N/A

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

This removes the legacy data-jptracks-* events that were attached to the old action links (Manage / Activate / toggle). It does not add any new analytics or collect new data. It does add a new REST surface under wp-abilities/v1 for the abilities listed above — all capability-gated (update_plugins) and not exposed to MCP agents (meta.mcp.public = false).

Testing instructions

A live Jurassic Ninja test site is running this branch (Jetpack connected):

Steps:

  1. Go to Jetpack → Beta Tester. Note the welcome/intro card, the Settings panel, and the compact plugin list — it paints instantly (preloaded); each row shows the plugin's icon, current version/state, and a chevron, and the whole row is a link to the manage screen (try middle-click / Cmd-click).
  2. Toggle Autoupdates / Email Notifications in the Settings panel — they persist and the email toggle only shows when autoupdates is on.
  3. Click a plugin (e.g. Jetpack) to open the manage screen. Confirm the breadcrumb (Beta Tester / ), the Currently Running card, the compact branch list (Latest Stable / Release Candidate / Bleeding Edge — each showing its concrete version) with Activate, the Feature Branches / Released Versions search, and the "To Test" panel.
  4. In Feature Branches, search by branch name — or paste a PR number / GitHub pull-request URL — then Activate a branch and confirm it installs/activates and the view refreshes.
  5. If a managed plugin has a newer build, confirm the info "update available" Notice appears and its Update action updates the plugin in place.
  6. Resize to a phone width — there should be no horizontal scrollbar; the footer stays pinned at the bottom.

Note: the Beta plugin has no PHPUnit harness today, so this PR adds no automated tests — a Beta_Abilities registration/permission test is a sensible follow-up. Build/typecheck/eslint/stylelint/phpcs/phan all pass.

enejb and others added 30 commits June 1, 2026 11:38
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Activity Log

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds package.json, webpack.config.js, tsconfig.json, and babel.config.js
modeled on projects/plugins/protect. Adds automattic/jetpack-wp-abilities
to composer.json. Updates .gitignore to cover build/, node_modules/, .cache/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngs)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y metadata

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…and wire init

- Add jetpack-beta/activate-branch and jetpack-beta/update-settings write abilities
- Refactor get_plugin() → build_plugin_view() and get_settings() → build_settings() for DRY reuse
- Extract plugin_view_schema() helper so the large schema literal lives in one place
- Wire Beta_Abilities::init() into plugins_loaded at priority 20 in jetpack-beta.php
- Require wp-admin includes inside activate_branch() for REST-context safety

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…clude

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…client

- Add `JPBETA__PLUGIN_FILE` constant to `jetpack-beta.php` for use with `Assets::register_script`.
- Add `automattic/jetpack-assets` to `composer.json` require (alphabetical order) and update `composer.lock`.
- Add `use Automattic\Jetpack\Assets` to `class-admin.php`; replace legacy `wp_enqueue_style`/`wp_enqueue_script`/`wp_localize_script` with `Assets::register_script`/`Assets::enqueue_script` + `wp_add_inline_script` bootstrapping `window.JetpackBeta`.
- Replace PHP template `require_once` calls in `render()` with `<div id="jetpack-beta-root"></div>`; preserve `PluginDataException` guard and network-admin `RuntimeException` path.
- Create `src/js/api/types.ts`: full TypeScript types for all ability payloads plus `window.JetpackBeta` global declaration.
- Create `src/js/api/abilities.ts`: typed `apiFetch` client for all five Beta abilities endpoints.
- Create `src/js/index.tsx`: entry point — bootstraps `apiFetch` middleware then mounts `<App />` via `createRoot`.
- Create `src/js/app.tsx`: `AdminPage` shell routing between `<PluginList />` and `<PluginManage slug={plugin} />` based on `window.JetpackBeta.plugin`.
- Create `src/js/screens/plugin-list.tsx` and `plugin-manage.tsx`: minimal type-correct stubs (replaced in Tasks 6–7).
- Create `src/js/style.scss`: minimal root layout rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the PluginManage screen (branch picker) and supporting components:
markdown-panel, branch-card, branch-section. The manage screen owns its
own AdminPage wrapper so it can inject a plugin-name breadcrumb after the
async getPlugin() resolves. app.tsx routes list vs manage accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d mu-plugin notice

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d CSS

The React app (Tasks 5–7) fully replaces the old PHP-template/vanilla-JS
UI. Delete the six dead templates, admin.js, updates.js, and admin.css.
Strip the now-dead GET-action handlers (activate-branch, toggle-autoupdates,
toggle-email-notifications) and the helper methods they used
(is_toggle_action, show_toggle, show_toggle_autoupdates, show_toggle_emails)
from class-admin.php, while keeping the multisite network-admin
access-control redirect, render(), render_banner(), to_test_content(),
admin_enqueue_scripts(), and plugin_action_links() intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pe, admin includes)

- Read abilities are called over GET with an `input` query envelope; writes
  POST with an `input` body envelope, matching the wp-abilities/v1 run controller.
- Add a default empty-object input to the zero-arg read abilities so GET
  requests (which cannot encode an empty object) pass input validation.
- Load wp-admin/includes/file.php + plugin.php in build_plugin_view() since the
  REST run endpoint executes outside wp-admin (WP_Filesystem/get_plugin_data).
- Add @wordpress/url dependency for addQueryArgs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…semantics

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…settings heading

- Wrap the plugin list in a Container/Col column (matching the My Jetpack module layout).
- Replace the per-row Manage button with a chevron; the whole card is now a
  keyboard-accessible button that navigates to the plugin's manage screen.
- Add a Settings heading above the global toggles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tainer as the list

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…markup

Use the same @wordpress/ui primitives, '/' separator and h1 current item as
@wordpress/admin-ui's Breadcrumbs, relabeled 'Beta Tester'. Links via a real
anchor since this admin page has no TanStack router for the component's RouterLink.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the redundant uppercase heading for the fixed branches (Latest Stable,
Release Candidate, Bleeding Edge); the card now carries the section name as its
label with the version as a sub-line only when it differs. Searchable sections
(Feature Branches, Released Versions) keep their heading and search.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… a plain card

- Replace the shared JetpackFooter (which hard-codes Products/Help links) with a
  minimal footer: Jetpack mark + Automattic byline only. AdminPage showFooter=false.
- Render the To Test / What changed panels as a regular Card with a heading
  instead of a CollapsibleCard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a shimmering Skeleton primitive and a card-row skeleton; the plugin list,
manage screen, and settings toggles now show layout-matching skeletons while
loading instead of a spinner. Honors prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…name immediately

- Apply jetpack-admin-page-layout-wp-build to the page body so the header pins
  to the top, the middle scrolls, and the footer stays fixed at the bottom
  (and breadcrumb links drop the wp-admin underline). Adds a stable
  'jetpack-beta-page' body class and renders AdminPage 'unwrapped' so the
  footer is a direct page child the mixin can pin.
- Pass the plugin display name in the bootstrap so the manage-screen breadcrumb
  renders immediately instead of waiting for the get-plugin ability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Preload the list-plugins payload into the page bootstrap (cached data) on the
overview screen so it paints instantly, and remember it in localStorage. The
list rarely changes once loaded, so a preloaded/cached list is not re-fetched;
the list-plugins ability is only called when neither is available.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cons

- Wrap each screen's constrained Container in a full-width scroll container so
  the scrollbar sits at the page edge rather than inset beside the content.
- Show each plugin's wordpress.org icon (ps.w.org/<slug>/assets/icon.svg) on
  the list rows, hiding it gracefully when a plugin has no wporg assets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The autoupdate and email-notification settings are site-wide Beta plugin
options, not per-plugin, so the toggles no longer appear on each plugin's
manage screen — only on the overview where they apply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plugins without wordpress.org assets now render a generic plugin icon in a
neutral box instead of hiding the image, so every list row stays aligned.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
enejb and others added 3 commits June 1, 2026 18:03
- activate_branch(): guard the post-activation plugin refresh — catch
  PluginDataException, handle a null Plugin, and propagate a WP_Error from
  build_plugin_view() instead of fataling on a type error.
- get_plugin view: sanitize to_test_html/what_changed_html with wp_kses_post()
  before returning, since the React UI injects them via dangerouslySetInnerHTML.
- update_settings(): normalize email_notifications to bool before persisting,
  matching the autoupdates path.
- get_abilities() docblock: correct the count (seven abilities: four read,
  three write).
- global-toggles: include inFlight in the applySetting useCallback deps and drop
  the eslint-disable, removing the stale-closure re-entrancy hazard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PHP (abilities):
- activate-branch: add an enum to the `source` schema and wrap
  install_and_activate() to catch InvalidArgumentException, so a bad source
  returns a clean WP_Error instead of an uncaught 500.
- update-plugin: restrict the target to the Beta-managed update set
  (Utils::plugins_needing_update) so the ability can't drive arbitrary updates.

PHP (admin):
- Re-add show_toggle_autoupdates/show_toggle_emails/show_toggle as deprecated
  shims (_deprecated_function) instead of removing them outright, avoiding
  fatals for any external caller.

JS/TS:
- plugin-list: render the plugin card as a real <a> link (proper link
  semantics + keyboard/middle-click) instead of role="button"; revalidate the
  remembered plugin list in the background (stale-while-revalidate) so a stale
  localStorage cache reconciles.
- plugin-manage: guard the post-update refresh against setState-after-unmount;
  rebuild the mu-plugin notice with createInterpolateElement (one translatable
  sentence) and the "Currently Running" title with sprintf; render section/card
  titles as real <h2> headings; add "(opens in a new tab)" announcements.
- global-toggles / markdown-panel / branch-section: section titles render as
  <h2> for a proper heading hierarchy.
- footer: announce the external Automattic link opens in a new tab.
- style.scss: unify the --wpds-dimension-padding-2xl fallback to 24px.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The card is a real <a> for link semantics, but it was inheriting the default
link color/underline. Inherit the admin text color and drop the underline, and
suppress the rectangular UA focus outline (it squared off the card's rounded
corners on focus) in favor of the existing blue-border + shadow focus indicator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@manzoorwanijk
Copy link
Copy Markdown
Member

I created a JN site and set the beta tester to this branch but I see WSOD on the beta tester page, with https://*.jurassic.ninja/wp-content/plugins/jetpack-beta-dev/build/index.js?minify=false&ver=7.0 pointing to 404

@ilonagl
Copy link
Copy Markdown
Contributor

ilonagl commented Jun 2, 2026

This looks great, Enej!!

I liked and appreciated the time when things where last updated, as it helps to orient yourself. Would it possible to include it in the new version?

Screenshot 2026-06-02 at 10 59 00

@keoshi
Copy link
Copy Markdown
Contributor

keoshi commented Jun 2, 2026

Looks great, @enejb!

A couple of thoughts:

Plugin overview

Manage a plugin

  • We're missing the settings on this page. Were they global, or respective to the selected plugin?
  • CTAs could be using the accent color.
  • How does it look like when a feature branch or a release version are selected?

Updates available

  • Notice could be informational (info) instead of a warning.

Comment on lines +4 to +7
* Mirrors `@automattic/jetpack-components`' JetpackFooter (Jetpack logo + the
* Automattic byline) but deliberately omits its hard-coded "Products" and
* "Help" menu links, which aren't relevant to the Beta Tester screen.
*
Copy link
Copy Markdown
Member

@simison simison Jun 2, 2026

Choose a reason for hiding this comment

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

It would be pretty simple to just add a feature that doesn't include those links, so you wouldn't need to duplicate the component, which is just more effort to maintain consistency and sort of against the whole idea of having central components like that. ;-)

const JetpackFooter: FC< JetpackFooterProps > = ( { className, menu, ...otherProps } ) => {
let items: JetpackFooterMenuItem[] = [];
if ( ! isWpcomPlatformSite() && ! window?.JetpackNetworkAdminData ) {
items = [
{
label: __( 'Products', 'jetpack-components' ),
href: getAdminUrl( 'admin.php?page=my-jetpack#/products' ),
},
{
label: __( 'Help', 'jetpack-components' ),
href: getAdminUrl( 'admin.php?page=my-jetpack#/help' ),
},
...items,
];
}
if ( menu ) {
items = [ ...items, ...menu ];
}

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.

Good call — done in 8bcfc7c. Added a showDefaultLinks prop (default true, so every existing consumer is unchanged) to the shared JetpackFooter and used <JetpackFooter showDefaultLinks={ false } /> here, dropping the duplicate footer.tsx and its now-redundant CSS.

Comment on lines +88 to +91
* Mirrors the markup of `@wordpress/admin-ui`'s `Breadcrumbs` (the component the
* My Jetpack screens use) — same `@wordpress/ui` primitives, `/` separator, and
* an `h1` current item — but links with a real anchor instead of the TanStack
* router `Link` that component depends on, since this admin page has no router.
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.

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.

Done in 8bcfc7c — linked WordPress/gutenberg#77039 in the renderBreadcrumbs docblock.

<Stack direction="row" align="center" justify="space-between">
<Stack direction="column" gap="xs">
<Card.Title>
<Text variant="body-md" render={ <h2 /> }>
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.

Note that Card.Title already sets styles so you don't need the <Text> here.

If you do need it, the variant should prolly be header-* instead since you're rendering it as h2?

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.

Done in 8bcfc7c — collapsed to <Card.Title render={ <h2 /> }>…</Card.Title> so Card.Title does the styling and we keep the heading semantics; removed the redundant nested <Text>.

Comment on lines +267 to +279
<a
href={ view.bug_report_url }
target="_blank"
rel="external noopener noreferrer"
aria-label={ __(
'Found a bug? Report it! (opens in a new tab)',
'jetpack-beta'
) }
/>
}
>
{ __( 'Found a bug? Report it!', 'jetpack-beta' ) }
</Button>
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.

Would one of these options work?

  • onClick sets document.location.href?
  • You'd use Link component instead of Button?

Noting that we'll need the Link button component:

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.

This is intentionally a link styled as a button (it navigates to the bug-report URL), so a real <a> is the correct element — onClick+document.location would drop middle/Cmd-click and copy-link, and a plain Link reads as text rather than a button. Added a comment referencing WordPress/gutenberg#77098 in 8bcfc7c so we can switch to a first-class link-button when it lands.

@@ -0,0 +1,56 @@
const path = require( 'path' );
const jetpackWebpackConfig = require( '@automattic/jetpack-webpack-config/webpack' );
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.

Might be a good point to add wp-build instead here. ;-)

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.

Great suggestion. This turns out to be a sizeable refactor — the JS would need restructuring into a wp-build sub-package and class-admin.php rewired from Assets::register_script() + index.asset.php to the generated build/build.php handle, and beta would be the first projects/plugins/* to adopt @wordpress/build (all current adopters are packages). To keep this PR focused on the UI modernization, I am splitting the wp-build migration into a dedicated follow-up PR — it also makes a nice before/after of @automattic/jetpack-webpack-config vs @wordpress/build.

- Footer: add a `showDefaultLinks` prop (default true) to the shared
  `@automattic/jetpack-components` JetpackFooter and use it with
  `showDefaultLinks={ false }` instead of duplicating the component. Drops
  beta's footer.tsx and its now-redundant CSS (the shared component brings its
  own styles). Changelog added to js-packages/components.
- Breadcrumb: link the upstream Gutenberg issue (#77039) explaining why we
  reimplement Breadcrumbs without the router.
- Currently Running card: let Card.Title render the <h2> directly instead of a
  redundant nested <Text variant="body-md">.
- Bug-report button: add a comment referencing the pending link-button issue
  (#77098) for the Button-rendered-as-anchor pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@anomiex
Copy link
Copy Markdown
Contributor

anomiex commented Jun 2, 2026

The plugin was the last major Jetpack plugin with no JS build step

You say this like it's a bad thing.

268 lines of vanilla admin.js, and a hand-rolled 1299-line admin.css

I'm not sure that replacing it with 1331 lines of TypeScript and some heavyweight external dependencies, resulting in replacing the 8.2K admin.js with 125K of bundled JS, is really a win for a developer-facing tool.

This seems to add a lot more moving parts that could break when someone is trying to debug plugins on their site.

@tbradsha
Copy link
Copy Markdown
Contributor

tbradsha commented Jun 2, 2026

I haven't looked at the code, but have similar concerns to Brad on the amount of added code (and this is before implementing with wp-build). For a dev plugin, I much prefer simple.

I tried to spin up a JN site, but it gave a blank screen (404 on index.js). Some thoughts on the UI based on the screenshot:

  • The main page is already long, and the new cards make it longer (more scrolling). What would a table view look like?
  • It seems to no longer show which version the release candidate/bleeding edge is?
  • The email notifications/auto-updates bar probably doesn't need a full card of space. Also, I notice before it showed on the manage page...is it only a global setting or is it also a per-plugin setting?
  • The "Stable"/"Dev" badges are a nice indicator at a glance.
  • Should the footer say "Jetpack"?
  • For the "new" screenshot, the Jetpack Beta Tester shows "Stable", but presumably you're running branch version of it?
  • Is this using the Core-themed components (see p1HpG7-xN8-p2)? It seems to be missing the blues that were added in a lot of the other plugins lately.

* @package automattic/jetpack-beta
*/

// @phan-file-suppress PhanUndeclaredFunction, PhanUndeclaredClassMethod @phan-suppress-current-line UnusedSuppression -- Abilities API added in WP 6.9; suppressions needed for older-WP compatibility runs.
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.

The minimum version we test against is 6.9 since #49021, this suppression shouldn't be needed anymore.

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.

Good catch — removed the suppression in 6901a1e. Phan passes without it now that the minimum tested WordPress is 6.9 (Abilities API symbols resolve).

);
}

$view = self::build_plugin_view( $refreshed );
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.

This doesn't do the right thing if Jetpack Beta Tester itself is the plugin that was activated. The fancy React UI doesn't reload.

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.

Fixed in 6901a1e. The activate-branch (and update-plugin) abilities now return a reload flag — true when the affected plugin is Jetpack Beta Tester itself — and the React UI does a full window.location.reload() instead of a soft view refresh in that case, since the running app's own PHP/JS was just swapped.

@anomiex
Copy link
Copy Markdown
Contributor

anomiex commented Jun 2, 2026

I created a JN site and set the beta tester to this branch but I see WSOD on the beta tester page, with https://*.jurassic.ninja/wp-content/plugins/jetpack-beta-dev/build/index.js?minify=false&ver=7.0 pointing to 404

The bug there is that the PR doesn't add a line in projects/plugins/beta/.gitattributes to apply production-include to build/**. It should probably also apply production-exclude to various TypeScript sources and CSS that get bundled for use at runtime.

We're missing the settings on [the manage plugin] page. Were they global, or respective to the selected plugin?

The settings are global, but IIRC were kept on the manage plugin page back in #20168 to make them more straightforward to access.

enejb and others added 12 commits June 2, 2026 10:09
Replace the stack of separate plugin cards with one bordered @wordpress/ui Card
whose rows are link items separated by hairline dividers — tighter and matching
the Social-style list. (@wordpress/ui has no list/item primitive, and
@wordpress/components' ItemGroup/Item is an experimental API blocked by
@wordpress/no-unsafe-wp-apis, so the list is composed from Card + rows.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surface the GitHub PR number on feature-branch cards (new `pr` field from
branch_to_section + schema + type), and extend the Feature Branches search to
accept a pasted pull-request URL (…/pull/12345) or a bare/`#`-prefixed PR
number in addition to branch text — making branches easier to find. Updated the
search placeholder to advertise it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The focus indicator was a square `outline`, which cut straight across the
card's rounded top/bottom corners on the first/last rows. Switch to an inset
box-shadow (follows border-radius) and round the first/last rows to the card's
`--wpds-border-radius-lg`, so the focus ring follows the rounded corners.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch the updates Notice from intent="warning" (orange) to intent="info";
it stays non-dismissable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generalize the compact-list styles (jetpack-beta-list / -list-row) and reuse
them for the branch picker: the simple sections (Existing, Latest Stable,
Release Candidate, Bleeding Edge) collapse into one bordered card of
divider-separated rows, and the searchable Feature Branches / Released Versions
sections render their results in the same compact card under their search box.
Branch cards become rows (no per-branch Card/gap).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eleton

- Plugin row links now show the same inset focus ring for :focus as for
  :focus-visible, overriding the wp-admin default that rendered only a blue
  bottom border on click.
- Loading skeleton is now a single compact list card of divider-separated
  skeleton rows (ListSkeleton), matching the loaded list instead of a stack of
  separate cards.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restore the "Welcome to Jetpack Beta Tester" intro (lost when the legacy
notice template was removed) as a React card at the top of the overview,
reusing the original copy with <em> emphasis via createInterpolateElement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ssion

- Reload the page after activating or updating Jetpack Beta Tester itself: the
  activate-branch / update-plugin abilities now return a `reload` flag (true
  when the affected plugin is Beta itself), and the React UI does a full
  window.location.reload() instead of a soft view refresh, since the running
  app's own code was just swapped.
- Remove the @phan-file-suppress for the Abilities API symbols; the minimum
  tested WordPress is 6.9 (which ships the Abilities API), so it's unused. Phan
  passes without it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ndidate rows

Their pretty version is just the label ("Bleeding Edge" / "Release Candidate"),
so the secondary version line was suppressed. Fall back to the raw `version`
string so each branch row shows which build it points at (the version you'd run).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
For plugins running a dev branch the list showed only the channel label
("Bleeding Edge" / "Release Candidate"). Surface the underlying version via a
new `active_version_detail` field on the list item (from dev_info()) and render
it as a secondary line, matching the manage screen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The server bootstrap (window.JetpackBeta.plugins) is localized on every page
load and already provides the instant-paint preload, so the localStorage layer
was redundant (and a prior staleness hazard). Keep the stale-while-revalidate
shape with the bootstrap as the cache: paint from boot.plugins, then revalidate
against the cache-bypassing list-plugins ability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- is_self/update-plugin reload now match both the stable (jetpack-beta/) and dev
  (jetpack-beta-dev/) file forms via a shared is_self_file() helper. Previously
  is_self() compared the running file (which is the -dev path when a dev build of
  Beta is active) against Plugin::plugin_file() (always the stable path), so
  activating/updating Beta while running its own dev build skipped the reload.
- branch-section: anchor the GitHub PR-URL regex to a host boundary so lookalike
  hosts (evilgithub.com) don't match.
- plugin-manage: guard handleActivated's setView with the mounted ref, matching
  handleUpdated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[JS Package] Components [Plugin] Beta For serving live branches and the beta versions. https://github.com/automattic/jetpack-beta RNA [Status] In Progress

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants