From 0a11614d7ff0ebf1b1841fc8a565e4a34dfeb58b Mon Sep 17 00:00:00 2001 From: keval shah Date: Mon, 25 May 2026 16:49:44 -0400 Subject: [PATCH] =?UTF-8?q?fix(playground):=20align=20Select=20All=20bulk-?= =?UTF-8?q?delete=20icon=20with=20session=20=E2=8B=AE=20column=20(#13313)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bulk-delete trash icon in the Select All row sat off-axis from the ``⋮`` MoreMenu icons of the session rows below: lower (the wrapper had ``py-1 mb-1`` while session rows are ``h-8`` with no vertical padding) and further left (the trash button was ``p-0`` inside an unpadded column while the SessionMoreMenu trigger is ``h-8 w-8 p-2``). Mirror the SessionSelector row shape exactly: - Outer wrapper: ``flex h-8 items-center justify-between`` — no extra vertical padding, no bottom margin. - Trash button: ``size="icon" h-8 w-8 p-2 rounded`` — matches the SessionMoreMenu trigger inner padding so the icon centers in the same 32×32 column as ``⋮`` below. - Inner padding moved to the Select All checkbox cluster (``px-2``) so the left edge still lines up with the SessionSelector internal padding. Drive-bys (touched lines biome would flag otherwise): - Remove unused ``isDefaultSession`` local in the sessions map. - Sort the alertStore/flowStore imports. - Convert the Select All click target from ``
`` to a real `` + ), +})); + +const baseProps = { + sessions: ["flow-1", "New Session 0", "New Session 1"], + onSessionSelect: jest.fn(), + currentSessionId: "flow-1", + onDeleteSession: jest.fn(), + onRenameSession: jest.fn().mockResolvedValue(undefined), + onBulkDeleteSessions: jest.fn(), +}; + +const checkAll = () => + fireEvent.click(screen.getByTestId("select-all-checkbox")); + +describe("ChatSidebar — Select All row alignment", () => { + it("does not render the bulk-delete button while nothing is selected", () => { + render(); + expect(screen.queryByTestId("bulk-delete-button")).not.toBeInTheDocument(); + }); + + it("renders the bulk-delete button with the same h-8 w-8 p-2 shape as a SessionMoreMenu trigger", () => { + render(); + checkAll(); + const trash = screen.getByTestId("bulk-delete-button"); + expect(trash).toBeInTheDocument(); + expect(trash.className).toContain("h-8"); + expect(trash.className).toContain("w-8"); + expect(trash.className).toContain("p-2"); + // No leftover sm-button padding that would skew horizontal centering. + expect(trash.className).not.toContain("p-0"); + }); + + it("hosts the bulk-delete button in an h-8 row with no extra vertical padding", () => { + render(); + checkAll(); + const trash = screen.getByTestId("bulk-delete-button"); + // Walk up to the Select All row wrapper. Its className must include + // `h-8` and must not add `py-1` / `py-*` padding that would push the + // trash button's center down relative to the SessionSelector rows. + const row = trash.closest("div.flex.items-center.justify-between"); + expect(row).not.toBeNull(); + expect(row!.className).toContain("h-8"); + expect(row!.className).not.toMatch(/\bpy-\d/); + expect(row!.className).not.toMatch(/\bmb-\d/); + }); + + it("calls onBulkDeleteSessions with the checked sessions when clicked", () => { + const onBulkDeleteSessions = jest.fn(); + render( + , + ); + checkAll(); + fireEvent.click(screen.getByTestId("bulk-delete-button")); + expect(onBulkDeleteSessions).toHaveBeenCalledTimes(1); + const [ids] = onBulkDeleteSessions.mock.calls[0]; + expect(ids).toEqual( + expect.arrayContaining(["New Session 0", "New Session 1"]), + ); + // Default session (currentFlowId) is non-selectable and must not be + // included in the bulk delete payload. + expect(ids).not.toContain("flow-1"); + }); +}); diff --git a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/chat-sidebar.tsx b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/chat-sidebar.tsx index 7d2ef4a8ce..49c8692973 100644 --- a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/chat-sidebar.tsx +++ b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/chat-sidebar.tsx @@ -3,8 +3,8 @@ import { useTranslation } from "react-i18next"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import ShadTooltip from "@/components/common/shadTooltipComponent"; import { Button } from "@/components/ui/button"; -import useFlowStore from "@/stores/flowStore"; import useAlertStore from "@/stores/alertStore"; +import useFlowStore from "@/stores/flowStore"; import { cn } from "@/utils/utils"; import { useGetFlowId } from "../../../hooks/use-get-flow-id"; import { SessionSelector } from "./session-selector"; @@ -142,18 +142,23 @@ export function ChatSidebar({ ) : (
{sessions.map((session, index) => { - const isDefaultSession = session === currentFlowId; const isFirstNonDefaultSession = index > 0 && sessions[index - 1] === currentFlowId; return (
- {/* Show Select All controls after the default session */} + {/* Show Select All controls after the default session. + Wrapper is sized + padded to mirror a SessionSelector + row (h-8, no extra vertical padding) so the trash + button lines up vertically and horizontally with the + `⋮` MoreMenu triggers in the session rows below. */} {isFirstNonDefaultSession && selectableSessions.length > 0 && ( -
-
+ - - )} -
+ + + + )}
)}