From 418beaf11be661c363634d2ac6a4f4df754ff6d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Fri, 12 Jun 2026 08:54:50 +0100 Subject: [PATCH] fix: move changes stage action to file icon slot (#1349) --- .../task/changes-panel-file-row.test.tsx | 68 ++++++++++++++++++ .../task/changes-panel-file-row.tsx | 71 ++++++++++++++----- 2 files changed, 121 insertions(+), 18 deletions(-) diff --git a/apps/web/components/task/changes-panel-file-row.test.tsx b/apps/web/components/task/changes-panel-file-row.test.tsx index 4001594461..2a08eb137f 100644 --- a/apps/web/components/task/changes-panel-file-row.test.tsx +++ b/apps/web/components/task/changes-panel-file-row.test.tsx @@ -155,6 +155,74 @@ describe("FileRow hover swap (stats <-> actions occupy same cell)", () => { }); }); +describe("FileRow tree-mode hover stage action", () => { + it("keeps the stage button in the icon slot instead of the right hover actions", () => { + const { container } = render( + + + , + ); + + const row = container.querySelector("[data-changes-file='src/file.go']") as HTMLElement | null; + expect(row).not.toBeNull(); + + const iconSlot = row!.querySelector("[data-testid='file-row-icon-action-slot']"); + expect(iconSlot).not.toBeNull(); + expect(iconSlot!.classList.contains("size-4")).toBe(true); + expect(iconSlot!.querySelector("button[title='Stage file']")).not.toBeNull(); + + const rightActions = row!.querySelector("[data-testid='file-row-hover-actions']"); + expect(rightActions).not.toBeNull(); + expect(rightActions!.querySelector("button[title='Stage file']")).toBeNull(); + }); + + it("keeps the unstage button in the same icon slot for staged files", () => { + const { container } = render( + + + , + ); + + const row = container.querySelector( + "[data-changes-file='src/staged.go']", + ) as HTMLElement | null; + expect(row).not.toBeNull(); + + const iconSlot = row!.querySelector("[data-testid='file-row-icon-action-slot']"); + expect(iconSlot).not.toBeNull(); + expect(iconSlot!.classList.contains("size-4")).toBe(true); + expect(iconSlot!.querySelector("button[title='Unstage file']")).not.toBeNull(); + + const rightActions = row!.querySelector("[data-testid='file-row-hover-actions']"); + expect(rightActions).not.toBeNull(); + expect(rightActions!.querySelector("button[title='Unstage file']")).toBeNull(); + }); +}); + describe("FileRow active-tab highlight", () => { it("renders data-active='true' and bg-accent/60 when isActive", () => { const { container } = render( diff --git a/apps/web/components/task/changes-panel-file-row.tsx b/apps/web/components/task/changes-panel-file-row.tsx index 7a3457519a..c8650b45a1 100644 --- a/apps/web/components/task/changes-panel-file-row.tsx +++ b/apps/web/components/task/changes-panel-file-row.tsx @@ -101,7 +101,15 @@ export function FileRow({ style={indentPx ? { paddingLeft: indentPx } : undefined} > {treeMode ? ( - + ) : ( - ) : null - } /> ); } +function TreeModeFileActionSlot({ + name, + isPending, + staged, + path, + repo, + onStage, + onUnstage, +}: { + name: string; + isPending: boolean; + staged: boolean; + path: string; + repo?: string; + onStage: (path: string, repo?: string) => void; + onUnstage: (path: string, repo?: string) => void; +}) { + return ( +
+ +
+ +
+
+ ); +} + function StageButton({ isPending, staged, @@ -209,17 +245,17 @@ function FileRowActions({ repo, onDiscard, onEditFile, - stageButton, }: { path: string; repo?: string; onDiscard: (path: string, repo?: string) => void; onEditFile: (path: string) => void; - /** Tree mode: stage/unstage control rendered alongside the other hover actions. */ - stageButton?: React.ReactNode; }) { return ( -
+
); }