Newsletter: settings on WPDS Card primitives (PR 5 / #48530)#48608
Newsletter: settings on WPDS Card primitives (PR 5 / #48530)#48608keoshi wants to merge 7 commits intotry/newsletter-modernization-tabsfrom
Conversation
|
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 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. |
9a65295 to
731cc0d
Compare
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.
731cc0d to
11b2ac7
Compare
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 |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
There is Button in WP UI but you might've intentionally left out in this PR.
| } | ||
| } | ||
|
|
||
| .jetpack-newsletter-placement { |
There was a problem hiding this comment.
Can be replaced with Stack component.
| gap: var(--wpds-dimension-gap-sm, 8px); | ||
| } | ||
|
|
||
| .jetpack-newsletter-placement__card { |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
Can we do this without overwriting styles on components using core selectors?
| gap: var(--wpds-dimension-gap-xs, 4px); | ||
| } | ||
|
|
||
| .jetpack-newsletter-placement__title { |
There was a problem hiding this comment.
Can likely replace with Text component.
| } | ||
| } | ||
|
|
||
| .jetpack-newsletter-placement__caption { |
There was a problem hiding this comment.
Can replace with Stack component.
|
|
||
| .jetpack-newsletter-placement__surface { | ||
| display: block; | ||
| cursor: pointer; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
This gave me idea to componentize focus rings in DS. 🤔
| gap: var(--wpds-dimension-gap-sm, 8px); | ||
| } | ||
|
|
||
| .jetpack-newsletter-subgroup__title { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
Check if fieldset form primitive is yet available from WP Ui's latest version.
simison
left a comment
There was a problem hiding this comment.
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)
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
Cardmigration 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
@wordpress/components'sCard/CardHeader/CardBody/CardFootertoCard.Root/Card.Header/Card.Title/Card.Contentfrom@wordpress/ui.__experimentalHeading(the legacy heading inside the header) becomesCard.Title;__experimentalTextbecomes WPDSText. Section internals — DataForm fields, save handlers, change tracking — stay byte-for-byte identical.Carddoesn't ship aFooterslot, 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 viajustify-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 tospace-betweenonly when there's more than one child.Save,Add plans/Manage plans) opt into__next40pxDefaultSizeso they ship the WordPress 7 default height ahead of the upgrade.Stack gap="xl"→--wpds-dimension-gap-xl) insideNewsletterSettingsBody, and add a 32px (--wpds-dimension-padding-3xl)padding-block-endon.newsletter-settingsso the last card has breathing room above the page bottom.Subscribe-placement cards (ported from #48420)
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 bydata-checked="true".OverlayIllustration,PopupIllustration,EndOfPostIllustration,FloatingIllustration) insections/placement-illustrations.tsx— pure vector, no external assets.SubscriptionsSectionswaps the four "Homepage and posts" placement booleans from a flatDataFormofToggleWithEditorLinkrows to a 2×2PlacementCardgrid. Navigation and Comments stay as inlineToggleControlsubgroups inside the same card, with editor "Preview and edit" links rendered next to the relevant labels.IA reorder + Email defaults rename
NewsletterSectionmastersubscriptionstoggle — it moves to the global Newsletter module activation control. The remainingwpcom_newsletter_send_defaulttoggle becomes its own focused card called Email defaults, located inside the email cluster instead of opening the page.sections/newsletter-section.tsx→sections/email-defaults-section.tsx(andNewsletterSection→EmailDefaultsSection); updatesections/index.tsaccordingly.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
site_type+ a stableplacementslug (overlay / modal / post_end / floating_button):jetpack_newsletter_placement_togglefires on every placement-card flip alongside the state update.jetpack_newsletter_placement_preview_clickfires when the user clicks a card's "Preview and edit" link.jetpack_newsletter_section_savepayloads gainchanged_keys(comma-joined) +change_count, sourced from the orchestrator's per-section changeset (subscriptionChanges,senderNameChanges,newsletterCategoriesChanges,welcomeEmailChanges→Object.keys()). Each affected section accepts an optionalchangedKeys: string[]prop.All gated behind
rsm_jetpack_ui_modernization_newsletterfor the chassis itself. The settings body isn't gated, by design.Related product discussion/links
Card/Tabs:@wordpress/uiDoes this pull request change what data or activity we track or use?
jetpack_newsletter_placement_toggle,jetpack_newsletter_placement_preview_click) and two new keys on the existingjetpack_newsletter_section_saveevent (changed_keys,change_count). All payloads carrysite_type+ a stable placement slug — no new identifiers or PII.add_script_datapayload. No new keys.Testing instructions
Pull this branch, run
pnpm installandpnpm jetpack build packages/newsletter --deps. Then verify both flag states.Flag OFF (legacy mode)
add_filter( 'rsm_jetpack_ui_modernization_newsletter', ... )exists in any mu-plugin /wp-config.php/ theme.wp-admin/admin.php?page=jetpack-newsletter.AdminPagechrome 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.Flag ON (modernized mode)
add_filter( 'rsm_jetpack_ui_modernization_newsletter', '__return_true' );to a mu-plugin or Code Snippet.wp-admin/admin.php?page=jetpack-newsletter. Click Settings in the tab nav.pixel.wp.com. Toggling a placement firesjetpack_newsletter_placement_toggle; clicking its "Preview and edit" link firesjetpack_newsletter_placement_preview_click; saving any manual-save section firesjetpack_newsletter_section_savewithchanged_keyslisting the keys actually flipped in that batch.Toggle round-trip
Screenshots
Top of the Settings tab — new IA (Paid first, Subscriptions card with placement grid below)
Subscribe-placement grid inside the Subscriptions card
Save button on the Sender settings card (40px, right-aligned)