fix: contain settings page overscroll (#1316)

This commit is contained in:
Carlos Florêncio
2026-06-08 22:42:14 +01:00
committed by GitHub
parent d78a1a4370
commit ec40bf0b87
4 changed files with 86 additions and 1 deletions
+5
View File
@@ -192,6 +192,11 @@
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) transparent;
}
/* App shell: lock the document to the viewport; each route owns its scroll container. */
html,
body {
@apply h-full overflow-hidden overscroll-none;
}
body {
@apply bg-background text-foreground;
}
@@ -116,7 +116,10 @@ function SettingsShell({
/>
{/* Scroll the content, not the topbar: min-h-0 lets this flex child
shrink below its content height so overflow-y-auto can take effect. */}
<div className="flex min-w-0 min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4 pt-0 pb-20">
<div
data-testid="settings-scroll-container"
className="flex min-w-0 min-h-0 flex-1 flex-col gap-4 overflow-y-auto overscroll-contain p-4 pt-0 pb-20"
>
{children}
</div>
</main>
@@ -227,6 +227,32 @@ test.describe("Automations settings page", () => {
});
});
test("create page keeps scrolling inside the settings pane", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.gotoNew();
const rootScrollStyles = await testPage.evaluate(() => ({
htmlOverflowY: getComputedStyle(document.documentElement).overflowY,
htmlOverscrollY: getComputedStyle(document.documentElement).overscrollBehaviorY,
bodyOverflowY: getComputedStyle(document.body).overflowY,
bodyOverscrollY: getComputedStyle(document.body).overscrollBehaviorY,
}));
expect(rootScrollStyles).toEqual({
htmlOverflowY: "hidden",
htmlOverscrollY: "none",
bodyOverflowY: "hidden",
bodyOverscrollY: "none",
});
const settingsScroller = testPage.getByTestId("settings-scroll-container");
await expect(settingsScroller).toBeVisible();
await expect(settingsScroller).toHaveCSS("overflow-y", "auto");
await expect(settingsScroller).toHaveCSS("overscroll-behavior-y", "contain");
await testPage.mouse.wheel(0, 3000);
await expect.poll(() => testPage.evaluate(() => window.scrollY), { timeout: 5_000 }).toBe(0);
});
test("enable/disable toggle on list page", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
@@ -0,0 +1,51 @@
import type { Locator, Page } from "@playwright/test";
import { test, expect } from "../fixtures/test-base";
import { AutomationsPage } from "../pages/automations-page";
/** Swipe up on a scroll container — the touch equivalent of wheel-down at scroll bottom. */
async function swipeUpOnElement(page: Page, element: Locator): Promise<void> {
const box = await element.boundingBox();
if (!box) throw new Error("scroll container has no bounding box");
const cdp = await page.context().newCDPSession(page);
const centerX = box.x + box.width / 2;
const startY = box.y + box.height - 20;
const endY = box.y + 20;
await cdp.send("Input.dispatchTouchEvent", {
type: "touchStart",
touchPoints: [{ x: centerX, y: startY }],
});
for (let i = 1; i <= 8; i++) {
const y = startY + ((endY - startY) * i) / 8;
await cdp.send("Input.dispatchTouchEvent", {
type: "touchMove",
touchPoints: [{ x: centerX, y }],
});
}
await cdp.send("Input.dispatchTouchEvent", {
type: "touchEnd",
touchPoints: [],
});
}
test.describe("Automations settings on mobile", () => {
test("create page does not hand off bottom overscroll to the document", async ({
testPage,
seedData,
}) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.gotoNew();
const settingsScroller = testPage.getByTestId("settings-scroll-container");
await expect(settingsScroller).toBeVisible();
await expect(settingsScroller).toHaveCSS("overscroll-behavior-y", "contain");
await settingsScroller.evaluate((el) => {
el.scrollTop = el.scrollHeight;
});
await swipeUpOnElement(testPage, settingsScroller);
await expect.poll(() => testPage.evaluate(() => window.scrollY), { timeout: 5_000 }).toBe(0);
});
});