My Jetpack: render interstitials with unified header (breadcrumbs-like back-link + license-key action)#48414
Conversation
…ack`/`label` Additive, back-compat-preserving prop additions to enable rendering the back-link inside AdminPage's `breadcrumbs` slot (the unified-header pattern). No call site changes in this commit; existing usages keep their default behavior. - useGoBack: accepts an optional `fallback` route. Defaults to MyJetpackRoutes.Home so existing callers keep navigating to `/` when the referrer history isn't available. - GoBackLink: accepts optional `to` (explicit destination) and `label` (text content). When `to` is set it overrides the legacy reload-derived destination. Defaults preserve the existing "Go back" / `/` behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nterstitials
Routes affected (all hash-routed inside the My Jetpack admin page):
/add-{akismet,backup,boost,crm,jetpack-ai,extras,protect,scan,social,
search,videopress,stats,security,growth,complete,license}
/jetpack-ai (product detail)
/protect-details (product detail)
Previously, each interstitial passed showHeader=false to AdminPage and
rendered its own GoBackLink at the top of the body. That bypassed the
unified jetpack-admin-page-layout header used everywhere else in MJ and
left the page without a visible title.
Now: the GoBackLink is passed through AdminPage's breadcrumbs slot, which
both restores the unified header treatment (mixin + admin-ui Page chrome)
and acts as the page title — matching the pattern already used by
VideoPress's edit-video-details screen. The body GoBackLink rows are
removed.
The link target is #/products (My Jetpack's Products tab) instead of the
previous /. The reload-on-license-activation semantics are preserved on
add-license-screen via a query string suffix on the to prop.
Drive-by: jetpack-ai/product-page.jsx was calling useGoBack with a
positional string ('jetpack-ai') where the hook destructures { slug }.
Fixed to useGoBack({ slug: 'jetpack-ai', fallback: '/products' }).
showBackground is preserved per file (false for legacy interstitials,
true for the two product-detail pages).
connection-screen and packages/backup are intentionally out of scope:
the former uses CloseLink (different UX pattern), the latter is a
different package and warrants its own review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the pattern used by jetpack-backup (packages/backup/src/js/ components/Admin/index.js): a single compact secondary "Use license key" button rendered in the unified header's actions slot, instead of the longer "Already have an existing plan or license key? Click here to get started." sentence in the body. Affects pricing-interstitial.jsx (always-on CTA) and the legacy product-interstitial.jsx (CTA gated on existingLicenseKeyUrl). The body Col that previously hosted the sentence is removed in both. The license analytics event (jetpack_myjetpack_interstitial_license_link_click) is preserved on pricing-interstitial via onClick. The unused imports left behind (Text, createInterpolateElement, jetpack-components Button in the legacy file) are dropped. 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 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
Code Coverage SummaryThis PR did not change code coverage! That could be good or bad, depending on the situation. Everything covered before, and still is? Great! Nothing was covered before? Not so great. 🤷 |
| breadcrumbs={ | ||
| <GoBackLink | ||
| onClick={ onClickGoBack } | ||
| to={ hasActivatedLicense ? '/products?reload=true' : '/products' } | ||
| label={ __( 'My Jetpack', 'jetpack-my-jetpack' ) } | ||
| /> | ||
| } |
There was a problem hiding this comment.
Funky solution and might break with future updates but good for now 😅
|
|
||
| return ( | ||
| <Link to={ to } className={ styles.link } onClick={ onClick }> | ||
| <Link to={ destination } className={ styles.link } onClick={ onClick }> |
There was a problem hiding this comment.
One solution visually could be importing Link from @wordpress/ui with render prop of Link from the react-router. But fine not in this PR.
| /> | ||
| } | ||
| actions={ | ||
| <WPButton |
There was a problem hiding this comment.
You could use Button from @wordpress/ui directly here, but not a blocker for merge. We'll just need to swap it anyway so why not now.
simison
left a comment
There was a problem hiding this comment.
I haven't tested but code is looking good. We'll need to iterate more on this tho but not a blocker.
…e back-link + license-key action) (Automattic#48414) * My Jetpack: extend useGoBack and GoBackLink with optional `to`/`fallback`/`label` Additive, back-compat-preserving prop additions to enable rendering the back-link inside AdminPage's `breadcrumbs` slot (the unified-header pattern). No call site changes in this commit; existing usages keep their default behavior. - useGoBack: accepts an optional `fallback` route. Defaults to MyJetpackRoutes.Home so existing callers keep navigating to `/` when the referrer history isn't available. - GoBackLink: accepts optional `to` (explicit destination) and `label` (text content). When `to` is set it overrides the legacy reload-derived destination. Defaults preserve the existing "Go back" / `/` behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * My Jetpack: render the back-link in AdminPage's breadcrumbs slot on interstitials Routes affected (all hash-routed inside the My Jetpack admin page): /add-{akismet,backup,boost,crm,jetpack-ai,extras,protect,scan,social, search,videopress,stats,security,growth,complete,license} /jetpack-ai (product detail) /protect-details (product detail) Previously, each interstitial passed showHeader=false to AdminPage and rendered its own GoBackLink at the top of the body. That bypassed the unified jetpack-admin-page-layout header used everywhere else in MJ and left the page without a visible title. Now: the GoBackLink is passed through AdminPage's breadcrumbs slot, which both restores the unified header treatment (mixin + admin-ui Page chrome) and acts as the page title — matching the pattern already used by VideoPress's edit-video-details screen. The body GoBackLink rows are removed. The link target is #/products (My Jetpack's Products tab) instead of the previous /. The reload-on-license-activation semantics are preserved on add-license-screen via a query string suffix on the to prop. Drive-by: jetpack-ai/product-page.jsx was calling useGoBack with a positional string ('jetpack-ai') where the hook destructures { slug }. Fixed to useGoBack({ slug: 'jetpack-ai', fallback: '/products' }). showBackground is preserved per file (false for legacy interstitials, true for the two product-detail pages). connection-screen and packages/backup are intentionally out of scope: the former uses CloseLink (different UX pattern), the latter is a different package and warrants its own review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * My Jetpack: move "Use license key" CTA into AdminPage actions slot Mirrors the pattern used by jetpack-backup (packages/backup/src/js/ components/Admin/index.js): a single compact secondary "Use license key" button rendered in the unified header's actions slot, instead of the longer "Already have an existing plan or license key? Click here to get started." sentence in the body. Affects pricing-interstitial.jsx (always-on CTA) and the legacy product-interstitial.jsx (CTA gated on existingLicenseKeyUrl). The body Col that previously hosted the sentence is removed in both. The license analytics event (jetpack_myjetpack_interstitial_license_link_click) is preserved on pricing-interstitial via onClick. The unused imports left behind (Text, createInterpolateElement, jetpack-components Button in the legacy file) are dropped. 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: dognose24 <6869813+dognose24@users.noreply.github.com>
* My Jetpack: migrate interstitial Buttons to @wordpress/ui Follow-up to #48414. Replaces the `Button` imports in the product interstitial / pricing-interstitial / more-requests screens with the new `Button` from `@wordpress/ui`. Affects: - `pricing-interstitial.jsx`: free/paid/bundle tier CTAs (`isLoading` → `loading`, `variant="secondary"` → `variant="outline"`, `fullWidth` → small `tier-cta` width:100% class) and the actions-slot "Use license key" CTA. - `product-interstitial.jsx`: the actions-slot "Use license key" CTA. - `jetpack-ai/more-requests.jsx`: the "Contact Us" + "Back" buttons. `href` cases use base-ui's `render={<a/>}` (with `nativeButton={false}`). `@wordpress/ui` Button styles live in `@layer wp-ui-components`, which loses to wp-admin's un-layered `a { color }` rule when rendered as an anchor; a single non-layered rule in `_inc/style.module.scss` re-asserts the button foreground color so anchor-rendered Buttons get the right text color. Also fixes invalid `<a><button/></a>` nesting on the "Back" button by moving react-router's `<Link>` into the Button's `render` prop. * My Jetpack: drop unneeded wp-ui Button anchor color override The non-layered `a[class*="__button"][class*="__is-"] { color: ... }` override existed to neutralize wp-admin's un-layered `a { color }` rule beating wp-ui's layered button color when a Button is rendered as an anchor (`render={<a/>}`). Auditing the actually-reachable interstitials: - Tier CTAs in pricing-interstitial render as native `<button>` (no `render` prop wired up on this path), so the cascade-layer issue doesn't apply. - The "Use license key" action button is anchor-rendered but uses the outline variant — wp-admin theme blue vs. wp-ui brand blue is visually indistinguishable on the white background. - The only solid-variant anchor Button in the migration lives in `more-requests.jsx` (`JetpackAIInterstitialMoreRequests`), which has zero consumers across the repo: no router entry in admin.jsx, no parent component imports it, and the AI store's `isOverLimit` state drives in-place notices on the product page rather than mounting the interstitial. It's unreachable code; the override was protecting a view that never renders. Removing the workaround keeps the file in line with other consumer packages and lets the next layer-aware Button consumer rely on the upstream cascade rather than a per-package band-aid.
Tracks the My Jetpack header-unification follow-ups noted while reviewing #48378.
Proposed changes
Brings the My Jetpack interstitial / product-detail / license-add screens into the same unified-header treatment used everywhere else in the dashboard (and matches the pattern already shipped by VideoPress's edit-video-details and jetpack-backup).
Before this PR each interstitial passed
showHeader={ false }to AdminPage and rendered its own<GoBackLink>at the top of the body, leaving these pages without a visible page title and bypassing the unified@wordpress/admin-ui Pagechrome (mixin, breadcrumbs, header actions slot). The "Already have an existing plan or license key? Click here to get started" sentence sat in a flex row alongside the back-link.This PR:
AdminPage'sbreadcrumbsslot (router-agnostic — just a<Link>fromreact-router, no@wordpress/admin-ui Breadcrumbsto avoid pulling in@wordpress/routeand a router refactor). The breadcrumbs slot doubles as the page title, matching the deliberate design call to keep these screens title-less aside from the back affordance.AdminPage'sactionsslot as a single compact secondary "Use license key" button — same pattern aspackages/backup/src/js/components/Admin/index.js. No morecreateInterpolateElement/<a>-in-translation-string boilerplate.#/productsfor the back-link (the My Jetpack Products tab) instead of the previous/. Thereload=truesemantics onadd-license-screenare preserved.useGoBack( 'jetpack-ai' )(the hook destructures{ slug }).Routes affected (all hash routes inside the My Jetpack admin page)
/add-akismet,/add-backup,/add-boost,/add-crm,/add-jetpack-ai,/add-extras,/add-protect,/add-scan,/add-social,/add-search,/add-videopress,/add-stats,/add-security,/add-growth,/add-complete,/add-license,/jetpack-ai,/protect-details.Out of scope
connection-screen/index.tsx— uses<CloseLink>(X icon dismissal), a different navigation pattern; needs a separate UX call.packages/backup/src/js/components/Admin/index.js— also usesshowHeader={ false }, but it's a different package and the change is independent.jetpack-admin-page-layoutmixin not being applied to MJ. With the unified header path now in use, the mixin's wp-admin chrome reset (notably#wpfooter) is the right next step. Tracked as a separate change so we can sanity-check the main MJ overview page in isolation.Related product discussion/links
Does this pull request change what data or activity we track or use?
No. The existing
jetpack_myjetpack_product_interstitial_back_link_clickandjetpack_myjetpack_interstitial_license_link_clickevents are preserved.Visual diff
/add-backup(PricingInterstitial — common path)/add-security(ProductInterstitial — bundle upsell with license CTA)/add-license(license activation screen)/jetpack-ai(product detail)/protect-details(product detail)Testing instructions
/wp-admin/admin.php?page=my-jetpack#/add-*route on a Jetpack-connected site./add-license: activate any pending license, then click the back-link. Thereload=truequery string should still trigger a refresh of the home view./jetpack-aiand/protect-details: confirm the back navigation works the same way and the page background remains.Changelog
packages/my-jetpack— patch / changed entry covers both the breadcrumbs and actions changes.🤖 Generated with Claude Code