try: Reimplement Jetpack settings with @wordpress/ui (PoC, do not merge)#48181
try: Reimplement Jetpack settings with @wordpress/ui (PoC, do not merge)#48181
Conversation
Replace Jetpack internal / @Automattic / Calypso UI primitives in _inc/client/security/ with @wordpress/components (legacy Gutenberg) primitives. @wordpress/ui 0.11.x is listed as a dependency but does not yet ship form-control primitives (ToggleControl, TextControl, TextareaControl, BaseControl, Button, Notice, Card); @wordpress/components remains the canonical fallback today — matches the existing pattern used elsewhere in the Jetpack admin (see traffic/, settings/index.jsx). Swaps per file - account-protection.jsx: SimpleNotice / NoticeAction → Notice - allowList.jsx: components/button → Button, components/forms FormFieldset → BaseControl, components/textarea → TextareaControl, @Automattic ToggleControl → ToggleControl - antispam.jsx: components/text-input → TextControl, components/forms FormFieldset+FormLabel → BaseControl, components/form-input-validation → Notice (inline), components/gridicon → @wordpress/icons Icon - backups-scan.jsx: components/card → Card + CardBody - sso.jsx: @Automattic ToggleControl → ToggleControl, @Automattic Gridicon → @wordpress/icons Icon (closeSmall), components/forms FormFieldset → BaseControl - waf.jsx: @Automattic ToggleControl/Status → ToggleControl + inline span (no primitive Status), components/button → Button, components/textarea → TextareaControl, components/card → Card + CardBody, components/forms FormFieldset → BaseControl Preserved (flagged in source as NOTE comments) - SettingsCard / SettingsGroup: Jetpack settings-save containers coupled to module-form Redux state; not pure UI primitives. - withModuleSettingsFormHelpers: Redux form HOC. - ModuleToggle: wraps ToggleControl with module-override state + analytics. - FoldableCard: no clean @wordpress/ui or @wordpress/components equivalent (Panel/PanelBody differ in header/open semantics). - Banner / JetpackBanner / PlanIcon: Jetpack-specific upsell + plan components with analytics and plan-awareness. - InfoPopover: Jetpack-styled popover; @wordpress/components Popover has a different anchoring API. Behavior preserved: Redux connections, REST / action creators, i18n wrapping, prop contracts, capability / offline-mode gates, module-active checks, and file layout all unchanged. Only UI primitives swapped; the dropped `toggling` prop on ToggleControl (Jetpack-specific spinner state) is a minor visual regression in saving state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace Jetpack custom / @Automattic UI primitives in the Writing settings section with @wordpress/ui primitives where available, falling back to @wordpress/components for controls that have no ui equivalent yet. - Chip -> @wordpress/ui Badge - Button (Jetpack/dops) -> @wordpress/ui Button - Card / CompactCard -> @wordpress/ui Card.Root + Card.Content - FormFieldset / FormLegend -> @wordpress/ui Fieldset.Root / Fieldset.Legend - FormLabel -> plain <label> (preserves jp-form-label class) - @automattic/jetpack-components ToggleControl -> @wordpress/components ToggleControl (@wordpress/ui has no toggle primitive) - FormSelect -> @wordpress/components SelectControl (@wordpress/ui Select is a compound Root/Trigger/Popup/Item API; SelectControl plays nicer with the existing updateFormStateOptionValue helper) Business-logic composites (SettingsCard, SettingsGroup, ModuleToggle, ModuleOverriddenBanner, withModuleSettingsFormHelpers, QuerySite, ClipboardButtonInput) are retained from _inc/client/components per the refactor rules and annotated with NOTE comments. Redux wiring, REST calls, action creators, i18n, prop contracts, feature gates, and file layout are unchanged.
Replace Jetpack internal UI primitives (components/text-input, components/forms FormLabel/FormFieldset/FormLegend/FormTextarea, components/button, components/card, components/notice) and @automattic/jetpack-components ToggleControl / @wordpress/components ExternalLink with @wordpress/ui primitives first, falling back to @wordpress/components only where @wordpress/ui has no equivalent (ToggleControl). Swaps per file: - Input ← TextInput (all files) - Textarea ← FormTextarea (seo.jsx) - Field.Root / Field.Label ← FormLabel (all) - Fieldset.Root / Fieldset.Legend ← FormFieldset / FormLegend (all) - Button ← components/button (site-stats, seo, google verification, seo/custom-seo-titles) - Card.Root / Card.Content ← Card (site-stats inactive card, related-posts preview + configure links, blaze dashboard link) - Link ← ExternalLink from @wordpress/components (google-analytics, sitemaps, verification-services, google verification) - Notice.Root / Notice.Description ← SimpleNotice (seo.jsx) - Icon (check) from @wordpress/icons ← Gridicon checkmark-circle (google verification) - ToggleControl from @wordpress/components ← ToggleControl from @automattic/jetpack-components (fallback: @wordpress/ui has no toggle primitive) Preserved as-is (Jetpack composites that wrap Redux/analytics/module- override logic and shared layout — rewriting inline would duplicate shared infrastructure): - SettingsCard, SettingsGroup, ModuleToggle, FoldableCard, ModuleOverriddenBanner, JetpackBanner, ClipboardButtonInput, Banner (yoast-promo), withModuleSettingsFormHelpers Redux connections, selectors, action creators, analytics tracks, prop contracts, i18n wrapping, module-is-active gates, and file layout are unchanged.
Replace the legacy dops Card (components/card) with Link from @wordpress/ui for the three "Configure your ..." links in Likes, Publicize, and Share Buttons settings. The remaining imports from _inc/client/components/ (SettingsCard, SettingsGroup, ModuleToggle, BlockThemeNotice, QuerySite, withModuleSettingsFormHelpers) are Redux-connected / analytics-aware composed helpers shared across Jetpack settings. They have no 1:1 equivalents in @wordpress/ui or @wordpress/components, so per the refactor exception they are left in place with an explanatory comment. No state, REST, i18n, prop contracts, or feature gates were changed.
Remove the forbidden `@automattic/jetpack-components` dependency from the settings section shell. `GlobalNotices` is replaced with an inline `SnackbarList` from `@wordpress/components` wired to `@wordpress/notices` via `@wordpress/data` hooks. `EmptyState` and `Stack` continue to come from `@wordpress/ui`. `ThemeProvider` from `@automattic/jetpack-components` is preserved with a rationale comment: it is a CSS-variable injector (not a UI primitive) with no equivalent in `@wordpress/ui` or `@wordpress/components`, and removing it would break the `--jp-*` tokens consumed by `style.scss`. Redux wiring, routing, i18n, feature gates, and file layout are unchanged.
Replace Jetpack custom / @Automattic UI primitives in the Discussion settings section with @wordpress/ui primitives where available, falling back to @wordpress/components for controls that have no ui equivalent yet. - FormFieldset -> @wordpress/ui Fieldset.Root - FormLabel -> plain <label> (preserves jp-form-label class) - TextInput (dops) -> @wordpress/components TextControl (@wordpress/ui has Input but the form expects a value-based onChange that plays nicer with updateFormStateOptionValue; TextControl also matches the pattern already used in sibling wpui migrations) - FormSelect -> @wordpress/components SelectControl (@wordpress/ui Select is a compound Root/Trigger/Popup/Item API that doesn't fit the simple options map the form uses) - @automattic/jetpack-components ToggleControl -> @wordpress/components ToggleControl (@wordpress/ui has no toggle primitive); merged the toggling prop into the existing disabled expression per the prop map Business-logic composites (SettingsCard, SettingsGroup, ModuleToggle, withModuleSettingsFormHelpers, QuerySite, SupportInfo/InfoPopover) are retained from _inc/client/components per the refactor rules and annotated with NOTE comments. Redux wiring, REST calls, action creators, i18n, prop contracts, feature gates, and file layout are unchanged.
Refactors `_inc/client/earn/` to use @wordpress/ui primitives as the default UI layer. Stays within the target directory and preserves Redux, REST, i18n, prop contracts, feature gates, and file layout. Swaps (to @wordpress/ui): - components/card (CompactCard + href) -> Card.Root / Card.Content with <a> - components/forms (FormFieldset/FormLegend) -> Fieldset.Root / Fieldset.Legend - components/text-input -> Input (Field.Root + Field.Label) - components/textarea -> Textarea - @wordpress/components ExternalLink -> Link (openInNewTab) - Added Stack for vertical rhythm between toggles and feature cards. Fallbacks (to @wordpress/components): - @automattic/jetpack-components ToggleControl -> @wordpress/components ToggleControl with __nextHasNoMarginBottom. Drops `toggling` (not in wpui target API); saving/disabled state still flows via `disabled`. Preserved helpers (NOTE comments in files): - SettingsCard, SettingsGroup, ModuleToggle, withModuleSettingsFormHelpers, QuerySite + components/data/*, getRedirectUrl. No pnpm install / build / test. Source-only edits.
Migrates the Jetpack settings "My Plan" section to @wordpress/ui primitives where available, falling back to @wordpress/components where @wordpress/ui has no equivalent. Swaps: - components/button + ExternalLink -> @wordpress/ui Button + Link (openInNewTab) via Base UI render prop - components/card (Calypso) -> @wordpress/ui Card.Root / Card.Content - components/text-input -> @wordpress/components TextControl (fallback; @wordpress/ui Input requires Field.Root composite) - isPrimary / primary -> variant="solid" tone="brand" - compact -> size="compact" - Dropped rna prop Preserved with NOTE: QuerySite, QueryRecommendationsData, QuerySitePlugins, MyPlanCard, PlanIcon, ProductActivated, ProductExpiration, UpgradeLink, getRedirectUrl. Redux wiring, REST helpers, i18n strings, prop contracts, feature gates, and file layout unchanged.
Replace Jetpack custom / @Automattic UI primitives in the Newsletter settings section with @wordpress/ui primitives where available, falling back to @wordpress/components for controls that have no ui equivalent yet. - Chip -> @wordpress/ui Badge - Button (Jetpack/dops, including primary+rna) -> @wordpress/ui Button with variant="solid" tone="brand" (primary) or variant="outline" size="compact" (secondary / small Gravatar button). - Card / CompactCard -> @wordpress/ui Card.Root + Card.Content (clickable cards become Card.Root > Card.Content > <a>). - FormFieldset / FormLegend -> @wordpress/ui Fieldset.Root / Fieldset.Legend, preserving the jp-form-legend / jp-form-label-wide class hooks. - TextInput + Col/Container sender-name layout -> @wordpress/ui Input inside a plain .sender-name flex container (Col/Container dropped without visual regression thanks to the existing style.scss layout). - Textarea (components/textarea) -> @wordpress/ui Textarea. - @automattic/jetpack-components ToggleControl -> @wordpress/components ToggleControl (@wordpress/ui has no toggle primitive); dropped the non-existent `toggling` prop and folded the saving state into the existing `disabled` expression to preserve save-in-progress UX. Business-logic composites (SettingsCard, SettingsGroup, ModuleToggle, withModuleSettingsFormHelpers, QuerySite, SupportInfo, TreeDropdown, createNotice action, getRedirectUrl, global-notices state) are retained per the refactor rules and annotated with NOTE comments. Redux wiring, REST calls, action creators, i18n, prop contracts, feature gates (isWpAdminNewsletterSettingsEnabled, FEATURE_NEWSLETTER_JETPACK), and file layout are unchanged.
Replace Jetpack internal / @Automattic / Calypso UI primitives in _inc/client/performance/ with @wordpress/ui primitives first, falling back to @wordpress/components only where @wordpress/ui has no equivalent (ToggleControl). Matches the pattern used elsewhere in the Jetpack admin (see traffic/, settings/index.jsx, security/). Swaps per file: - media.jsx: - FormLegend / FormFieldset (components/forms) → Fieldset.Root / Fieldset.Legend (@wordpress/ui) - ToggleControl (@automattic/jetpack-components) → ToggleControl (@wordpress/components); `toggling` prop dropped (no equivalent) - ProgressBar (@automattic/jetpack-components) → native <progress> element (semantic HTML, no @wordpress/ui progress primitive exists) - `rna` prop dropped on JetpackBanner - search.jsx: - Card (components/card) with `compact` + `href` → Card.Root (size="compact") + Card.Content with inner <a href> - FormFieldset → Fieldset.Root - ToggleControl (@automattic/jetpack-components) → ToggleControl (@wordpress/components); `toggling` prop dropped - speed-up-site.jsx: - FormFieldset → Fieldset.Root - ToggleControl (@automattic/jetpack-components) → ToggleControl (@wordpress/components); `toggling` prop / togglingSiteAccelerator state dropped (ToggleControl has no `toggling` prop) - index.jsx: - NOTE comment added for QuerySite + components/data/* Preserved as-is (Jetpack composites that wrap Redux/analytics/module- override logic and shared layout — rewriting inline would duplicate shared infrastructure): - SettingsCard, SettingsGroup, ModuleToggle, JetpackBanner, QuerySite + components/data/*, withModuleSettingsFormHelpers - getRedirectUrl from @automattic/jetpack-components (helper, not a UI primitive) Redux connections, selectors, action creators, analytics tracks, prop contracts, i18n wrapping, module-is-active gates, REST usage, feature gates (siteHasFeature, FEATURE_* constants), and file layout are unchanged.
Swap the `CompactFormToggle` opt-out control for `ToggleControl` from `@wordpress/components` (priority-2 fallback — `@wordpress/ui` does not currently ship a toggle primitive). The helper text previously rendered via `<p>` is moved into the toggle's `label`, matching the peer pattern used in `security/sso.jsx` (wrapping with `jp-form-toggle-explanation`). `ExternalLink` is kept from `@wordpress/components` because `@wordpress/ui`'s `Link` does not expose the accessible "opens in a new tab" affordance required for these outbound policy links. Jetpack-specific containers (`SettingsCard`, `SettingsGroup`), the module-settings form HOC, and the `getRedirectUrl` helper are preserved with explanatory NOTE comments per the refactor exception list. Redux, REST, i18n, prop contracts, and feature gates are unchanged.
Replace the Jetpack internal `components/card` wrapper used for the
"Visit the Reader" link with `@wordpress/ui` Card.Root + Card.Content
(priority 1). The anchor is now a plain <a> inside Card.Content,
preserving href/target/rel/onClick tracking.
Preserved with NOTE comments (per the refactor rules):
- QuerySite + components/data (Redux data-fetching, not UI)
- SettingsCard / SettingsGroup (save buttons, override, upsells)
- ModuleToggle (ToggleControl + analytics wrapper)
- withModuleSettingsFormHelpers (form-state HOC)
- SimpleNotice (status="is-info"/showDismiss has no
parity in @wordpress/ui Notice yet)
- getRedirectUrl (allowed helper, not a UI primitive)
No changes to Redux wiring, REST flows, i18n strings, prop contracts,
feature gates, or file layout. No edits outside the target directory.
Replace leaf UI primitives in projects/plugins/jetpack/_inc/client/recommendations with @wordpress/ui (Button) and @wordpress/icons (Icon), leaving Jetpack composites (SidebarCard, StepProgressBar, InstallButton, InfoPopover, JetpackLoadingIcon, MoneyBackGuarantee, AppsBadge, ProgressBar, Query* data fetchers) and shared state helpers in place with NOTE comments explaining why. - Button (components/button) -> Button from @wordpress/ui primary/rna -> variant="solid" tone="brand" borderless -> variant="ghost" - Gridicon (components/gridicon) -> Icon from @wordpress/icons arrow-left -> chevronLeft checkmark -> check checkmark-circle-> check external -> external star -> starFilled - Drop legacy dops `is-rna` class from ExternalLink button-styled links. No Redux, REST, i18n, or prop contracts changed; no file layout changes.
Replace the Jetpack internal Banner primitive (components/banner) that was used as the "Activate module" CTA with @wordpress/ui Card.Root + Card.Content + Button primitives. The dops-banner hack (anchor with href="#" intercepted by a document-level click listener) is gone — Button's onClick now fires updateOptions directly, so the componentDid Mount/componentWillUnmount anchor-click plumbing was removed. Swaps: - Card.Root / Card.Content ← Banner wrapper (components/banner) - Button (variant="solid" tone="brand" size="compact") ← Banner's callToAction prop + internal dops-button rendering - Inline title/description markup ← Banner title/description props Preserved as-is (Jetpack composites that wrap Redux state, module override gating, connection flow, analytics, or shared settings layout — rewriting inline would duplicate shared infrastructure): - SettingsCard, SettingsGroup, ConnectUserBar, withModuleSettingsFormHelpers Redux connections, selectors, safelist, search gating (searchTerms.length >= 3), admin-only gating, offline/ site-connection availability checks, i18n wrapping, and file layout are unchanged. CSS hook (.jp-searchable-banner) preserved so any existing scoped styles keep applying. Prop mapping applied per spec: - isPrimary → variant="solid" tone="brand" - compact → size="compact" - rna dropped
Scope: `_inc/client/notices/`. - `validation-error-list.jsx`: convert to a function component and wrap the summary + message list in `Stack` from `@wordpress/ui` for the inner layout. Prop contract (`messages`) and `displayName` preserved. - `index.js`: unchanged behaviour — add a NOTE documenting that this is a pure data/event module (Calypso-style notice emitter + store) with no UI primitives to migrate. The rendering path in `components/global-notices/index.jsx` deliberately keeps the `SimpleNotice` + `NoticeAction` pairing, which is on the preserve list. - `style.scss` and `README.md`: unchanged. No pushes, no PR, no pnpm install, no builds, no tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
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 🤖 🔴 Action required: Please include detailed testing steps, explaining how to test your change, like so: 🔴 Action required: We would recommend that you add a section to the PR description to specify whether this PR includes any changes to data or privacy, like so: Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! Jetpack plugin: The Jetpack plugin has different release cadences depending on the platform:
If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. |
Code Coverage SummaryCannot generate coverage summary while tests are failing. 🤐 Please fix the tests, or re-run the Code coverage job if it was something being flaky. |
about-page, form-input-validation, owner-disconnect-dialog, textarea, tree-selector had zero remaining imports after the 16-section refactor. Also removed the orphaned about-page SCSS import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sibling relative imports (../owner-disconnect-dialog, ../tree-selector) from connect-button and tree-dropdown meant these were still in use — my earlier unused-scan missed the relative sibling imports. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Experimental spike — do not merge. Proof-of-concept reimplementation of all 16 sections of the legacy Jetpack settings SPA (
_inc/client/) using@wordpress/uias the primary UI primitive layer, with@wordpress/componentsas a fallback where@wordpress/uihas no equivalent.This is a Jurassic Ninja visual test to evaluate how the existing settings UI looks once shared, custom Jetpack components are replaced with the new WordPress Design System primitives.
Scope
16 sections, one commit per section:
at-a-glance(19 files)recommendations(22 files)traffic(10 files)writing(7 files)newsletter(7 files)my-plan(7 files)security(6 files)performance(4 files)sharing(4 files)discussion(2 files)earn(2 files)notices(2 files)privacy(1 file)settings(1 file)reader(1 file)searchable-modules(1 file)Total: 96 files changed.
Approach
_inc/client/components/*UI primitives,@automattic/jetpack-componentsUI primitives, and Calypso components with@wordpress/uifirst,@wordpress/componentsas fallback.SettingsCard,SettingsGroup,ModuleToggle,FoldableCard,JetpackBanner,DashItem, etc.Known regressions / risks
ToggleControl.togglingprop is dropped everywhere —@wordpress/components ToggleControlhas no saving-spinner equivalent. Save gating still works viadisabled..dops-card/.dops-button/.is-compact/.is-card-linkSCSS selectors won't match@wordpress/uioutput..jp-*classnames were preserved on outer wrappers, but layout/border/hover states may shift.@wordpress/uiLinklacks the external-link affordance (arrow icon + SR text);ExternalLinkfrom@wordpress/componentsis used as a fallback where needed.Gridicon→@wordpress/icons Icon: viewBox/stroke differences.@wordpress/uiversion declared inpackage.jsonis0.11.0but the resolved pnpm tree currently uses0.9.0. All primitives used exist in both, perbuild/index.cjs.Test plan