From 579eb8e61a421edea592b4fb9f9a6f94a7665b33 Mon Sep 17 00:00:00 2001 From: stroblme Date: Mon, 24 Aug 2026 11:20:32 +0200 Subject: [PATCH] Size the rail to what it carries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stretched end to end, a panel with two dashboards showed a pill nine tenths empty — which was tolerable while the rail was chrome outside the canvas and obvious once it moved onto the panel itself. It now hugs its entries and sits centred in the column it reserves; that column is the same width either way, so no arrangement moves. A rail longer than the panel is tall still scrolls, and still draws no bar: a wall panel is swiped, and there is no room for one. --- NOTEPAD.md | 4 ---- .../src/components/Dashboard/ui/glass/Surfaces.tsx | 6 +++++- .../components/Dashboard/ui/material/Surfaces.tsx | 8 +++++--- frontend/tests/panel.spec.ts | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/NOTEPAD.md b/NOTEPAD.md index 502ce83..ec503eb 100644 --- a/NOTEPAD.md +++ b/NOTEPAD.md @@ -35,10 +35,6 @@ Deferring because out of scope is fine, but don't mention deferring than. (`ui/segmented.tsx`, `Common/RangePicker.tsx`, and the flow screens). The dashboard no longer shares them — it has its own, one per look — so unifying the remaining three is now purely an app-side job. -- CHORE/UI: a panel's rail runs the full height of the canvas whatever it - carries, so two dashboards leave most of a tall pill empty. Hugging its - contents and centring would read better; the column it reserves stays the - same either way. - CHORE/UI: the house panels are laid out for 1280x800 — twelve columns, twelve rows. A chart's fixed chrome is now its legend alone: the range picker moved to a column down the right-hand edge and gave its row back, so a third chart diff --git a/frontend/src/components/Dashboard/ui/glass/Surfaces.tsx b/frontend/src/components/Dashboard/ui/glass/Surfaces.tsx index 9dff9ed..910e777 100644 --- a/frontend/src/components/Dashboard/ui/glass/Surfaces.tsx +++ b/frontend/src/components/Dashboard/ui/glass/Surfaces.tsx @@ -158,7 +158,11 @@ export function Rail({ entries }: RailProps) { aria-label="Dashboards on this panel" data-testid={TESTID.rail} className={cn( - "gl-surface gl-rail pointer-events-auto absolute inset-y-3 left-3 z-10 flex w-12 flex-col items-center gap-1 p-1", + "gl-surface gl-rail pointer-events-auto absolute left-3 top-1/2 z-10 flex max-h-[calc(100%-1.5rem)] w-12 -translate-y-1/2 flex-col items-center gap-1 p-1", + // As tall as what it carries, centred in the column it reserves: a + // rail of two stretched to the height of the panel is mostly empty + // pill. More dashboards than the panel is tall still scroll, but no + // bar is ever drawn — a wall panel is swiped, and there is no room. "overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", )} > diff --git a/frontend/src/components/Dashboard/ui/material/Surfaces.tsx b/frontend/src/components/Dashboard/ui/material/Surfaces.tsx index b8a585a..cbe7edd 100644 --- a/frontend/src/components/Dashboard/ui/material/Surfaces.tsx +++ b/frontend/src/components/Dashboard/ui/material/Surfaces.tsx @@ -124,9 +124,11 @@ export function Rail({ entries }: RailProps) { aria-label="Dashboards on this panel" data-testid={TESTID.rail} className={cn( - "m3-rail pointer-events-auto absolute inset-y-3 left-3 z-10 flex w-12 flex-col items-center gap-1 p-1", - // More dashboards than the column is tall still scroll, but no bar is - // ever drawn: a wall panel is swiped, and there is no room for one. + "m3-rail pointer-events-auto absolute left-3 top-1/2 z-10 flex max-h-[calc(100%-1.5rem)] w-12 -translate-y-1/2 flex-col items-center gap-1 p-1", + // As tall as what it carries, centred in the column it reserves: a + // rail of two stretched to the height of the panel is mostly empty + // pill. More dashboards than the panel is tall still scroll, but no + // bar is ever drawn — a wall panel is swiped, and there is no room. "overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", )} > diff --git a/frontend/tests/panel.spec.ts b/frontend/tests/panel.spec.ts index 0f2b228..eaadefd 100644 --- a/frontend/tests/panel.spec.ts +++ b/frontend/tests/panel.spec.ts @@ -8,6 +8,9 @@ import { api, apiPage, deleteAll } from "./utils/api" * is inside the canvas, scaled with it, and wearing the dashboard's own look. * Drawn outside, it was a strip of the app's design bolted to the edge of * somebody's wall panel — and on a scaled canvas it did not even line up. + * + * It is also as tall as what it carries. Stretched end to end, a panel with + * two dashboards showed a pill nine tenths empty. */ const flowName = `test_panel_${Date.now().toString(36)}` @@ -114,6 +117,17 @@ test("the rail is drawn on the panel, in the panel's own look", async ({ ) expect(inside, "the rail is drawn outside the panel it belongs to").toBe(true) + // As tall as what it carries rather than as tall as the panel: a rail of + // two stretched end to end is mostly empty pill. + expect( + box.height, + "the rail is stretched to the height of the panel", + ).toBeLessThan(canvas.height / 2) + const centres = Math.abs( + box.y + box.height / 2 - (canvas.y + canvas.height / 2), + ) + expect(centres, "the rail is not centred in its column").toBeLessThan(2) + // And the arrangement keeps clear of it rather than sitting under it. const tile = (await page.getByTestId("widget-frame").first().boundingBox())! expect(tile.x, "a widget is drawn under the rail").toBeGreaterThanOrEqual(