From fde262a481f0428d1b4126f4de837d9aae744916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Almeida?= <57680069+zeval@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:19:53 +0000 Subject: [PATCH] feat(web): add collapsible sections to changes panel (#457) * feat(web): add collapsible sections to changes panel Sections (Unstaged, Staged, Commits, PR Changes, PR Commits) can now be collapsed/expanded by clicking the section header or chevron arrow. Includes Playwright E2E test validating collapse and expand behavior. * fix(web): add aria-expanded to collapsible section toggles Addresses CodeRabbit review: adds aria-expanded attribute to section toggle buttons for screen reader support, and asserts the attribute in the E2E test. * fix(web): render section toggle as button only when collapsible Non-collapsible sections keep a plain label to avoid ambiguous getByRole("button") matches in E2E tests (e.g. "Commit" action button vs "Commits (0)" section header). * fix(web): use exact match for Commit button in pre-hooks E2E test The collapsible "Commits" section toggle button now matches getByRole("button", { name: "Commit" }) via substring. Use exact matching to only target the action button. --- .../task/changes-panel-timeline.tsx | 43 ++++++-- apps/web/e2e/tests/git-changes-panel.spec.ts | 104 ++++++++++++++++++ .../e2e/tests/git-commit-pre-hooks.spec.ts | 3 +- 3 files changed, 142 insertions(+), 8 deletions(-) diff --git a/apps/web/components/task/changes-panel-timeline.tsx b/apps/web/components/task/changes-panel-timeline.tsx index b0f562d484..9b01c0cb65 100644 --- a/apps/web/components/task/changes-panel-timeline.tsx +++ b/apps/web/components/task/changes-panel-timeline.tsx @@ -1,10 +1,12 @@ "use client"; +import { useState } from "react"; import { IconGitCommit, IconGitPullRequest, IconCloudUpload, IconChevronDown, + IconChevronRight, IconLoader2, } from "@tabler/icons-react"; @@ -54,6 +56,7 @@ function TimelineSection({ action, isLast, children, + collapsible = true, "data-testid": testId, }: { dotColor: string; @@ -62,8 +65,12 @@ function TimelineSection({ action?: React.ReactNode; isLast?: boolean; children?: React.ReactNode; + collapsible?: boolean; "data-testid"?: string; }) { + const [collapsed, setCollapsed] = useState(false); + const canCollapse = collapsible && !!label; + return (
{/* Vertical line + dot */} @@ -77,18 +84,38 @@ function TimelineSection({ {/* Header */} {label && (
- - {label} - {typeof count === "number" && ( - ({count}) - )} - + {canCollapse ? ( + + ) : ( + + {label} + {typeof count === "number" && ( + ({count}) + )} + + )} {action}
)} {/* Children (file list, buttons, etc.) */} - {children} + {!collapsed && children}
); @@ -359,6 +386,7 @@ export function PRFilesSection({ files, isLast, onOpenDiff }: PRFilesSectionProp label="PR Changes" count={files.length} isLast={isLast} + data-testid="pr-changes-section" > {files.length > 0 && (