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
This commit is contained in:
gsxdsm
2026-06-21 11:07:17 -07:00
parent b20a25c526
commit 47be640bfb
3 changed files with 45 additions and 1 deletions

View File

@@ -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;

View File

@@ -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";

View File

@@ -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 });