From 3b6e2d98d3f26206b28289d6292b77653b27ae79 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Thu, 12 Feb 2026 08:00:21 +0000 Subject: [PATCH] feat(frontend): stack TaskBoard columns on mobile Mobile-first layout for TaskBoard: stack Inbox/In Progress/Review/Done vertically to avoid horizontal scroll, while retaining the horizontal kanban layout on larger screens. Includes component tests and frontend README notes for mobile validation. --- frontend/README.md | 10 ++++ .../components/organisms/TaskBoard.test.tsx | 51 +++++++++++++++++++ .../src/components/organisms/TaskBoard.tsx | 23 +++++---- 3 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 frontend/src/components/organisms/TaskBoard.test.tsx diff --git a/frontend/README.md b/frontend/README.md index 33052326..7ca1e33c 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -108,6 +108,16 @@ It will: - add `Authorization: Bearer ` automatically from local mode token or Clerk session - parse errors into an `ApiError` with status + parsed response body +## Mobile / responsive UI validation + +When changing UI intended to be mobile-ready, validate in Chrome (or similar) using the device toolbar at common widths (e.g. **320px**, **375px**, **768px**). + +Quick checklist: +- No horizontal scroll +- Primary actions reachable without precision taps +- Focus rings visible when tabbing +- Modals/popovers not clipped + ## Common commands From `frontend/`: diff --git a/frontend/src/components/organisms/TaskBoard.test.tsx b/frontend/src/components/organisms/TaskBoard.test.tsx new file mode 100644 index 00000000..35fb111f --- /dev/null +++ b/frontend/src/components/organisms/TaskBoard.test.tsx @@ -0,0 +1,51 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { TaskBoard } from "./TaskBoard"; + +describe("TaskBoard", () => { + it("uses a mobile-first stacked layout (no horizontal scroll) with responsive kanban columns on larger screens", () => { + render( + , + ); + + const board = screen.getByTestId("task-board"); + + expect(board.className).toContain("overflow-x-hidden"); + expect(board.className).toContain("sm:overflow-x-auto"); + expect(board.className).toContain("grid-cols-1"); + expect(board.className).toContain("sm:grid-flow-col"); + }); + + it("only sticks column headers on larger screens (avoids weird stacked sticky headers on mobile)", () => { + render( + , + ); + + const header = screen + .getByRole("heading", { name: "Inbox" }) + .closest(".column-header"); + expect(header?.className).toContain("sm:sticky"); + expect(header?.className).toContain("sm:top-0"); + // Ensure we didn't accidentally keep unscoped sticky behavior. + expect(header?.className).not.toContain("sticky top-0"); + }); +}); diff --git a/frontend/src/components/organisms/TaskBoard.tsx b/frontend/src/components/organisms/TaskBoard.tsx index 362d75d2..003fbba3 100644 --- a/frontend/src/components/organisms/TaskBoard.tsx +++ b/frontend/src/components/organisms/TaskBoard.tsx @@ -334,7 +334,13 @@ export const TaskBoard = memo(function TaskBoard({ return (
{columns.map((column) => { const columnTasks = grouped[column.status] ?? []; @@ -385,18 +391,19 @@ export const TaskBoard = memo(function TaskBoard({
-
+
@@ -473,9 +480,7 @@ export const TaskBoard = memo(function TaskBoard({ onClick={() => onTaskSelect?.(task)} draggable={!readOnly && !task.is_blocked} isDragging={draggingId === task.id} - onDragStart={ - readOnly ? undefined : handleDragStart(task) - } + onDragStart={readOnly ? undefined : handleDragStart(task)} onDragEnd={readOnly ? undefined : handleDragEnd} />