From 1ff87147b7997753dea62243fb7225c6e2c753e5 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 21 Jun 2026 02:59:22 -0700 Subject: [PATCH] FN-6831: narrow list sidebar and wrap task titles Refine the desktop list split pane so narrower sidebars still show readable task titles. - Lower the list-view sidebar minimum width from 280px to 200px across resize and keyboard handling. - Clamp desktop list task titles to two wrapped lines with overflow protection. - Update ListView coverage for the smaller persisted/keyboard minimum and title-clamp CSS. Files changed: packages/dashboard/app/components/ListView.css | 10 +++++++++- packages/dashboard/app/components/ListView.tsx | 2 +- .../app/components/__tests__/ListView.test.tsx | 22 +++++++++++++++++----- 3 files changed, 27 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-6831 Fusion-Task-Lineage: ac3e455d-8cb0-4f42-ac5c-d735e56d2b89 --- .../dashboard/app/components/ListView.css | 10 ++++++++- .../dashboard/app/components/ListView.tsx | 2 +- .../components/__tests__/ListView.test.tsx | 22 ++++++++++++++----- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/dashboard/app/components/ListView.css b/packages/dashboard/app/components/ListView.css index 1029795b02..5e50cb4038 100644 --- a/packages/dashboard/app/components/ListView.css +++ b/packages/dashboard/app/components/ListView.css @@ -581,10 +581,18 @@ FN-6529 requires list-view agent-active tasks to use a simple static highlight i max-width: 100%; } +/* FNXC:ListView 2026-06-21-01:42: Desktop list task titles clamp to two wrapped lines so longer task names remain legible in the narrower split sidebar without allowing unbounded row growth. */ .list-title-text { + display: -webkit-box; + max-width: 100%; + min-width: 0; overflow: hidden; + overflow-wrap: anywhere; text-overflow: ellipsis; - white-space: nowrap; + white-space: normal; + word-break: break-word; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; } .list-cell-date { diff --git a/packages/dashboard/app/components/ListView.tsx b/packages/dashboard/app/components/ListView.tsx index 56adba3ebd..a1a3c9c857 100644 --- a/packages/dashboard/app/components/ListView.tsx +++ b/packages/dashboard/app/components/ListView.tsx @@ -179,7 +179,7 @@ function readSidebarWidth(projectId?: string): number { return fallbackWidth; } -const LIST_SIDEBAR_MIN_WIDTH = 280; +const LIST_SIDEBAR_MIN_WIDTH = 200; // FNXC:ListView 2026-06-21-01:42: The desktop task-list split sidebar minimum is 200 instead of 280 so users can shrink the left panel further on narrow desktop layouts while resize, keyboard, and ARIA paths share one clamp value. const LIST_SIDEBAR_MAX_RATIO = 0.65; const LIST_SIDEBAR_KEYBOARD_STEP = 16; diff --git a/packages/dashboard/app/components/__tests__/ListView.test.tsx b/packages/dashboard/app/components/__tests__/ListView.test.tsx index a3b0614a5a..18df7d3e3b 100644 --- a/packages/dashboard/app/components/__tests__/ListView.test.tsx +++ b/packages/dashboard/app/components/__tests__/ListView.test.tsx @@ -1,3 +1,4 @@ +import { readFileSync } from "node:fs"; import { describe, it, expect, vi } from "vitest"; import { useState } from "react"; import { render, screen, fireEvent, waitFor, within, act } from "@testing-library/react"; @@ -400,7 +401,7 @@ describe("ListView", () => { expect(document.getElementById("list-view-options-panel")).toBeNull(); }); - it("displays tasks in table format", () => { + it("displays tasks in table format with two-line title styling", () => { const tasks = [ createMockTask({ id: "FN-001", title: "First Task" }), createMockTask({ id: "FN-002", title: "Second Task" }), @@ -412,6 +413,13 @@ describe("ListView", () => { expect(screen.getByText("First Task")).toBeDefined(); expect(screen.getByText("FN-002")).toBeDefined(); expect(screen.getByText("Second Task")).toBeDefined(); + const listTitleTextRule = readFileSync("app/components/ListView.css", "utf8").match(/\.list-title-text\s*\{[^}]*\}/)?.[0] ?? ""; + expect(listTitleTextRule).toContain("display: -webkit-box"); + expect(listTitleTextRule).toContain("-webkit-line-clamp: 2"); + expect(listTitleTextRule).toContain("-webkit-box-orient: vertical"); + expect(listTitleTextRule).toContain("white-space: normal"); + expect(listTitleTextRule).toContain("overflow-wrap: anywhere"); + expect(listTitleTextRule).not.toContain("white-space: nowrap"); }); it("shows fast indicator in desktop rows only for fast-mode tasks", () => { @@ -1072,23 +1080,27 @@ describe("ListView", () => { viewportSpy.mockRestore(); }); - it("supports keyboard resizing on the desktop split-pane handle", () => { + it("supports keyboard resizing on the desktop split-pane handle", async () => { const viewportSpy = mockDesktopViewport(); const clientWidthSpy = vi.spyOn(window.HTMLElement.prototype, "clientWidth", "get").mockReturnValue(1000); + localStorage.setItem(scopedStorageKey("kb-dashboard-list-sidebar-width"), "150"); const tasks = [createMockTask({ id: "FN-001", title: "Task" })]; renderListView({ tasks }); + await waitFor(() => expect(screen.getByTestId("list-split-sidebar")).toHaveStyle({ width: "200px" })); const handle = screen.getByTestId("list-split-resize-handle"); const startWidth = Number(handle.getAttribute("aria-valuenow")); expect(handle).toHaveAttribute("tabindex", "0"); - expect(handle).toHaveAttribute("aria-valuemin", "280"); - expect(Number(handle.getAttribute("aria-valuemax"))).toBeGreaterThanOrEqual(280); + expect(handle).toHaveAttribute("aria-valuemin", "200"); + expect(Number(handle.getAttribute("aria-valuemax"))).toBeGreaterThanOrEqual(200); fireEvent.keyDown(handle, { key: "ArrowRight" }); - expect(Number(handle.getAttribute("aria-valuenow"))).toBeGreaterThan(startWidth); + fireEvent.keyDown(handle, { key: "Home" }); + expect(handle).toHaveAttribute("aria-valuenow", "200"); + expect(screen.getByTestId("list-split-sidebar")).toHaveStyle({ width: "200px" }); clientWidthSpy.mockRestore(); viewportSpy.mockRestore(); });