From 47be640bfb508e72f9468d4e53ae466f3c47a916 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 21 Jun 2026 11:07:17 -0700 Subject: [PATCH] FN-6836: allow narrower left sidebar resizing Allow the dashboard left sidebar to resize down to a compact but still labeled width. - Lower the resizable left sidebar minimum width from 192px to 160px. - Keep narrow sidebar labels truncated so badges and toggles stay aligned. - Cover drag, keyboard, and persisted-width clamping at the new minimum. Files changed: .../dashboard/app/components/LeftSidebarNav.css | 4 +++ .../dashboard/app/components/LeftSidebarNav.tsx | 5 ++- .../components/__tests__/LeftSidebarNav.test.tsx | 37 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-6836 Fusion-Task-Lineage: e136647f-3cd8-4b82-8186-b69d630946fc --- .../app/components/LeftSidebarNav.css | 4 ++ .../app/components/LeftSidebarNav.tsx | 5 ++- .../__tests__/LeftSidebarNav.test.tsx | 37 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/packages/dashboard/app/components/LeftSidebarNav.css b/packages/dashboard/app/components/LeftSidebarNav.css index 0187fee084..1d5114c0b6 100644 --- a/packages/dashboard/app/components/LeftSidebarNav.css +++ b/packages/dashboard/app/components/LeftSidebarNav.css @@ -110,6 +110,10 @@ The active sidebar item must use the defined --status-todo-bg token so the dashb right: calc(var(--space-xs) * -1); } +/* +FNXC:Navigation 2026-06-21-00:00: +The narrower resizable sidebar must preserve row rhythm by truncating labels instead of wrapping or pushing badges/toggles out of place. Rail mode still hides labels entirely through the collapsed sidebar rule. +*/ .left-sidebar-nav__label { flex: 1; min-width: 0; diff --git a/packages/dashboard/app/components/LeftSidebarNav.tsx b/packages/dashboard/app/components/LeftSidebarNav.tsx index 785482e05a..902d7352fc 100644 --- a/packages/dashboard/app/components/LeftSidebarNav.tsx +++ b/packages/dashboard/app/components/LeftSidebarNav.tsx @@ -57,9 +57,12 @@ interface SidebarNavEntry { /* FNXC:Navigation 2026-06-20-00:00: The experimental sidebar default is intentionally narrower than the original 256px layout so desktop/tablet navigation preserves more board content while keeping the existing resize clamps. + +FNXC:Navigation 2026-06-21-00:00: +The minimum resizable width is lowered so users can recover board/content space without forcing full rail collapse. Keep the floor at the narrowest label-legible width; below this point users should switch to collapse/rail mode to preserve icons, badges, and labels. */ const LEFT_SIDEBAR_DEFAULT_WIDTH = 224; -const LEFT_SIDEBAR_MIN_WIDTH = 192; +const LEFT_SIDEBAR_MIN_WIDTH = 160; const LEFT_SIDEBAR_MAX_WIDTH = 384; const LEFT_SIDEBAR_WIDTH_STORAGE_KEY = "fusion:left-sidebar-width"; const LEFT_SIDEBAR_COLLAPSED_STORAGE_KEY = "fusion:left-sidebar-collapsed"; diff --git a/packages/dashboard/app/components/__tests__/LeftSidebarNav.test.tsx b/packages/dashboard/app/components/__tests__/LeftSidebarNav.test.tsx index ad81a7b01b..b6efbe3e51 100644 --- a/packages/dashboard/app/components/__tests__/LeftSidebarNav.test.tsx +++ b/packages/dashboard/app/components/__tests__/LeftSidebarNav.test.tsx @@ -388,6 +388,21 @@ describe("LeftSidebarNav", () => { expect(window.localStorage.getItem("fusion:left-sidebar-width")).toBe("384"); }); + it("clamps and persists the narrower minimum drag resize width", () => { + renderSidebar(); + const sidebar = screen.getByTestId("left-sidebar-nav"); + const handle = screen.getByTestId("sidebar-nav-resize-handle"); + + expect(handle).toHaveAttribute("aria-valuemin", "160"); + + fireEvent.pointerDown(handle, { clientX: 224, pointerId: 1 }); + fireEvent.pointerMove(document, { clientX: 0 }); + fireEvent.pointerUp(document, { clientX: 0, pointerId: 1 }); + + expect(sidebar).toHaveStyle({ width: "160px", minWidth: "160px" }); + expect(window.localStorage.getItem("fusion:left-sidebar-width")).toBe("160"); + }); + it("restores persisted width and keyboard-resizes within clamps", () => { window.localStorage.setItem("fusion:left-sidebar-width", "999"); renderSidebar(); @@ -401,6 +416,28 @@ describe("LeftSidebarNav", () => { expect(window.localStorage.getItem("fusion:left-sidebar-width")).toBe("336"); }); + it("restores below-minimum persisted width to the narrower minimum", () => { + window.localStorage.setItem("fusion:left-sidebar-width", "120"); + renderSidebar(); + + expect(screen.getByTestId("left-sidebar-nav")).toHaveStyle({ width: "160px", minWidth: "160px" }); + expect(screen.getByTestId("sidebar-nav-resize-handle")).toHaveAttribute("aria-valuenow", "160"); + }); + + it("keyboard resizing clamps and persists the narrower minimum width", () => { + renderSidebar(); + + const sidebar = screen.getByTestId("left-sidebar-nav"); + const handle = screen.getByTestId("sidebar-nav-resize-handle"); + + fireEvent.keyDown(handle, { key: "ArrowLeft", shiftKey: true }); + fireEvent.keyDown(handle, { key: "ArrowLeft", shiftKey: true }); + + expect(sidebar).toHaveStyle({ width: "160px", minWidth: "160px" }); + expect(handle).toHaveAttribute("aria-valuenow", "160"); + expect(window.localStorage.getItem("fusion:left-sidebar-width")).toBe("160"); + }); + it("routes clicks to view changes, todos view, and settings callback", () => { const onOpenSettings = vi.fn(); const { onChangeView } = renderSidebar({ todosEnabled: true, onOpenSettings });