feat(account): open My Account as a modal over the page behind it (#48)

/account had no site header and no links of any kind, so once a customer opened it the only way out was the browser's back button or editing the URL.

It is now a modal rendered over whatever the customer was looking at, while staying a real route. Opening it from the header pushes /account and names the current page as the backdrop, so closing returns there with filters and scroll position intact, and the browser's Back button does the same thing as the close control. Entering /account directly — a bookmark, the link in a verification email, or the redirect after registering — has no page behind it, so it falls back to rendering the storefront as the backdrop. Closing therefore always lands somewhere real rather than on nothing.

Keeping it a route rather than view state means the URL still works: it can be bookmarked, shared, and refreshed with the account view still open, which is what the existing header link and the four post-authentication redirects already depend on.

Also scopes the account switch locator in the favorites spec to the modal. The storefront now renders behind the account view and has a theme switch of its own, so an unscoped switch locator was only picking the right control by DOM accident.

Verified with 68 end-to-end tests, 5 of them new, all passing, and type checking clean. No backend changes.

Closes #48
This commit is contained in:
2026-08-18 15:47:00 -05:00
parent e4b2cc1935
commit 534513c760
5 changed files with 184 additions and 25 deletions
+6 -3
View File
@@ -130,10 +130,13 @@ test.describe('Favoriting items', () => {
await page.request.put('/api/customers/me/favorite-alerts', { data: { enabled: true } });
await page.goto('/account');
const toggle = page.getByText('Email me when an item I favorited is sold');
await expect(toggle).toBeVisible();
// Scoped to the account modal. The storefront renders behind it and has a
// theme switch of its own, so an unscoped switch locator only picks the
// right control by DOM accident.
const account = page.getByRole('dialog', { name: 'My Account' });
await expect(account.getByText('Email me when an item I favorited is sold')).toBeVisible();
await page.getByRole('switch').last().click();
await account.getByRole('switch').last().click();
await expect(page.getByText('Turned off')).toBeVisible();
const me = await (await page.request.get('/api/customers/me')).json();