From 20dbdcedf24c4054ca9c406d27276ffc4c2d9468 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Mon, 3 Aug 2026 12:25:56 +0200 Subject: [PATCH] fix focused pane scrolling --- .../src/components/board/advanced-focus/wheel.ts | 14 ++++++++++---- e2e/board-advanced-interactions.spec.ts | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/apps/nextjs/src/components/board/advanced-focus/wheel.ts b/apps/nextjs/src/components/board/advanced-focus/wheel.ts index 4cf5bc36d..285fb22ec 100644 --- a/apps/nextjs/src/components/board/advanced-focus/wheel.ts +++ b/apps/nextjs/src/components/board/advanced-focus/wheel.ts @@ -1,9 +1,11 @@ -const canScrollVertically = (element: HTMLElement, delta: number) => { +const isVerticalScrollContainer = (element: HTMLElement) => { if (element.scrollHeight <= element.clientHeight) return false; const overflowY = getComputedStyle(element).overflowY; - if (overflowY !== "auto" && overflowY !== "scroll") return false; + return overflowY === "auto" || overflowY === "scroll"; +}; +const canScrollVertically = (element: HTMLElement, delta: number) => { return delta < 0 ? element.scrollTop > 0 : element.scrollTop + element.clientHeight < element.scrollHeight; }; @@ -11,17 +13,21 @@ export const redirectShiftWheel = (root: HTMLElement, target: EventTarget | null if (delta === 0) return false; let current = target instanceof HTMLElement ? target : null; + let startedInScrollContainer = false; while (current && root.contains(current)) { - if (canScrollVertically(current, delta)) { + const isCurrentScrollContainer = isVerticalScrollContainer(current); + startedInScrollContainer ||= isCurrentScrollContainer; + if (isCurrentScrollContainer && canScrollVertically(current, delta)) { current.scrollTop += delta; return true; } if (current === root) break; current = current.parentElement; } + if (startedInScrollContainer) return false; const viewport = [...root.querySelectorAll("[data-scrollbars='y'], [data-scrollbars='xy']")].find( - (element) => canScrollVertically(element, delta), + (element) => isVerticalScrollContainer(element) && canScrollVertically(element, delta), ); if (!viewport) return false; diff --git a/e2e/board-advanced-interactions.spec.ts b/e2e/board-advanced-interactions.spec.ts index 17f07f5cd..0b3f12bbb 100644 --- a/e2e/board-advanced-interactions.spec.ts +++ b/e2e/board-advanced-interactions.spec.ts @@ -122,8 +122,12 @@ describe("Board advanced interactions", () => { probe.innerHTML = '
'; probe.addEventListener("wheel", (event) => event.stopPropagation()); element.append(probe); + + const unrelated = probe.cloneNode(true) as HTMLDivElement; + unrelated.dataset.shiftScrollProbe = "unrelated"; + element.append(unrelated); }); - const scrollProbe = advancedSurface.locator("[data-shift-scroll-probe]"); + const scrollProbe = advancedSurface.locator("[data-shift-scroll-probe='true']"); await page.keyboard.down("Shift"); const pixelEventCancelled = await scrollProbe.evaluate( (element) => @@ -144,6 +148,15 @@ describe("Board advanced interactions", () => { await page.keyboard.up("Shift"); expect(lineEventCancelled).toBe(true); expect(await scrollProbe.evaluate((element) => element.scrollTop)).toBeGreaterThanOrEqual(48); + await scrollProbe.evaluate((element) => { + element.scrollTop = element.scrollHeight; + element.dispatchEvent( + new WheelEvent("wheel", { bubbles: true, cancelable: true, shiftKey: true, deltaX: 120 }), + ); + }); + expect( + await advancedSurface.locator("[data-shift-scroll-probe='unrelated']").evaluate((element) => element.scrollTop), + ).toBe(0); await page.keyboard.press("Escape"); await expect(advancedSurface).toBeHidden(); await expect(widget).toBeFocused();