Skip to content

feat(emails): sync site styles into WC classic email options + preview thumbnails (NPPD-1537)#4758

Open
kmwilkerson wants to merge 4 commits into
nppd-1524-card-expiry-warning-emailfrom
nppd-1537-sync-site-styles-into-wc-email-options
Open

feat(emails): sync site styles into WC classic email options + preview thumbnails (NPPD-1537)#4758
kmwilkerson wants to merge 4 commits into
nppd-1524-card-expiry-warning-emailfrom
nppd-1537-sync-site-styles-into-wc-email-options

Conversation

@kmwilkerson
Copy link
Copy Markdown

@kmwilkerson kmwilkerson commented May 26, 2026

What this PR does

Two complementary changes that together make the 6 WooCommerce Subscriptions classic-template emails feel native to a Newspack site (NPPD-1537):

1. Site styles sync into WC's classic email options so WC Subs email template doesn't require manual updating.

2. Preview thumbnails for those same emails in the Newspack emails management UI, using the now-branded classic render path.

The 3 WC core emails (Account Created, Refund, New Order) already render with theme.json colors via the block email editor and are unaffected by part 1. They keep their existing block-editor thumbnail path.

image

Depends on slice 2 (#4732) for the Email_Preview class this PR extends, which depends on slice 1 (#4727). Builds on NPPD-1524 (#4756).

Code changes

Backend — includes/plugins/woocommerce/class-woocommerce-email-style-sync.php (new)

  • New WooCommerce_Email_Style_Sync class. Syncs woocommerce_email_base_color (from newspack_get_theme_colors()['primary_color']) and woocommerce_email_header_image (from wp_get_attachment_url( get_theme_mod('custom_logo') )) into WC's classic email options.
  • First-run pattern matches WooCommerce_Emails: option-based version flag (newspack_wc_email_style_sync_version) ensures the sync runs once on first admin load; bumping CURRENT_VERSION re-triggers it.
  • Re-syncs on customize_save_after and after_switch_theme (theme-agnostic hooks that survive a theme switch).
  • Intentionally does NOT sync woocommerce_email_text_color, woocommerce_email_background_color, woocommerce_email_body_background_color — those stay at WC defaults to avoid readability issues. primary_text_color from newspack_get_theme_colors() is contrast-computed against the brand color (not against white), so mapping it to body text would break on sites with dark primaries. Inline code comment documents the decision.
  • Inline note acknowledges that WC's own EmailStyleSync may run on the same hooks when woocommerce_email_auto_sync_with_theme is enabled — last-write-wins is fine since both produce brand-aligned values.

Backend — includes/plugins/woocommerce/class-woocommerce-connection.php

  • Adds include_once for the new style sync class alongside the other WC integration files (matches existing pattern; no change to class-newspack.php).

Backend — includes/wizards/newspack/class-email-preview.php

  • Widens the REST route regex from (?P<post_id>\d+) to (?P<id>[\w:-]+) to accept both numeric (block-editor post) and string (WC email ID, prefixed wc:) identifiers. Single route, no parallel endpoints.
  • Handler validates the wc: ID against the registry BEFORE any resolution. Numeric IDs continue through the existing get_post() branch.
  • New get_wc_classic_preview_html( string $wc_email_id ) method resolves the email ID → WC_Email class name via WC()->mailer()->get_emails(), then uses WC's \Automattic\WooCommerce\Internal\Admin\EmailPreview\EmailPreview::set_email_type() + render(). class_exists() guard handles the case where WC's class moves (it's in WC's Internal namespace — inline comment notes the dependency). Wrapped in try/catch so render failures fall through to the frontend's existing envelope-icon fallback.
  • Classic render path is not cached — render is fast enough that the cache complexity isn't worth it. Block render path keeps its existing transient cache.

Backend — includes/wizards/newspack/class-emails-section.php

  • For WC emails without a block-editor template post (i.e., the 6 WC Subs emails), preview_post_id is now wc:{email_id} instead of null. Frontend can now request a thumbnail for every WC email surfaced in the UI.
  • get_wc_email_template_post_id() changed from private to public static so Email_Preview can use it to decide which render path to take.

Frontend — src/wizards/newspack/views/settings/emails/

  • email-preview.tsx: widens postId prop type to number | string. Existing template-literal path construction handles both shapes — no other changes needed.
  • emails.tsx: adds preview_id?: number | string | null to the EmailItem interface.

Tests

  • PHPUnit (tests/unit-tests/woocommerce-email-style-sync.php, new): 5 tests covering first-run sync of color + logo, skip-when-version-stamped, theme-change re-sync, no-logo fallback to empty string.
  • PHPUnit (tests/unit-tests/email-preview.php, extended): wc: identifier with valid registry entry routes to get_wc_classic_preview_html(); wc: identifier not in registry returns 404; classic render produces non-empty HTML.
  • Jest (email-preview.test.js, extended): string postId constructs correct API path; component renders correctly with string identifier.

Why this PR

The expensive path to fix WC Subs email branding is converting them to WC's block email editor — a multi-month upstream workstream that's not in this project's scope. This is the cheap path: sync the site's brand color and logo into the legacy WC options the classic templates already read, and reuse slice 2's preview infrastructure to render thumbnails via WC's existing EmailPreview class. Combined effect is that WC Subs emails now feel native end-to-end: branded in the management UI AND branded when delivered to readers.

Originally scoped to just the style sync. Thumbnail work was folded in once the investigation confirmed the thumbnail render path could reuse most of slice 2's existing code — better to ship the complete WC Subs branding story in one cohesive PR than two consecutive smaller ones.

Manual testing

Tested locally with Newspack, WooCommerce, WooCommerce Subscriptions, and the WC Block Email Editor feature enabled. Verified:

  • Deleted newspack_wc_email_style_sync_version to simulate first run, loaded any admin page, confirmed wp option get woocommerce_email_base_color matches site primary color and wp option get woocommerce_email_header_image matches site logo URL
  • WooCommerce → Settings → Emails → Email Options reflects synced values
  • Changed primary color in Customizer + saved, verified woocommerce_email_base_color updated via customize_save_after
  • Switched themes, verified resync via after_switch_theme
  • Settings → Emails → Reader revenue chip: all 6 WC Subs emails now show preview thumbnails rendered with site colors and logo (previously blank)
  • WC core emails (Account Created, Refund, New Order) thumbnails continue to render via the block-editor path, unaffected by this PR
  • Toggled an email off/on via the chip UI, confirmed preview still loads correctly

All Submissions:

@kmwilkerson kmwilkerson force-pushed the nppd-1537-sync-site-styles-into-wc-email-options branch from 65ec487 to bdd33a8 Compare May 26, 2026 19:56
@kmwilkerson kmwilkerson marked this pull request as ready for review May 26, 2026 20:09
Copilot AI review requested due to automatic review settings May 26, 2026 20:09
@kmwilkerson kmwilkerson requested a review from a team as a code owner May 26, 2026 20:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves WooCommerce email branding consistency in Newspack by (1) syncing Newspack site branding into WooCommerce classic email options (for classic-template/WC Subscriptions emails) and (2) extending the unified Emails UI preview system to support classic WC emails via wc:{email_id} identifiers, enabling preview thumbnails for those emails.

Changes:

  • Add a WooCommerce classic-email style sync that writes the site primary color + logo into woocommerce_email_* options on first-run and on theme/style changes.
  • Extend the email preview REST endpoint to accept both numeric IDs and wc:{email_id} identifiers, rendering classic-template previews via WC’s legacy preview renderer when needed.
  • Update the Emails UI/types/tests to support string preview identifiers and request previews for all surfaced WooCommerce emails.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
includes/plugins/woocommerce/class-woocommerce-email-style-sync.php New class to sync site primary color + logo into WC classic email options and keep them updated.
includes/plugins/woocommerce/class-woocommerce-connection.php Includes the new style-sync integration file during WooCommerce connection init.
includes/wizards/newspack/class-email-preview.php Expands preview REST route to accept wc: IDs and adds classic WC preview rendering path.
includes/wizards/newspack/class-emails-section.php Populates preview_id for WC emails (post ID when block template exists, otherwise wc:{email_id}) and exposes template lookup helper publicly.
src/wizards/newspack/views/settings/emails/email-preview.tsx Allows postId to be `number
src/wizards/newspack/views/settings/emails/emails.tsx Renames preview_post_idpreview_id and supports string IDs for previews.
src/wizards/newspack/views/settings/emails/emails.test.js Updates mock WC rows to provide preview_id for preview rendering.
src/wizards/newspack/views/settings/emails/email-preview.test.js Adds tests for string wc: preview IDs and endpoint path construction.
tests/unit-tests/woocommerce-email-style-sync.php New PHPUnit suite for the WC style-sync helpers/guards in CI contexts.
tests/unit-tests/email-preview.php Updates tests for REST param rename (id) and adds coverage for unregistered wc: preview IDs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread includes/wizards/newspack/class-email-preview.php
Comment thread tests/unit-tests/woocommerce-email-style-sync.php
Comment thread tests/unit-tests/woocommerce-email-style-sync.php
@kmwilkerson kmwilkerson force-pushed the nppd-1524-card-expiry-warning-email branch from facde84 to 98b0e2d Compare May 26, 2026 20:39
@kmwilkerson kmwilkerson force-pushed the nppd-1537-sync-site-styles-into-wc-email-options branch from f9e5b74 to 3410271 Compare May 26, 2026 20:39
@kmwilkerson kmwilkerson force-pushed the nppd-1524-card-expiry-warning-email branch from 98b0e2d to 05ae747 Compare May 26, 2026 23:17
@kmwilkerson kmwilkerson force-pushed the nppd-1537-sync-site-styles-into-wc-email-options branch from 3410271 to 643cb6d Compare May 26, 2026 23:21
kmwilkerson and others added 4 commits May 26, 2026 18:38
…w thumbnails (NPPD-1537)

Sync the site's primary brand color and logo into WooCommerce's classic
email template options so WC Subs transactional emails arrive on-brand.
Extend the email preview REST endpoint to accept wc:{email_id} string
identifiers alongside numeric post IDs, enabling preview thumbnails for
classic-template WC emails that lack block-editor template posts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…st suites

Remove the global WooCommerce class stub that was causing
Emails_Section tests to think WC was available (then fail on WC()).
Test private helpers via Reflection instead.

Also unhook Emails::maybe_update_email_templates during set_up to
prevent set_theme_mod from calling the missing
Newspack_Newsletters::update_color_palette.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Tighten the email preview route regex from `[\w:-]+` to `\d+|wc:[\w-]+`
  so non-digit identifiers like `123abc` can't slip past `absint()` and
  resolve to a different email post than was requested.
- Restore the `update_option_theme_mods_{theme}` action in `tear_down()`
  of the style-sync test suite, so the deliberate removal in `set_up()`
  doesn't leak into other test classes via shared global hook state.
The test asserted that no WC email options were written when the version
option was already current, but in CI `class_exists( 'WooCommerce' )` is
false so `maybe_sync_on_first_run()` short-circuits on the WC guard before
ever checking the version — the assertion passed for the wrong reason. The
version check is one obvious line and adding a testing-only helper to
exercise it isn't worth the churn.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants