Overlays didn't lock body scroll, so the page behind a modal/sheet scrolled under it. Add a ref-counted scroll lock (so stacked overlays don't prematurely release it) and hold it via an effect in Modal and Sheet while open, restoring on close or unmount. Focus-trapping was already handled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
import { test, expect, type Page } from './fixtures';
|
|
|
|
// Opening an overlay (Sheet/Modal) locks background scroll so the page behind
|
|
// it can't scroll under the overlay; closing restores it.
|
|
|
|
const MOBILE = { width: 390, height: 780 } as const;
|
|
|
|
async function authed(page: Page) {
|
|
await page.route('**/api/v1/auth/config', (r) =>
|
|
r.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ self_register_enabled: true, private_mode: false })
|
|
})
|
|
);
|
|
await page.route('**/api/v1/auth/me', (r) =>
|
|
r.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
user: { id: 'u1', username: 'reader', created_at: '2026-01-01T00:00:00Z', is_admin: false }
|
|
})
|
|
})
|
|
);
|
|
await page.route('**/api/v1/auth/me/preferences', (r) =>
|
|
r.fulfill({ status: 200, contentType: 'application/json', body: '{}' })
|
|
);
|
|
await page.route('**/api/v1/me/bookmarks*', (r) =>
|
|
r.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ items: [], page: { limit: 50, offset: 0, total: 0 } })
|
|
})
|
|
);
|
|
}
|
|
|
|
const bodyOverflow = (page: Page) => page.evaluate(() => document.body.style.overflow);
|
|
|
|
test('opening the account password sheet locks body scroll, closing restores it', async ({
|
|
page
|
|
}) => {
|
|
await authed(page);
|
|
await page.setViewportSize(MOBILE);
|
|
await page.goto('/profile/account');
|
|
|
|
expect(await bodyOverflow(page)).not.toBe('hidden');
|
|
|
|
await page.getByTestId('account-row-change-password').click();
|
|
await expect(page.getByTestId('password-sheet')).toBeVisible();
|
|
expect(await bodyOverflow(page)).toBe('hidden');
|
|
|
|
// Close via Escape; scroll is released.
|
|
await page.keyboard.press('Escape');
|
|
await expect(page.getByTestId('password-sheet')).toBeHidden();
|
|
expect(await bodyOverflow(page)).not.toBe('hidden');
|
|
});
|