Skip to content

Newsletter: settings on WPDS Card primitives (PR 5 / #48530)#48608

Open
keoshi wants to merge 7 commits intotry/newsletter-modernization-tabsfrom
try/newsletter-modernization-wpds-cards
Open

Newsletter: settings on WPDS Card primitives (PR 5 / #48530)#48608
keoshi wants to merge 7 commits intotry/newsletter-modernization-tabsfrom
try/newsletter-modernization-wpds-cards

Conversation

@keoshi
Copy link
Copy Markdown
Contributor

@keoshi keoshi commented May 7, 2026

Part of #48530 — fifth of six PRs to modernize Jetpack's Newsletter into a unified wp-admin product. Stacked on #48607 (Subscribers/Settings tabs). Once #48607 merges, GitHub auto-flips this PR's base to trunk.

This PR started as a pure WPDS Card migration but absorbed the missing settings-presentation work that was in the prototype (#48420) — placement cards, IA reorder, and per-toggle Tracks — so the modernization tree lands on the same shape the prototype settled on rather than catching that up in a follow-up.

Proposed changes

WPDS Card migration

  • Convert all settings sections from @wordpress/components's Card / CardHeader / CardBody / CardFooter to Card.Root / Card.Header / Card.Title / Card.Content from @wordpress/ui. __experimentalHeading (the legacy heading inside the header) becomes Card.Title; __experimentalText becomes WPDS Text. Section internals — DataForm fields, save handlers, change tracking — stay byte-for-byte identical.
  • WPDS Card doesn't ship a Footer slot, so the row that used to live inside <CardFooter> becomes a <div className="newsletter-card-footer"> at the bottom of <Card.Content>. Save-button sections right-align the button via justify-content: flex-end; multi-link footers (e.g. the Email-defaults privacy + manage links before they were dropped) bracket via a :has(> :nth-child(2)) rule that switches to space-between only when there's more than one child.
  • Save and action buttons (Save, Add plans / Manage plans) opt into __next40pxDefaultSize so they ship the WordPress 7 default height ahead of the upgrade.
  • Space cards 24px apart (Stack gap="xl"--wpds-dimension-gap-xl) inside NewsletterSettingsBody, and add a 32px (--wpds-dimension-padding-3xl) padding-block-end on .newsletter-settings so the last card has breathing room above the page bottom.

Subscribe-placement cards (ported from #48420)

  • Add PlacementCard (sections/placement-card.{tsx,scss}) — a selectable card composed from <label htmlFor> + CheckboxControl, a 16:9-ish SVG wireframe slot, a caption with an optional "Preview and edit" link, and a brand-toned active-stroke driven by data-checked="true".
  • Ship four inline 294×192 SVG illustrations (OverlayIllustration, PopupIllustration, EndOfPostIllustration, FloatingIllustration) in sections/placement-illustrations.tsx — pure vector, no external assets.
  • SubscriptionsSection swaps the four "Homepage and posts" placement booleans from a flat DataForm of ToggleWithEditorLink rows to a 2×2 PlacementCard grid. Navigation and Comments stay as inline ToggleControl subgroups inside the same card, with editor "Preview and edit" links rendered next to the relevant labels.

IA reorder + Email defaults rename

  • Drop the legacy NewsletterSection master subscriptions toggle — it moves to the global Newsletter module activation control. The remaining wpcom_newsletter_send_default toggle becomes its own focused card called Email defaults, located inside the email cluster instead of opening the page.
  • Rename sections/newsletter-section.tsxsections/email-defaults-section.tsx (and NewsletterSectionEmailDefaultsSection); update sections/index.ts accordingly.
  • Drop the privacy-info / manage-subscribers footer links from the renamed card — privacy info already lives in the standard product flow, manage-subscribers is one click away on the Subscribers tab.
  • New IA inside newsletter-settings.tsx: Paid → Subscriptions → Email defaults → Email content → Email byline → Email sender settings → Email reply-to settings → Welcome email → Newsletter categories. Everything sits inside the same <Disabled isDisabled={ ! data.subscriptions }> boundary now that the always-editable master toggle is gone.

Telemetry deltas

  • New events tagged with site_type + a stable placement slug (overlay / modal / post_end / floating_button):
    • jetpack_newsletter_placement_toggle fires on every placement-card flip alongside the state update.
    • jetpack_newsletter_placement_preview_click fires when the user clicks a card's "Preview and edit" link.
  • Existing jetpack_newsletter_section_save payloads gain changed_keys (comma-joined) + change_count, sourced from the orchestrator's per-section changeset (subscriptionChanges, senderNameChanges, newsletterCategoriesChanges, welcomeEmailChangesObject.keys()). Each affected section accepts an optional changedKeys: string[] prop.

Note: because #48607 lifted the section body out of the legacy chrome (NewsletterSettingsBody is shared between the legacy NewsletterSettingsApp wrapper and the modernized chassis), this refactor lights up on both surfaces — the legacy Newsletter Settings page also gains the WPDS Card primitives, the placement grid, and the IA reorder. The legacy page is being retired in #48613.

All gated behind rsm_jetpack_ui_modernization_newsletter for the chassis itself. The settings body isn't gated, by design.

Related product discussion/links

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

  • No new REST surface.
  • Two new Tracks events (jetpack_newsletter_placement_toggle, jetpack_newsletter_placement_preview_click) and two new keys on the existing jetpack_newsletter_section_save event (changed_keys, change_count). All payloads carry site_type + a stable placement slug — no new identifiers or PII.
  • No changes to privacy notices.
  • add_script_data payload. No new keys.

Testing instructions

Pull this branch, run pnpm install and pnpm jetpack build packages/newsletter --deps. Then verify both flag states.

Flag OFF (legacy mode)

  • Make sure no add_filter( 'rsm_jetpack_ui_modernization_newsletter', ... ) exists in any mu-plugin / wp-config.php / theme.
  • Visit wp-admin/admin.php?page=jetpack-newsletter.
  • The legacy Jetpack-styled AdminPage chrome still wraps the settings body. Inside, every card now renders with WPDS Card primitives, the four homepage placements show as a 2×2 grid of selectable cards with illustrations + "Preview and edit" links, and the order matches the new IA above.
  • The "Newsletter" master toggle no longer surfaces here — flip the Newsletter module via Jetpack's main module settings if you need to disable everything.
  • All save flows round-trip: flip a placement → click Save in the Subscriptions card → success snackbar → reload → setting persists.
  • Auto-save fields (Email defaults toggle, Email content, Email byline, Reply-to settings) flip → success snackbar → reload → settings persist.

Flag ON (modernized mode)

  • Add add_filter( 'rsm_jetpack_ui_modernization_newsletter', '__return_true' ); to a mu-plugin or Code Snippet.
  • Hard-refresh wp-admin/admin.php?page=jetpack-newsletter. Click Settings in the tab nav.
  • Same WPDS Card layout, same placement grid, same IA, same save-flow behaviour as above.
  • DevTools → Network → filter pixel.wp.com. Toggling a placement fires jetpack_newsletter_placement_toggle; clicking its "Preview and edit" link fires jetpack_newsletter_placement_preview_click; saving any manual-save section fires jetpack_newsletter_section_save with changed_keys listing the keys actually flipped in that batch.

Toggle round-trip

  • Comment out the modernization filter and refresh — legacy chrome returns; the inner cards still use WPDS Card + the placement grid. Re-enable and refresh — modernized chassis returns. Both directions reversible without cache clears.

Screenshots

Top of the Settings tab — new IA (Paid first, Subscriptions card with placement grid below)

Settings tab top

Subscribe-placement grid inside the Subscriptions card

Placement grid

Save button on the Sender settings card (40px, right-aligned)

Sender settings save

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 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 try/newsletter-modernization-wpds-cards branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack try/newsletter-modernization-wpds-cards
bin/jetpack-downloader test jetpack-mu-wpcom-plugin try/newsletter-modernization-wpds-cards

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

github-actions Bot commented May 7, 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!

@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 May 7, 2026
@jp-launch-control
Copy link
Copy Markdown

jp-launch-control Bot commented May 7, 2026

Code Coverage Summary

Cannot generate coverage summary while tests are failing. 🤐

Please fix the tests, or re-run the Code coverage job if it was something being flaky.

Full summary · PHP report · JS report

@keoshi keoshi force-pushed the try/newsletter-modernization-wpds-cards branch from 9a65295 to 731cc0d Compare May 7, 2026 15:33
@keoshi keoshi changed the title Newsletter: settings on WPDS Card primitives (PR 5 / #48530) Newsletter: settings on WPDS Card primitives May 7, 2026
@keoshi keoshi added [Status] Needs Review This PR is ready for review. and removed [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. labels May 7, 2026
@keoshi keoshi self-assigned this May 7, 2026
@keoshi keoshi requested review from CGastrell, dhasilva and ilonagl May 7, 2026 15:37
keoshi added 3 commits May 7, 2026 11:46
Move all 9 settings sections from `@wordpress/components`'s
`Card` / `CardHeader` / `CardBody` / `CardFooter` over to
`Card.Root` / `Card.Header` / `Card.Title` / `Card.Content` from
`@wordpress/ui`. `__experimentalHeading` (legacy heading inside the
header) becomes `Card.Title`; `__experimentalText` becomes WPDS
`Text`. Section internals — DataForm fields, save handlers, change
tracking — stay byte-for-byte identical.

WPDS `Card` doesn't ship a `Footer` slot, so the row that used to
live inside `<CardFooter>` becomes a `<div className="newsletter-card-footer">`
at the bottom of `<Card.Content>`. The shared style:
* save-button rows (Subscriptions, Newsletter categories, Sender
  settings, Welcome email) right-align via `justify-content: flex-end`,
* the Newsletter section's two-link row (Privacy + Manage subscribers)
  brackets via a `:has(> :nth-child(2))` rule that switches to
  `space-between` only when there's more than one child.

Save and action buttons (`Save`, `Add plans` / `Manage plans`) opt
into `__next40pxDefaultSize` so they ship the WordPress 7 default
height ahead of the upgrade. Pure refactor — no behaviour changes.
…elow

The settings body stacked its cards with `gap="md"` (12px), which
left them sitting too close together on the modernized chassis once
they got the new WPDS Card chrome. Switch the inner and outer
`Stack`s to `gap="xl"` so each card pair sits exactly 24px apart
via `--wpds-dimension-gap-xl` — same rhythm as the page-header
padding inset, no magic numbers.

Add a `padding-block-end` of `--wpds-dimension-padding-3xl` (32px)
to `.newsletter-settings` so the last card has room before the
page bottom on overflow scroll.
@keoshi keoshi force-pushed the try/newsletter-modernization-wpds-cards branch from 731cc0d to 11b2ac7 Compare May 7, 2026 15:46
@keoshi keoshi changed the title Newsletter: settings on WPDS Card primitives Newsletter: settings on WPDS Card primitives (PR 5 / #48530) May 7, 2026
keoshi added 4 commits May 7, 2026 12:52
Bring the prototype's placement-card surface back: a selectable card
composed from `<label htmlFor>` + `CheckboxControl`, a 16:9-ish SVG
wireframe slot, a caption with an optional "Preview and edit" link,
and a brand-toned active-stroke driven by `data-checked="true"`.
Ported verbatim — same component API the prototype settled on so the
SubscriptionsSection can swap its flat `ToggleWithEditorLink` rows for
a 2×2 grid of cards in the next commit.

The four illustration components (`OverlayIllustration`,
`PopupIllustration`, `EndOfPostIllustration`, `FloatingIllustration`)
ship as inline 294×192 SVG wireframes — pure vector, no external
assets. Subgroup heading and `<fieldset>` reset styles join the
package stylesheet alongside the placement-card scss for the
neighbouring Navigation / Comments groups.
Replace the flat `ToggleWithEditorLink` rows in the Subscriptions card
with a 2×2 grid of `PlacementCard`s — overlay, modal, end-of-post,
floating button — each backed by its own SVG wireframe and an optional
"Preview and edit" link to the matching site-editor template part.
Navigation and Comments stay as inline `ToggleControl` subgroups
inside the same card so the whole subscription configuration reads
without scrolling between cards.

Telemetry catches up with the prototype:

* `jetpack_newsletter_placement_toggle` fires on every placement
  card flip, tagged with the placement slug + new value.
* `jetpack_newsletter_placement_preview_click` fires when the user
  clicks a card's preview link.
* `jetpack_newsletter_section_save` payload gains
  `changed_keys` / `change_count` so we can see what's actually in
  each user's batch instead of just the section name. The orchestrator
  will start passing the staged keys in the next commit.

The section accepts an optional `changedKeys` prop (still optional
for now so the orchestrator wiring lands separately without breaking
the build) and falls back to an empty array if the parent doesn't
supply it.
The legacy `NewsletterSection` doubled as a master `subscriptions`
toggle and a "send new posts as newsletters by default" toggle.
Now that the master toggle moves to the global Newsletter module
activation control, drop it from this UI and reshape the surviving
`wpcom_newsletter_send_default` field into a focused, email-themed
card called **Email defaults**.

While the section was being reshaped:

* Drop the "Privacy information" + "Manage all subscribers" footer
  links — they don't fit the email-defaults framing, and the latter is
  redundant with the Subscribers tab now living one click away.
* Rename the file to `sections/email-defaults-section.tsx` and the
  exported component to `EmailDefaultsSection`. Update
  `sections/index.ts` accordingly.

Reshuffle the IA in `newsletter-settings.tsx` to walk users from
activation flavour → distribution → email behaviour → categorisation:

  Paid → Subscriptions → Email defaults → Email content → Email byline
  → Email sender settings → Email reply-to settings → Welcome email →
  Newsletter categories.

Drop the always-editable wrapper around the deleted
`NewsletterSection`; everything now sits inside the same
`<Disabled isDisabled={ ! data.subscriptions }>` boundary that gates
the rest of the page on module status.
Subscriptions, Sender, Categories, and Welcome each fire a
`jetpack_newsletter_section_save` Tracks event when the user clicks
their Save button. Until now those events only carried the section
name — useful for counts but not for understanding which fields users
actually flip together inside a single batch.

Each section now accepts an optional `changedKeys: string[]` prop and
forwards it as `changed_keys` (comma-joined) + `change_count` on the
save event. The orchestrator wires it from the changeset state it
already maintains for each section's manual save (`subscriptionChanges`,
`senderNameChanges`, `newsletterCategoriesChanges`,
`welcomeEmailChanges` → `Object.keys()`).
*/
export function OverlayIllustration(): JSX.Element {
return (
<svg
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.

Could use SVG component from @wordpress/primitives for all SVG, and their inner elements.

Notice,
__experimentalHeading as Heading, // eslint-disable-line @wordpress/no-unsafe-wp-apis
} from '@wordpress/components';
import { Notice } from '@wordpress/components';
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.

We have Notice component at WP UI but you might've intentionally left it for another PR.

__experimentalText as Text, // eslint-disable-line @wordpress/no-unsafe-wp-apis
__experimentalHeading as Heading, // eslint-disable-line @wordpress/no-unsafe-wp-apis
} from '@wordpress/components';
import { Button } from '@wordpress/components';
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.

There is Button in WP UI but you might've intentionally left out in this PR.

}
}

.jetpack-newsletter-placement {
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.

Can be replaced with Stack component.

gap: var(--wpds-dimension-gap-sm, 8px);
}

.jetpack-newsletter-placement__card {
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 using Card component without Card.Content element or with Card.Content + Card.FullBleed work here as basis for the tile?

// 12px-from-edges absolute wrapper. Zero them out so the visible
// checkbox box is exactly equidistant from the card's top and inline
// end edges.
.components-base-control,
Copy link
Copy Markdown
Member

@simison simison May 8, 2026

Choose a reason for hiding this comment

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

Can we do this without overwriting styles on components using core selectors?

gap: var(--wpds-dimension-gap-xs, 4px);
}

.jetpack-newsletter-placement__title {
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.

Can likely replace with Text component.

}
}

.jetpack-newsletter-placement__caption {
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.

Can replace with Stack component.


.jetpack-newsletter-placement__surface {
display: block;
cursor: pointer;
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.

Pointer is also in WPDS tokens available.

border-radius: var(--wpds-border-radius-md, 8px);
background: var(--wpds-color-bg-surface-neutral-strong, #fff);
overflow: hidden;
transition: border-color 0.15s ease-out;
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.

Let's apply only when "not prefers reduced motion" media query applies.

https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-reduced-motion

border-color: var(--wpds-color-stroke-surface-neutral-strong, #b0b0b0);
}

// Focus ring on the card itself (not the checkbox input), using the
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.

This gave me idea to componentize focus rings in DS. 🤔

gap: var(--wpds-dimension-gap-sm, 8px);
}

.jetpack-newsletter-subgroup__title {
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.

Can be replaced by Text component.

// Comments). Each subgroup carries a small heading and a vertical Stack
// of toggles; the heading sits on its own row so multi-line toggle labels
// stay aligned.
.jetpack-newsletter-subgroup {
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.

Can be replaced by Stack component.

// Reset the native `<fieldset>` border / padding so the visual cluster
// inside Card.Content is driven by the inner Stacks alone — the fieldset
// only carries the `disabled` semantic for assistive tech.
.jetpack-newsletter-section__fieldset {
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.

Check if fieldset form primitive is yet available from WP Ui's latest version.

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.

Haven't looked more closely visually but remember to test mobile. I left a bunch of comments that should point AI to right direction with components. 🙂

(Likely typos because of mobile)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants