Skip to content

Componentry: migrate ContextualUpgradeTrigger to @wordpress/ui Notice#48909

Merged
CGastrell merged 5 commits into
trunkfrom
change/migrate-cut-wp-ui
May 19, 2026
Merged

Componentry: migrate ContextualUpgradeTrigger to @wordpress/ui Notice#48909
CGastrell merged 5 commits into
trunkfrom
change/migrate-cut-wp-ui

Conversation

@CGastrell
Copy link
Copy Markdown
Contributor

@CGastrell CGastrell commented May 18, 2026

Part of #48160.

Proposed changes

Migrate all 7 in-repo consumers of ContextualUpgradeTrigger off @automattic/jetpack-components and onto @wordpress/ui's Notice composition. Mark the ContextualUpgradeTrigger source as @deprecated (JSDoc only — no API change) following the chip → Badge (#48162) and Status (#48711) precedents. No deletions on the source; npm consumers continue to compile.

This is the second slice in the wrapper-first migration plan after Status (#48711). The shadowing analysis identified CUT as the highest impact-per-line wrapper: 44 LOC source, but its migration collapses 6 shadowed Text + 2 shadowed Button + 1 shadowed Title sites that would otherwise need separate re-touches.

Design refresh, not visual parity. The Notice chrome (rounded, soft-tinted, intent icon at top-left) is intentionally different from the legacy CUT card. This aligns with the broader UI modernization established by #48550 (Search dashboard SimpleNoticeNotice).

Per-consumer mapping

  • plugins/protect (3 sites — co-located helper):
    • src/js/components/threats-list/free-list.jsx
    • src/js/routes/scan/scan-footer.jsx
    • src/js/routes/firewall/index.jsx
    • Wrapped in a small package-local <UpgradeNotice> helper at src/js/components/upgrade-notice/index.tsx. The helper is a thin shell over Notice.Root + Notice.Description + Notice.Actions and supports both onClick and href callsites. Following the AdminSectionHero.StatusIndicator pattern from the Status slice.
  • js-packages/scan (1 site — inline):
    • src/components/threat-modal/threat-fix-details.tsx — inline Notice.Root with an ActionButton.
  • packages/videopress (1 site — inline):
    • src/client/admin/components/admin-page/index.tsx — inline Notice.Root with an ActionButton. The conditional empty description short-circuits cleanly inside the Notice composition.
  • packages/search (1 site — inline):
    • src/dashboard/components/pages/sections/plan-usage-section.jsx — inline Notice.Root with an ActionButton. Drops the ThemeProvider wrapper (Notice has its own styles).
  • packages/publicize (1 site — inline Notice + IconTooltip):
    • _inc/components/admin-page/toggles/social-module-toggle/index.tsx — only consumer that used tooltipText. Inline Notice.Root with an ActionLink, plus an IconTooltip from @automattic/jetpack-components for the click-toggle "more details" affordance next to the description. Keeping IconTooltip from jetpack-components matches the Boost precedent (plugins/boost/.../page-cache/meta/meta.tsx): @wordpress/ui Tooltip chains through @wordpress/theme private-apis, which adds a wp-theme script handle that the older Assets::register_script pipeline doesn't have polyfill plumbing for. IconTooltip migration becomes a separate slice later (item Publicize: Add filter to allow plugins to bypass Publicize #7 in the shadow analysis priority).
  • js-packages/components (source — @deprecated JSDoc only):
    • components/contextual-upgrade-trigger/index.tsx — adds the deprecation block above the export, pointing at Notice. Implementation, styles, stories, and barrel export are unchanged.

Test fixtures

  • packages/search/src/dashboard/components/pages/sections/test/plan-usage-section.test.jsx — replaces the @automattic/jetpack-components ContextualUpgradeTrigger mock with a @wordpress/ui Notice mock that emits the same upgrade-trigger / upgrade-description / upgrade-cta testids. All existing assertions pass unchanged.
  • packages/search/tests/js/dashboard/pages/dashboard-page.test.jsx — drops the now-dead ContextualUpgradeTrigger entry from the @automattic/jetpack-components virtual mock.

Acceptance

  • grep -rn "from '@automattic/jetpack-components'" projects/ | grep '\bContextualUpgradeTrigger\b' returns zero matches in source files. ✅
  • Builds green: pnpm jetpack build plugins/protect, pnpm jetpack build packages/videopress --deps, pnpm jetpack build packages/search, pnpm jetpack build packages/publicize, pnpm jetpack build js-packages/scan, pnpm jetpack build js-packages/components. ✅
  • JS tests green: pnpm jetpack test js packages/search, pnpm jetpack test js packages/publicize, pnpm jetpack test js js-packages/scan. ✅
  • ESLint + Prettier clean on all touched files (pre-existing text-domain warnings on jetpack-videopress-pkg / jetpack-search-pkg / jetpack-publicize-pkg are unchanged from trunk). ✅

Related product discussion/links

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

No.

Testing instructions

  1. Install a sandboxed site with Jetpack, Jetpack Protect, Jetpack VideoPress, Jetpack Social, and Jetpack Search on the free tier (so the upgrade prompts render).
  2. Protect → Scan footer — visit Jetpack → Protect (free plan). The upgrade prompt at the bottom of the page should render as a @wordpress/ui Notice (info intent, rounded, soft-tinted background, info icon, "Upgrade Jetpack Protect now" action button). Click the action — the existing upgrade flow should fire (upgradePlan() via usePlan).
  3. Protect → Firewall — visit Jetpack → Protect → Firewall (free plan). Below the "Automatic firewall protection" toggle, the upgrade prompt should render as a @wordpress/ui Notice. Action button triggers the same upgrade flow.
  4. Protect → Threats list — with vulnerability data in the free list, expand a threat row that has a fixedIn value. The "How to fix it?" panel should show the new Notice with the action button.
  5. VideoPress admin page — visit Jetpack → VideoPress on a site without a VideoPress purchase. The upgrade prompt should render as a Notice with the "Upgrade now to unlock unlimited videos…" action button.
  6. Search dashboard — visit Jetpack → Search on a free Search plan with simulated usage data (overage or near-overage). The plan-usage section should render the upgrade Notice in the appropriate scenario (records, requests, both, or no-overage).
  7. Publicize → Social module toggle — visit Jetpack → Social (or the Jetpack Settings → Sharing tab on a non-WPCOM site without paid Social features). Below the "Automatically share your posts" toggle, the "Unlock advanced sharing options" Notice should render. Click the info icon next to the description — the tooltip with the longer copy should toggle open (click-toggle UX, matching the legacy CUT). Click "Power up Jetpack Social" — it should navigate to the upsell URL.
  8. Scan threat modal — open the Jetpack Scan threat modal on a fixable threat. The "How can Jetpack auto-fix this threat?" section should render the Notice with the "Upgrade Jetpack now" action button.

Screenshots

Surface Before (trunk) After (this PR)
Protect → Scan footer cut-migration-protect-scan-footer-before cut-migration-protect-scan-footer-after
Protect → Firewall upgrade prompt cut-migration-protect-firewall-before cut-migration-protect-firewall-after
Protect → Threats list fix CTA drag image here drag image here
VideoPress admin upgrade trigger drag image here drag image here
Search dashboard plan-usage upgrade drag image here drag image here
Publicize social-module-toggle upsell (incl. tooltip) drag image here drag image here
Scan threat-modal fix-details drag image here drag image here

NOTE: the missing screenshots refer to exact same implementation replacements

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack or WordPress.com Site Helper), and enable the change/migrate-cut-wp-ui branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack change/migrate-cut-wp-ui
bin/jetpack-downloader test jetpack-mu-wpcom-plugin change/migrate-cut-wp-ui

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions github-actions Bot added [Feature] Publicize Now Jetpack Social, auto-sharing [JS Package] Components [JS Package] Scan [Package] Publicize [Package] Search Contains core Search functionality for Jetpack and Search plugins [Package] VideoPress [Plugin] Protect A plugin with features to protect a site: brute force protection, security scanning, and a WAF. [Tests] Includes Tests RNA labels May 18, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

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!


