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
This commit is contained in:
gsxdsm
2026-06-21 02:59:22 -07:00
parent 9d55f29c7a
commit 1ff87147b7
3 changed files with 27 additions and 7 deletions

View File

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

View File

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

View File

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