A page is a document: drop the sticky chrome and the bounded-frame opt-out

This commit is contained in:
2026-09-09 15:26:45 +02:00
parent 60aa8a888e
commit 53ab35bfb8
12 changed files with 39 additions and 170 deletions
-38
View File
@@ -201,44 +201,6 @@ test.describe.serial("authenticated admin journey", () => {
await expect(page.locator("table")).not.toContainText("Morning — Front desk");
});
// `fill: true` is a contract addition whose whole behaviour is CSS, and whose effect depends on
// markup the shell does not own — so it is pinned here, on the reference plugin's own list, rather
// than by asserting the class name the shell emits.
test("plugin page: a filled page scrolls its table, not the document, and the header stays put", async () => {
// The region is the viewport less ~185px of chrome, so this leaves ~75px: enough that the 3-row
// fixture overflows it by half its height, and enough to hold the header a row scrolls under.
const original = page.viewportSize();
await page.setViewportSize({ width: 1280, height: 260 });
await page.goto("/scheduling/shifts");
const bounded = await page.evaluate(() => {
const wrap = document.querySelector(".table-wrap");
const frame = document.querySelector(".app-fill");
if (!(wrap instanceof HTMLElement) || !(frame instanceof HTMLElement)) return null;
return {
frameScrolls: frame.scrollHeight > frame.clientHeight,
regionScrolls: wrap.scrollHeight > wrap.clientHeight,
};
});
expect(bounded, ".table-wrap and .app-fill must render").not.toBeNull();
// Both halves, and both discriminating: miss a wrapper in the page's chain and the table grows
// instead of scrolling, which pushes the frame past its own height.
expect(bounded?.frameScrolls, "a filled page must fit its frame").toBe(false);
expect(bounded?.regionScrolls, "the fixture must overflow the region, or the rest proves nothing").toBe(true);
const headTop = async () => {
const box = await page.locator("thead th").first().boundingBox();
expect(box, "the header must have a box to stay put").not.toBeNull();
return box?.y;
};
const before = await headTop();
await page.locator(".table-wrap").evaluate((el) => el.scrollTo(0, el.scrollHeight));
await expect(page.locator("tbody tr").last()).toBeInViewport();
expect(await headTop(), "the header sticks to a scrollport that moves").toBe(before);
// The journey shares one page; leaving it short would hand the next test a window it never chose.
if (original) await page.setViewportSize(original);
});
test("plugin settings: the screen names the variable that sets each declared key", async () => {
await page.goto("/admin/plugin-settings");
await expect(page.locator("h1")).toHaveText("Plugin settings");
+1 -22
View File
@@ -35,7 +35,7 @@ for (const [name, path, tail] of [
["the public landing", "/", ".landing-actions .btn"],
] as const) {
for (const width of [1280, 390]) {
test(`${name} scrolls to its end at ${width}px wide, and the chrome stays put`, async ({ page }) => {
test(`${name} scrolls to its end at ${width}px wide`, async ({ page }) => {
await page.setViewportSize({ width, height: 200 });
await page.goto(path);
@@ -44,27 +44,6 @@ for (const [name, path, tail] of [
await page.keyboard.press("End");
// Whole, not merely touched: a control half under the fold is not reachable either.
await expect(page.locator(tail).last()).toBeInViewport({ ratio: 1 });
// The sticky pair is the whole reason the document may scroll: on a narrow screen the
// hamburger in the topbar is the only way back into the nav.
await expect(page.locator(".topbar")).toBeInViewport();
if (width > 860) await expect(page.locator(".brand-name")).toBeInViewport();
// The open nav is a fixed overlay, so a reader cannot scroll the page out from under the
// scrim. Focus can still move it, and stopping that needs script this page does not have.
if (width <= 860) {
await page.locator(".hamburger").click(); // the label is the control; the checkbox takes no pointer
await expect(page.locator("#nav-toggle")).toBeChecked();
// The lock is one CSS rule, and whether a key press moves the page with the nav open turns
// out to differ by engine — so pin the rule itself, then the behaviour it buys.
expect(await page.evaluate(() => getComputedStyle(document.body).overflow)).toBe("hidden");
// Both directions: opening the nav may leave the page at either end, and a key press toward
// the end it already sits at cannot move it whether the lock holds or not.
const before = await page.evaluate(() => window.scrollY);
for (const key of ["End", "Home"]) {
await page.keyboard.press(key);
expect(await page.evaluate(() => window.scrollY), key + " cannot scroll the page while the nav is open").toBe(before);
}
}
});
}
}