fix: contain board canvas overflow

This commit is contained in:
Thomas Camlong committed 2026-08-03 16:46:43 +02:00
1 parent 49d623e5c2
commit cc71ab8cd9
5 files changed
+14 -9

No files matched your search

@@ -9,7 +9,7 @@
}
.gutter {
position: sticky;
position: relative;
top: 0;
align-self: start;
padding-top: 24px;
@@ -1,6 +1,6 @@
"use client";
import { useEffect, useRef } from "react";
import { useEffect, useState } from "react";
import { Box } from "@mantine/core";
import { useCurrentLayout, useInitialViewportWidth, useRequiredBoard } from "@homarr/boards/context";
@@ -24,17 +24,16 @@ import classes from "./_client.module.css";
const APP_SHELL_INLINE_PADDING = 32;
const useFixedBoardGutters = () => {
const columnsRef = useRef<HTMLDivElement>(null);
const [columns, setColumns] = useState<HTMLDivElement | null>(null);
useEffect(() => {
const columns = columnsRef.current;
if (!columns) return;
let frame = 0;
const update = () => {
frame = 0;
const columnsRect = columns.getBoundingClientRect();
const headerBottom = document.querySelector("header")?.getBoundingClientRect().bottom ?? 0;
const headerBottom = document.querySelector("[data-app-shell-header]")?.getBoundingClientRect().bottom ?? 0;
const stickyTop = Math.max(0, headerBottom + 16);
for (const gutter of columns.querySelectorAll<HTMLElement>("[data-board-gutter]")) {
@@ -62,6 +61,7 @@ const useFixedBoardGutters = () => {
const resizeObserver = new ResizeObserver(scheduleUpdate);
resizeObserver.observe(columns);
document.addEventListener("scroll", scheduleUpdate, { capture: true, passive: true });
window.addEventListener("scroll", scheduleUpdate, { passive: true });
window.addEventListener("resize", scheduleUpdate, { passive: true });
scheduleUpdate();
@@ -69,11 +69,12 @@ const useFixedBoardGutters = () => {
window.cancelAnimationFrame(frame);
resizeObserver.disconnect();
document.removeEventListener("scroll", scheduleUpdate, { capture: true });
window.removeEventListener("scroll", scheduleUpdate);
window.removeEventListener("resize", scheduleUpdate);
};
}, []);
}, [columns]);
return columnsRef;
return setColumns;
};
export const ClientBoard = () => {
@@ -3,6 +3,7 @@
max-width: 100%;
min-width: 0;
overflow: visible;
overflow-x: auto;
overscroll-behavior-inline: contain;
}
+1 -1
View File
@@ -18,7 +18,7 @@ interface Props {
export const MainHeader = ({ logo, actions, hasNavigation = true }: Props) => {
return (
<AppShellHeader maw="100vw" zIndex={201} style={{ overflowX: "hidden" }}>
<AppShellHeader maw="100vw" zIndex={201} style={{ overflowX: "hidden" }} data-app-shell-header>
<Group h="100%" gap="xl" px="md" justify="apart" wrap="nowrap">
<Group h="100%" align="center" style={{ flex: 1 }} wrap="nowrap">
{hasNavigation && <ClientBurger />}
+4 -1
View File
@@ -177,6 +177,7 @@ describe("Board grid", () => {
const zoomedTileBox = await expectBoundingBoxAsync(logicalTile);
expect(zoomedTileBox.width).toBeGreaterThan(normalZoomTileBox.width * 1.25);
await expectHorizontalOverflowAsync(canvas);
await expectDocumentNotHorizontallyScrollableAsync(page);
const overflowRailY = (await expectBoundingBoxAsync(rail)).y;
await page.evaluate(() => window.scrollTo({ top: 300, behavior: "auto" }));
await expect.poll(async () => (await expectBoundingBoxAsync(rail)).y).toBeGreaterThanOrEqual(0);
@@ -348,6 +349,8 @@ describe("Board grid", () => {
await page.setViewportSize({ width: 600, height: 1200 });
await expect(canvas).toHaveAttribute("data-canvas-scale", String(minimumCanvasScale));
await expectHorizontalOverflowAsync(canvas);
await expectDocumentNotHorizontallyScrollableAsync(page);
for (const direction of ["n", "ne", "e", "se", "s", "sw", "w", "nw"] as const) {
await expectTargetSizeAsync(firstItem.locator(`:scope > [data-grid-resize-handle="${direction}"]`));
}
@@ -823,8 +826,8 @@ describe("Board grid", () => {
const stickyRailY = (await expectBoundingBoxAsync(rail)).y;
await page.evaluate(() => window.scrollTo({ top: 500, behavior: "auto" }));
await expect.poll(async () => (await expectBoundingBoxAsync(rail)).y).toBeGreaterThanOrEqual(0);
const scrolledRailY = (await expectBoundingBoxAsync(rail)).y;
expect(scrolledRailY).toBeGreaterThanOrEqual(0);
expect(scrolledRailY).toBeLessThanOrEqual(stickyRailY + 0.5);
await page.evaluate(() => window.scrollTo({ top: 0, behavior: "auto" }));