Jetpack plugin:

The Jetpack plugin has different release cadences depending on the platform:

  • WordPress.com Simple releases happen as soon as you deploy your changes after merging this PR (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly:
    • Scheduled release: June 2, 2026
    • Code freeze: June 1, 2026

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Social 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.


Protect 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.

@jp-launch-control
Copy link
Copy Markdown

jp-launch-control Bot commented May 18, 2026

Code Coverage Summary

Coverage changed in 1 file.

File Coverage Δ% Δ Uncovered
projects/packages/search/src/dashboard/components/pages/sections/plan-usage-section.jsx 92/107 (85.98%) 0.13% 0 💚

Full summary · PHP report · JS report

@CGastrell CGastrell marked this pull request as ready for review May 18, 2026 16:34
@github-actions github-actions Bot added [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] Social Issues about the Jetpack Social plugin labels May 18, 2026
CGastrell added 4 commits May 18, 2026 18:09
The CUT → Notice migration in social-module-toggle pulled in @wordpress/ui's
Tooltip composition, which transitively chains through @wordpress/theme's
private-apis. social-admin-page.asset.php now declares wp-theme and
wp-private-apis as deps — neither is in WP core, both are polyfilled by
wp-build-polyfills.

class-social-admin-page.php was enqueuing social-admin-page directly without
first registering the polyfills, so the new handles 404'd and the browser
threw 'SyntaxError: Unexpected token <' parsing the HTML error page as JS.
React tree never mounted, Social e2e times out at the onboarding 'Get Started'
button. Pattern matches what videopress / forms / scan / podcast / newsletter
/ backup already do.
…ess/ui Tooltip

The wp-theme polyfill registration approach didn't take effect in publicize's
older Assets::register_script pipeline. After diagnosis: no consumer in the
codebase has shipped a working @wordpress/ui Tooltip in a non-wp-build plugin
yet. Boost (the closest precedent) keeps IconTooltip from jetpack-components
for the same reason.

Revert the wp-build-polyfills wiring + lockfile regen. Use IconTooltip in
social-module-toggle's upgrade Notice. Preserves click-toggle UX from the
original CUT tooltipText. IconTooltip migration becomes a separate slice
later (item #7 in the shadow analysis priority).
@CGastrell CGastrell force-pushed the change/migrate-cut-wp-ui branch from de4324e to b3ceac3 Compare May 18, 2026 21:09
…s a11y speak

The Notice composition triggers @wordpress/a11y speak() which renders the
description text into a visually-hidden .a11y-speak-region in document.body.
The existing getByText/queryByText assertions matched both the visible Notice
and the speak region, causing 'multiple elements found' on the positive case
and 'expected not in document, found' on the negative case (because the
speak region persists across jsdom renders).

Fix: pass the testing-library ignore option to text queries so they target
the visible Notice DOM only. Test-only change; no production code touched.
@CGastrell CGastrell merged commit 27bf601 into trunk May 19, 2026
139 of 145 checks passed
@CGastrell CGastrell deleted the change/migrate-cut-wp-ui branch May 19, 2026 12:58
Copilot AI pushed a commit that referenced this pull request May 19, 2026
…#48909)

* Componentry: migrate ContextualUpgradeTrigger to @wordpress/ui Notice

* Publicize: register wp-build-polyfills before social-admin-page enqueue

The CUT → Notice migration in social-module-toggle pulled in @wordpress/ui's
Tooltip composition, which transitively chains through @wordpress/theme's
private-apis. social-admin-page.asset.php now declares wp-theme and
wp-private-apis as deps — neither is in WP core, both are polyfilled by
wp-build-polyfills.

class-social-admin-page.php was enqueuing social-admin-page directly without
first registering the polyfills, so the new handles 404'd and the browser
threw 'SyntaxError: Unexpected token <' parsing the HTML error page as JS.
React tree never mounted, Social e2e times out at the onboarding 'Get Started'
button. Pattern matches what videopress / forms / scan / podcast / newsletter
/ backup already do.

* Lockfile: regenerate consumer composer.lock after publicize wp-build-polyfills dep

* Publicize: use IconTooltip from jetpack-components instead of @wordpress/ui Tooltip

The wp-theme polyfill registration approach didn't take effect in publicize's
older Assets::register_script pipeline. After diagnosis: no consumer in the
codebase has shipped a working @wordpress/ui Tooltip in a non-wp-build plugin
yet. Boost (the closest precedent) keeps IconTooltip from jetpack-components
for the same reason.

Revert the wp-build-polyfills wiring + lockfile regen. Use IconTooltip in
social-module-toggle's upgrade Notice. Preserves click-toggle UX from the
original CUT tooltipText. IconTooltip migration becomes a separate slice
later (item #7 in the shadow analysis priority).

* Publicize: update social-module-toggle test for @wordpress/ui Notice's a11y speak

The Notice composition triggers @wordpress/a11y speak() which renders the
description text into a visually-hidden .a11y-speak-region in document.body.
The existing getByText/queryByText assertions matched both the visible Notice
and the speak region, causing 'multiple elements found' on the positive case
and 'expected not in document, found' on the negative case (because the
speak region persists across jsdom renders).

Fix: pass the testing-library ignore option to text queries so they target
the visible Notice DOM only. Test-only change; no production code touched.
yuliyan pushed a commit that referenced this pull request May 20, 2026
…#48909)

* Componentry: migrate ContextualUpgradeTrigger to @wordpress/ui Notice

* Publicize: register wp-build-polyfills before social-admin-page enqueue

The CUT → Notice migration in social-module-toggle pulled in @wordpress/ui's
Tooltip composition, which transitively chains through @wordpress/theme's
private-apis. social-admin-page.asset.php now declares wp-theme and
wp-private-apis as deps — neither is in WP core, both are polyfilled by
wp-build-polyfills.

class-social-admin-page.php was enqueuing social-admin-page directly without
first registering the polyfills, so the new handles 404'd and the browser
threw 'SyntaxError: Unexpected token <' parsing the HTML error page as JS.
React tree never mounted, Social e2e times out at the onboarding 'Get Started'
button. Pattern matches what videopress / forms / scan / podcast / newsletter
/ backup already do.

* Lockfile: regenerate consumer composer.lock after publicize wp-build-polyfills dep

* Publicize: use IconTooltip from jetpack-components instead of @wordpress/ui Tooltip

The wp-theme polyfill registration approach didn't take effect in publicize's
older Assets::register_script pipeline. After diagnosis: no consumer in the
codebase has shipped a working @wordpress/ui Tooltip in a non-wp-build plugin
yet. Boost (the closest precedent) keeps IconTooltip from jetpack-components
for the same reason.

Revert the wp-build-polyfills wiring + lockfile regen. Use IconTooltip in
social-module-toggle's upgrade Notice. Preserves click-toggle UX from the
original CUT tooltipText. IconTooltip migration becomes a separate slice
later (item #7 in the shadow analysis priority).

* Publicize: update social-module-toggle test for @wordpress/ui Notice's a11y speak

The Notice composition triggers @wordpress/a11y speak() which renders the
description text into a visually-hidden .a11y-speak-region in document.body.
The existing getByText/queryByText assertions matched both the visible Notice
and the speak region, causing 'multiple elements found' on the positive case
and 'expected not in document, found' on the negative case (because the
speak region persists across jsdom renders).

Fix: pass the testing-library ignore option to text queries so they target
the visible Notice DOM only. Test-only change; no production code touched.
CGastrell added a commit that referenced this pull request May 22, 2026
Drop the last 5 migration-relevant @automattic/jetpack-components imports
in js-packages/connection. ActionButton's isLoading/displayError/errorMessage
behavior is preserved by mapping isLoading -> @wordpress/ui Button's loading
prop and rendering the error message as a sibling paragraph with the same
jp-action-button__error class. The default error string matches ActionButton's
default, so existing tests pass unchanged.

Keepers (DecorativeCard, getRedirectUrl, JetpackLogo, PricingCard,
TermsOfService) remain on @automattic/jetpack-components; the package
dependency stays.

Follows #48150 (Publicize), #48489 (My Jetpack interstitials), #49098
(Partner Coupon), and #48909 (CUT migration).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Publicize Now Jetpack Social, auto-sharing [JS Package] Components [JS Package] Scan [Package] Publicize [Package] Search Contains core Search functionality for Jetpack and Search plugins [Package] VideoPress [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] Protect A plugin with features to protect a site: brute force protection, security scanning, and a WAF. [Plugin] Social Issues about the Jetpack Social plugin RNA [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant