Skip to content

My Jetpack: render interstitials with unified header (breadcrumbs-like back-link + license-key action)#48414

Merged
CGastrell merged 3 commits into
trunkfrom
feat/mj-interstitials-unified-header
May 4, 2026
Merged

My Jetpack: render interstitials with unified header (breadcrumbs-like back-link + license-key action)#48414
CGastrell merged 3 commits into
trunkfrom
feat/mj-interstitials-unified-header

Conversation

@CGastrell
Copy link
Copy Markdown
Contributor

@CGastrell CGastrell commented Apr 30, 2026

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 Page chrome (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:

  • Renders the back-link inside AdminPage's breadcrumbs slot (router-agnostic — just a <Link> from react-router, no @wordpress/admin-ui Breadcrumbs to avoid pulling in @wordpress/route and 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.
  • Moves the license-key CTA into AdminPage's actions slot as a single compact secondary "Use license key" button — same pattern as packages/backup/src/js/components/Admin/index.js. No more createInterpolateElement / <a>-in-translation-string boilerplate.
  • Targets #/products for the back-link (the My Jetpack Products tab) instead of the previous /. The reload=true semantics on add-license-screen are preserved.
  • Drive-by: fixes a pre-existing positional-argument bug in 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 uses showHeader={ false }, but it's a different package and the change is independent.
  • The jetpack-admin-page-layout mixin 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_click and jetpack_myjetpack_interstitial_license_link_click events are preserved.

Visual diff

Screen Before (desktop · mobile) After (desktop · mobile)
/add-backup (PricingInterstitial — common path) add-backup-desktop add-backup-mobile add-backup-desktop add-backup-mobile
/add-security (ProductInterstitial — bundle upsell with license CTA) add-security-desktop add-security-mobile add-security-desktop add-security-mobile
/add-license (license activation screen) add-license-desktop add-license-mobile add-license-desktop add-license-mobile
/jetpack-ai (product detail) jetpack-ai-desktop jetpack-ai-mobile jetpack-ai-desktop jetpack-ai-mobile
/protect-details (product detail) protect-details-desktop protect-details-mobile protect-details-desktop protect-details-mobile

Testing instructions

  1. Visit each /wp-admin/admin.php?page=my-jetpack#/add-* route on a Jetpack-connected site.
  2. Confirm the page now has the unified header, with a "← My Jetpack" back-link as the title and a compact "Use license key" button on the right (where applicable).
  3. Click "← My Jetpack" — should land on the Products tab of the dashboard (not the Overview).
  4. On /add-license: activate any pending license, then click the back-link. The reload=true query string should still trigger a refresh of the home view.
  5. On /jetpack-ai and /protect-details: confirm the back navigation works the same way and the page background remains.
  6. Mobile / narrow viewport: header reflows, back-link and actions stack appropriately.

Changelog

  • packages/my-jetpack — patch / changed entry covers both the breadcrumbs and actions changes.

🤖 Generated with Claude Code

CGastrell and others added 3 commits April 30, 2026 12:17
…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>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 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), and enable the feat/mj-interstitials-unified-header branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack feat/mj-interstitials-unified-header

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
Copy link
Copy Markdown
Contributor

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!

@github-actions github-actions Bot added the [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. label Apr 30, 2026
@CGastrell CGastrell changed the title My Jetpack: render interstitials with unified header (breadcrumbs back-link + license-key action) My Jetpack: render interstitials with unified header (breadcrumbs-like back-link + license-key action) Apr 30, 2026
@jp-launch-control
Copy link
Copy Markdown

Code Coverage Summary

This 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. 🤷

Full summary · PHP report · JS report

@CGastrell CGastrell self-assigned this Apr 30, 2026
Comment on lines +59 to +65
breadcrumbs={
<GoBackLink
onClick={ onClickGoBack }
to={ hasActivatedLicense ? '/products?reload=true' : '/products' }
label={ __( 'My Jetpack', 'jetpack-my-jetpack' ) }
/>
}
Copy link
Copy Markdown
Member

@simison simison May 1, 2026

Choose a reason for hiding this comment

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

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

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
Copy link
Copy Markdown
Member

@simison simison May 1, 2026

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member

@simison simison left a comment

Choose a reason for hiding this comment

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

I haven't tested but code is looking good. We'll need to iterate more on this tho but not a blocker.

@CGastrell CGastrell merged commit ffdb89c into trunk May 4, 2026
126 of 127 checks passed
@CGastrell CGastrell deleted the feat/mj-interstitials-unified-header branch May 4, 2026 13:59
@github-actions github-actions Bot removed the [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. label May 4, 2026
Copilot AI pushed a commit to dognose24/jetpack that referenced this pull request May 4, 2026
…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>
CGastrell added a commit that referenced this pull request May 5, 2026
* 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants