diff --git a/src/shared/styles/globals.css b/src/shared/styles/globals.css index 12e18e9d..dfe5a760 100644 --- a/src/shared/styles/globals.css +++ b/src/shared/styles/globals.css @@ -68,6 +68,8 @@ background: var(--workspace); font-synthesis: none; -webkit-font-smoothing: antialiased; + /* Keep wheel/trackpad gestures from rubber-banding the whole app. */ + overscroll-behavior: none; } * { box-sizing: border-box; diff --git a/tests/browser/layout.spec.mjs b/tests/browser/layout.spec.mjs index 9ddd1ecb..bb25f2e8 100644 --- a/tests/browser/layout.spec.mjs +++ b/tests/browser/layout.spec.mjs @@ -71,6 +71,48 @@ async function shellFits(page, width) { } } +test("page overscroll is disabled while message history still scrolls", async ({ + page, + app, +}) => { + await open(page, app); + // Headless wheel input does not reproduce macOS trackpad rubber-banding. + // Check the viewport policy as well as real panel scrolling and shell bounds. + await expect(page.locator("html")).toHaveCSS("overscroll-behavior", "none"); + const shell = page.locator(".shell-background"); + const bounds = await box(shell); + const history = page.getByRole("region", { name: "Channel message history" }); + await settle(page); + const initialOffset = await history.evaluate((el) => el.scrollTop); + await history.hover(); + await page.mouse.wheel(0, -300); + await expect + .poll(() => history.evaluate((el) => el.scrollTop)) + .toBeLessThan(initialOffset - 100); + await settle(page); + expect(await box(shell)).toEqual(bounds); + + // Projects has no overflowing content: gestures must leave the shell in place. + await page + .getByRole("navigation", { name: "Pages", exact: true }) + .getByRole("button", { name: "Projects", exact: true }) + .click(); + await page.getByRole("heading", { name: "Projects", exact: true }).hover(); + for (const [x, y] of [ + [0, -600], + [0, 600], + [-600, 0], + [600, 0], + ]) { + await page.mouse.wheel(x, y); + await page.evaluate(() => new Promise(requestAnimationFrame)); + expect(await box(shell)).toEqual(bounds); + expect(await page.evaluate(() => [window.scrollX, window.scrollY])).toEqual( + [0, 0], + ); + } +}); + test("bento surfaces, centered tabs, real link panel and compact community navigation", async ({ page, app, diff --git a/tests/browser/typeahead.spec.mjs b/tests/browser/typeahead.spec.mjs index 2a7e7b99..358ca537 100644 --- a/tests/browser/typeahead.spec.mjs +++ b/tests/browser/typeahead.spec.mjs @@ -534,7 +534,8 @@ test("current custom catalog drives typeahead and signed tags across community r await expect(input).toHaveValue(":party-parrot "); await input.press("ControlOrMeta+z"); await expect(input).toHaveValue(":party-parrot: "); - await input.press("End"); + // macOS End scrolls the document; use its caret shortcut when overscroll is off. + await input.press(process.platform === "darwin" ? "Meta+ArrowRight" : "End"); await input.pressSequentially("hello"); await expect(input).toHaveValue(":party-parrot: hello"); await input.press("Enter");