feat(my-account): v2 prototype#4679
Conversation
Phase 1 plumbing for the My Account v2 prototype. Adds a new
`Newspack\My_Account_UI_V2_Demo` class that gates a stub on
`is_account_page()` + admin + `?v2-demo`, sets a scoped body class,
enqueues a dedicated webpack bundle (`my-account-v2-demo`), and
short-circuits to a no-op for everyone else. Loaded after the v1
class so v1's filters register first.
Bundles in `src/my-account/v2-demo/{index.js,style.scss}`. SCSS is
intentionally empty under the `.newspack-my-account--v2-demo` scope
— newspack-ui primitives drive the visuals in later phases.
Brief and dev log live under `docs/my-account-v2-prototype-*.md`.
There was a problem hiding this comment.
Pull request overview
This PR introduces Phase 1 “plumbing” for an admin-only My Account v2 prototype demo gate, activated by the ?v2-demo query flag on WooCommerce /my-account/ URLs, without changing behavior for non-admins or requests without the flag.
Changes:
- Adds a new webpack entry + stub JS/SCSS bundle for
my-account-v2-demo. - Loads a new PHP gate class that (when active) adds a scoped body class, enqueues the v2-demo bundle, and renders a simple “hello” stub in the My Account content.
- Adds initial prototype brief + devlog documentation.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
webpack.config.js |
Registers the new my-account-v2-demo build entry. |
src/my-account/v2-demo/index.js |
Adds minimal v2-demo entrypoint (public-path + scoped stylesheet + verification log). |
src/my-account/v2-demo/style.scss |
Adds an empty, fully-scoped styling wrapper for demo isolation. |
includes/plugins/woocommerce/my-account/class-woocommerce-my-account.php |
Includes the v2-demo gate class after v1 in the v1 branch. |
includes/plugins/woocommerce/my-account/class-my-account-ui-v2-demo.php |
Implements the admin-only ?v2-demo gate (query var/body class/assets/stub render). |
docs/my-account-v2-prototype-devlog.md |
Adds a devlog template + Phase 1 entry. |
docs/my-account-v2-prototype-brief.md |
Adds the detailed design/dev brief for the prototype phases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address Copilot review on #4679: - Inline comment in class-woocommerce-my-account.php now reads as forward-tense ("later phases re-filter…") rather than implying Phase 1 already does the filter override work. - Brief §4 now points at class-woocommerce-my-account.php for the include location, matching §10 Step 3.
Captures the sync conclusions that aren't otherwise in the brief or the code: auto-flush guard strategy, single-PR delivery, prototype-removal cleanup obligations, and the Phase 2 deferral of the login-redirect query-string strip.
Replaces the Phase 1 stub with the real Newsletters surface — grouped sections, frequency badges, optional SUBSCRIBER-ONLY badges, and client-side optimistic Sign up / Unsubscribe flow with snackbar confirmations. Built entirely from newspack-ui primitives, gated on ?v2-demo and admin capability so non-admins see v1 unchanged. - Register `newsletters` rewrite endpoint with the option-keyed auto-flush guard from the cross-phase decision log; bump ENDPOINTS_VERSION to 2. - Hook `woocommerce_account_newsletters_endpoint` to render the v2 template via load_template; skip wc_get_template since myaccount/newsletters.php isn't a real WC template path. - v2-only "Newsletters" menu item via woocommerce_account_menu_items at priority 1100 (after v1's 1001). - Preserve ?v2-demo on every internal nav link via woocommerce_get_endpoint_url filter, closing the Phase 1 login-redirect query-string-strip open question. - PHP fake data on the class as get_fake_data(), shipped to JS via wp_localize_script and to templates by passing into load_template. - No scoped CSS — row layout composes from nested __stack (horizontal + vertical), --justify-between for left/right pinning, <img width height> attrs for thumbnail size, and <hr> between vertical-stack siblings for the hairline separator. style.scss stays wrapper-only.
b948257 to
71786f3
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Render snackbar markup directly with setTimeout cleanup instead of routing through newspackUI.notices.openNotice — the latter calls closeNotice on dismiss, which always sends an admin-ajax dismissal using a server-issued nonce we don't have, producing a 403 on every toast. - Redirect non-demo visitors hitting /my-account/newsletters/ to /my-account/edit-account/ via template_redirect at priority 9 so they never land on an empty account body. Use get_query_var(), not is_wc_endpoint_url() (which only matches WC's hardcoded list). - Enqueue the JS bundle using deps from dist/my-account-v2-demo.asset.php so @wordpress/i18n is loaded automatically. Matches the canonical pattern used elsewhere in this repo (e.g. trait-content-gate-layout). - Drop role="list" / role="listitem" from the newsletters template — <hr> separators between rows would break ARIA list semantics, and we're not actually conveying list semantics with <div>/<button> children. Visual grouping is enough. - Update the brief: bump picsum reference from 128×128 to 144×144 to match the retina rationale, and correct the §6 mapping-table row for SUBSCRIBER-ONLY (Figma uses --secondary, not --outline).
Adds the Donations list and detail surfaces to the My Account v2 prototype, gated behind ?v2-demo. Registers a `donations` rewrite endpoint with a value parameter (`/my-account/donations/<id>/`), bumps ENDPOINTS_VERSION to 3 to re-trigger the auto-flush guard, and renders both list (recurring + previous + billing-history button) and detail (recurring active / cancelled, one-time, no-fees) variants from pure newspack-ui composition. See docs/my-account-v2-prototype-devlog.md.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- donation-details: replace remaining inline `style="flex:1"` on date rows + payment-method with `__stack--justify-between` (matches the Amount section refactor — pure newspack-ui composition). - donations list: add `tabindex="0"`, `role="link"`, and a descriptive `aria-label` to clickable `<tr>`s; wire keyboard activation (Enter/Space) in `donations.js` so rows are reachable without a mouse. - donations.js: wire a stub snackbar for the "Billing history" Button Card so the click feels intentional ahead of Phase 6's `billing_history_inline` scenario fixture. - demo-flag preservation: read the existing `$_GET[ self::DEMO_FLAG ]` value rather than forcing `'1'`, so navigation keeps the chosen scenario (e.g. `?v2-demo=cancelled-sub` per brief §7). - register_endpoints: gate the flush behind `is_user_logged_in()` + `manage_options` so anonymous frontend traffic doesn't pay the ~50ms rule-regeneration cost. The first admin to hit any /my-account/ page after a deploy triggers it; rules apply for everyone after that.
* feat(my-account): build v2 prototype payment methods (Phase 6) Reproduces WC core's /my-account/payment-methods/ page byte-for-byte under the ?v2-demo flag, fed by fake data instead of wc_get_customer_saved_methods_list. Same template_redirect priority-8 takeover pattern Phase 4 uses for subscriptions: drops both WC core's default renderer and v1's wc_get_template swap to payment-information.php so the v2 template is the sole handler. - Adds 'payment-methods' to the v2 sidebar between Subscriptions and the rest - Bumps ENDPOINTS_VERSION 4 -> 5 to re-fire the auto-flush guard once - Snackbar-only dispatcher (Make default / Delete / Add new) — no modals * fix(my-account): address Copilot review on Phase 6
) * feat(my-account): build v2 prototype scenarios + polish (Phase 7) Wires the `?v2-demo=<scenario>` mechanism (11 scenarios), retires the `extras` subscription bucket in favour of a fixture registry, and sweeps the open-question polish backlog accumulated across Phases 3–6 (hash fragments, "The News Paper" hardcodes). Adds an inline Expired badge to the payment-methods table for the `expired-payment` scenario, mirroring v1's parse-MM/YY-then-emit-badge pattern. Empty scenarios surface a newspack-ui notice on the donations + subscriptions list pages. Brief gets a new Phase 8 — Documentation pass (reader's guide, architectural map, productionisation playbook) so the next person to touch the prototype has a single guide-shaped doc to start from. * fix(my-account): address Copilot review on Phase 7 - empty_payment_methods() now returns [] (was [ 'cc' => [] ]) so no-payment-methods / empty scenarios surface WC core's "No saved methods found." notice instead of an empty-tbody table. - expired-payment falls back to index 0 when every card is default, so the scenario is deterministic regardless of the underlying card mix. - Expired-badge detection uses date_parse_from_format( 'n/y', … ) with parse-error checks, mirroring v1's payment-information template. Malformed values (02/ab, 13/27, 00/27, empty) no longer false-positive.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 42 out of 43 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Homepage overlay close: add visibility:hidden + toggle aria-hidden on close so the closed dialog is no longer focusable / discoverable by screen readers (was only opacity:0 + pointer-events:none). - Payment-methods template: ksort the actions array directly instead of computing a sorted-but-unused $action_keys variable; matches v1. - Edit-address modal: pair Town/City (form-row-first) with County (form-row-last), promote Post code to form-row-wide so the WC float grid stops breaking on first/first/last.
redirect_non_demo_v2_endpoints() previously bounced any non-demo /my-account/newsletters/ request to /edit-account/, which would break the real newsletters endpoint on sites that have newspack-newsletters installed (its Subscription class owns woocommerce_account_newsletters_endpoint at priority 10). Mirror the existing WCS guard for /subscriptions/: only redirect /newsletters/ when no real handler is present, gated by class_exists( 'Newspack_Newsletters_Subscription' ). Donations keeps unconditional redirect (no real handler exists outside this prototype). Subscriptions WCS guard intact.
Bring the guide back in sync with the current implementation:
- edit-account.{php,js} → account-settings.{php,js} (file tree,
walkthrough, hook table)
- render_homepage_drawer → render_homepage_overlay
- Homepage body class: newspack-my-account--my-account-v2-demo-homepage
→ newspack-my-account-v2-demo-homepage
- Body-class cleanup note: newspack-my-account--my-account-v2-demo
→ newspack-my-account-v2-demo
- Newsletters walkthrough: state now persists via localStorage
(key newspack-my-account-v2-demo:newsletters), not reset-on-reload
All Submissions:
Changes proposed in this Pull Request:
Lands the My Account v2 design-review prototype — an admin-gated, fake-data, clickable build of the redesigned
/my-account/experience, plus a small WP-admin companion. It exists so stakeholders can actually use the redesign during review on any Newspack site, without touching what real readers see.Figma: My Account — i5 (Final)
How it works (mental model in 60 seconds)
?my-account-v2-demo=1to any/my-account/...URL (or to the homepage). The prototype renders.is_account_page() + logged-in + manage_options + ?my-account-v2-demo. Non-admins get v1 unchanged. No-flag admins also get v1 unchanged.My_Account_V2_Demo::get_fake_data()method feeds every template and JS module. Real readers, orders, subscriptions, and payment methods are never touched. You can demo this on a brand-new site with zero donations or subs configured.localStorage; everything else resets./wp-admin/admin.php?page=newspack-audience#/reader-account-customization) shows publishers what the v2 customisation controls would look like. Pure-demo: local React state, Save is a no-op. Doesn't need the?my-account-v2-demoflag — admin caps gate the whole wizard already.Quick start — demo it in 30 seconds
prototype/my-account-demointo a Newspack site with WooCommerce (and ideally WC Subscriptions).n npm run build(orn npm startfor watch)./my-account/?my-account-v2-demo=1./wp-admin/admin.php?page=newspack-audience#/reader-account-customization.Demo walkthrough
A suggested running order for a stakeholder review. Each surface notes what's clickable and what just fires a snackbar.
1. Sidebar + entry —
/my-account/?my-account-v2-demo=1The shell. Sidebar shows Account details / Newsletters / Donations / Subscriptions / Payment information (in that order). Dashboard body is v1 unchanged — all the v2 work lives behind the sidebar items.
2. Newsletters —
/my-account/newsletters/?my-account-v2-demo=1Sectioned list (Featured / Technology / Subscriber-only) of newsletter rows, each with a thumbnail, frequency badge, optional
SUBSCRIBER-ONLYoutline badge, and a Sign up / Unsubscribe button.localStorageso the demo feels like real persistence; productionisation removes that layer).?my-account-v2-demo=no-categoriesfor the flat-list variant.3. Donations —
/my-account/donations/?my-account-v2-demo=1List with two sections:
Click the active card's Manage donation → detail page (
/donations/don-001/). Detail page has:A cancelled donation (click any cancelled row in the Previous table) shows a CANCELLED badge + a single Restart donation button → transaction modal, snackbar on submit.
The list-page bottom has a billing history Button Card. Try
?my-account-v2-demo=billing-historyto see the inline-table variant instead.4. Subscriptions —
/my-account/subscriptions/?my-account-v2-demo=1Same shape as Donations: active section + previous section. Each card click → detail page.
The detail page is the variant-richest in the prototype. Walk these URLs in order to see all six branches:
/subscriptions/sub-001/?my-account-v2-demo=1/subscriptions/sub-cancelled/?my-account-v2-demo=1/subscriptions/sub-expired/?my-account-v2-demo=1/subscriptions/sub-expiring/?my-account-v2-demo=1/subscriptions/sub-renewed/?my-account-v2-demo=1/subscriptions/sub-active-no-fees/?my-account-v2-demo=1The Change subscription modal is the most ambitious in the prototype: segmented control of frequency tabs → tier cards → transaction step with a billing readout + Stripe-style payment form. Every "Pay now" snackbars and closes.
5. Payment information —
/my-account/payment-methods/?my-account-v2-demo=1Card-based shell (rebuilt onto v1's
payment-information.phpshape mid-prototype after design review reversed the original WC-core-table call). Two sections:__box --bordercard per saved card. Default badge on the default row, Expired badge under?my-account-v2-demo=expired-payment. Each card has a More dropdown with Edit + Delete modals, plus an Add payment method button that opens a singleton modal.Every modal Confirm fires a slug-keyed snackbar and closes. Try
?my-account-v2-demo=no-addressesto demo the empty-addresses fallback.6. Account settings —
/my-account/edit-account/?my-account-v2-demo=1Profile + Password forms reusing v1's DOM. Save buttons fire snackbars without navigating. Forgot-password link snackbars.
The Delete account button is the centrepiece. It opens a two-step modal:
Close (X / ESC / backdrop / Cancel) resets to step 1.
7. Homepage drawer —
/?my-account-v2-demo=1The same flag on the site homepage opens a right-side drawer overlay with a backdrop. Greeting reads "Good {morning|afternoon|evening}, {first name}." based on the visitor's local time. The drawer mirrors the v2 sidebar menu, adds a placeholder secondary nav (FAQ / Contact us / Privacy Policy / Terms of Service), and a Sign out footer.
/my-account/<slug>/?my-account-v2-demo=1./my-account/page, the sidebar's "Back to Homepage" link reopens the drawer (the flag survives the round-trip).8. WP-admin: Reader Account Customization —
/wp-admin/admin.php?page=newspack-audience#/reader-account-customizationThe admin companion. New tab in the Audience wizard, between Configuration and Checkout & Payment. Three two-column sections — Branding (logo upload), Newsletters (page title + description), Account & Billing (Terminology toggle with
Customrevealing stacked singular + plural inputs, plus cancel-recurring-donation message and billing/invoice footer).The brief's §2.1 / §2.1.1 reflexes (no custom CSS, reuse v1 class names) don't apply to this surface — admin React pages compose from
@wordpress/componentsfirst,packages/components/srcsecond, custom SCSS last. The single scoped SCSS rule that ships zeroes legacyBaseControlmargins so the per-section<VStack spacing={8}>is the only source of vertical rhythm.Scenario fixtures (alternate states)
Every URL accepts a scenario name in place of
1to demo specific variants without writing real data:1(or any unknown value)cancelled-subexpiringrenewedno-feesbilling-historyno-categoriesexpired-paymentemptyno-donations/no-subscriptions/no-payment-methods/no-addressesAllow-listed; anything outside the table falls through to the happy path. Full reference: Guide § Scenario index.
Gating regression check
Before signing off, log out (or switch to a non-admin user) and confirm the prototype is invisible to real readers:
/my-account/?my-account-v2-demo=1→ v1 renders unchanged. Nonewspack-my-account-v2-demobody class; nomy-account-v2-demo.{js,css}enqueued./my-account/newsletters/?my-account-v2-demo=1and/donations/?…→ bounce toedit-account(the redirect-non-demo guard)./my-account/subscriptions/?…→ on a WCS-installed site, WCS' default still renders for non-admins (no demo hijack)./?my-account-v2-demo=1→ homepage renders unchanged (no drawer)./wp-admin/admin.php?page=newspack-audience#/reader-account-customization→ non-admins can't reach the wizard at all (admin caps gate the whole thing).Doc trio for deeper dives
Phasing — how this came together
Each phase / polish pass shipped as its own PR into
prototype/my-account-demo; this umbrella PR lands the whole prototype intotrunkas one merge. Scope grew from the original four primary surfaces to five plus a WP-admin companion, and from six modals to twelve, as design review surfaced gaps the original brief didn't cover.?my-account-v2-demoflag, body class, asset bundle scaffoldnewslettersendpoint, auto-flush guarddonationsendpoint with id-value resolution<table>DOM; later rebuilt — see post-Phase-8 row), payment-methods endpoint takeoverdocs/my-account-v2-prototype-guide.mdform-edit-account.phpswap, stateless form submits, Delete account two-step modallocalStoragepersistencepayment-information.phpshell with payment methods + addresses sections + 5 new modalsdonation-details.phpretro-fitted to the v1-class-names-first reflex it predated; canonical WCS table classes;paid→completedfake-data status alignmentgap-9→gap-11,__separatorprimitive replacing|glyph, sidebar relabel, expired variant wired (badge + Renew + modal)Scope guardrails (still true at merge)
is_demo_active()returns false. The homepage overlay has a parallelis_homepage_demo_active()gate. The WP-admin companion lives behind the existing Audience wizard's admin caps..newspack-my-account-v2-demo; the homepage overlay uses its own.newspack-my-account-v2-demo-homepagescope. The dedicatedstyle.scssis intentionally minimal — most of the surface area lives on newspack-ui primitives and v1 class names. The handful of scoped rules that exist are each documented inline with a "why" callout pointing back to the brief reflexes.localStorageonly (productionisation removes that layer entirely). The WP-admin companion is local React state only.newsletters,donations) ship redirect-non-demo guards so guessable URLs bounce non-admins toedit-account. Subscriptions on WCS-installed sites still render WCS' default for non-admin readers.Productionisation — what's needed before this becomes the real My Account
Productionising the prototype into v1 is a separate workstream, not a follow-up to this PR. The Guide § Productionisation playbook is the to-do list when that work is scoped: replace fake-data slices with real WC/WCS/ESP queries, wire real Stripe on every modal form, scrub the
remove_all_actionssledgehammers (subscriptions, payment-methods, newsletters) down to handler-name-keyed removals, drop the?my-account-v2-demogate + body-class scope + auto-flush plumbing + menu-item conditional + homepage drawer gate, productionise the WP-admin companion (real persistence, wire Custom terminology through to v2 templates), and pick up deferred design-system work.Carryover items not blocking the prototype:
__badge--erroris lighter than Figma; right fix is a__badge--error-strongaddition to_badge.scss. Touches the shared design system → separate PR with design sign-off.order_details+subscription_detailscell padding renders ~56px row height vs Figma's ~32px row pitch. Shared by donation + subscription detail pages.__separatorprimitive promotion — Subscriptions list polish introduced a prototype-local vertical-hairline. If a third caller appears in v1 it should graduate to_dividers.scss.?my-account-v2-demo=multi-subscenario — supported trivially; shipping is a stakeholder-review call.cancelled; a dedicated frame would let us confirm whether copy / structure should diverge.Other information: