From f305e68bec4bfebf9f07220469e41c6ecbaa6402 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 3 Oct 2026 08:44:55 +0200 Subject: [PATCH] fix(dashboard): keep an over-wide focused rail card in view In a window narrower than a card (or under zoom), a focused card could never fit, so its own snap offset fell short of the needed scroll and the rail jumped to the next card's snap point, moving the focused card offscreen. Such a card is now aligned at its own start instead. Co-Authored-By: Claude Opus 5.5 --- .../lib/rails/dashboard-rail.component.spec.ts | 18 ++++++++++++++++++ .../src/lib/rails/dashboard-rail.component.ts | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.spec.ts b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.spec.ts index af1de8e36..4c6fcc43e 100644 --- a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.spec.ts +++ b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.spec.ts @@ -559,6 +559,24 @@ describe('DashboardRailComponent', () => { }); }); + it('keeps a card wider than the visible area at its own start instead of skipping past it', async () => { + // The 1000px viewport cannot hold a 1200px card, so the next + // card's snap point (2420) would move the focused one offscreen. + const { scrollTo, focusLink } = await renderRail({ + count: 3, + width: 1200, + stride: 1210, + scrollLeft: 1210, + }); + + focusLink(1); + + expect(scrollTo).toHaveBeenCalledWith({ + left: 1210, + behavior: 'auto', + }); + }); + it('aligns a card cut off on the left with the start edge', async () => { const { scrollTo, focusLink } = await renderRail({ count: 6, diff --git a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.ts b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.ts index 112d84728..44917caf7 100644 --- a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.ts +++ b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.ts @@ -318,7 +318,13 @@ export class DashboardRailComponent implements AfterViewInit, OnDestroy { visible.left; const maxLeft = track.scrollWidth - track.clientWidth; let left = maxLeft; - if (hiddenLeft) { + // A card wider than the visible area (a narrow window, or zoom) can + // never fit: show its start rather than a later card's snap point, + // which would move it offscreen. + if ( + hiddenLeft || + rect.right - rect.left > visible.right - visible.left + ) { left = snapOffset(card); } else { const needed = track.scrollLeft + rect.right - visible.right;