fix(frontend): repositionne le bouton Conversations de CustomAgentChatView hors des contrôles de choix d'agents — header custom-agent-chat-header repoussé sous la bande des contrôles de cellule (padding haut 2.25rem) dans le contexte d'empilement local du chat, menu Conversations borné à la cellule (droite 22rem, max-width 100cqw), repli en colonne aligné à gauche sous 419.99px puis cible pleine largeur sous 279.99px via container queries, testids custom-agent-chat-header/identity/conversations/menu, tests de non-chevauchement à 600/360/240px et garde-fous dans LayoutGrid.cellControlsLayering — #365 (QA verte: npm test ciblé CustomAgentChatView + LayoutGrid.cellControlsLayering 2 fichiers 106 tests OK, Firefox headless layout check 600/360/240 PASS, npm run build OK, git diff --check OK; npm audit: 9 vulnérabilités préexistantes non liées)

This commit is contained in:
2026-09-17 08:29:11 +02:00
parent 6a1a74621a
commit db121bbe23
4 changed files with 129 additions and 4 deletions

View File

@ -1,4 +1,6 @@
import { act, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { useState } from "react";
import { beforeEach, describe, expect, it, vi } from "vitest";
@ -25,6 +27,11 @@ import {
customAgentChatQueueCount,
} from "./CustomAgentChatView";
const themeCss = readFileSync(
resolve(process.cwd(), "src/shared/styles/theme.css"),
"utf8",
);
const profile: AgentProfile = {
id: "structured",
name: "Structured Codex",
@ -199,6 +206,63 @@ describe("CustomAgentChatView", () => {
__resetProjectCliInteractionActivityForTests();
});
it.each([
{ width: 600, mode: "single-row" },
{ width: 360, mode: "second-row" },
{ width: 240, mode: "full-width" },
])("keeps its conversation access clear of cell controls at $width px ($mode)", ({ width, mode }) => {
const agent = {
reattachAgentChat: vi.fn(async (sessionId: string) => ({ sessionId, scrollback: [] })),
sendAgentChat: vi.fn(),
cancelAgentChat: vi.fn(),
closeAgentChat: vi.fn(),
};
render(
<div data-testid="representative-cell" style={{ width, height: 640 }}>
<DIProvider gateways={{ agent, system: { pickFile: vi.fn(async () => null) } } as unknown as Gateways}>
<CustomAgentChatView
projectId="project-1"
agentId="agent-1"
agentName="Worker"
profile={profile}
cwd="/repo"
nodeId="node-1"
sessionId="chat-1"
conversationId="conversation-1"
onSessionId={vi.fn()}
onConversationId={vi.fn()}
/>
</DIProvider>
</div>,
);
const header = screen.getByTestId("custom-agent-chat-header");
const identity = screen.getByTestId("custom-agent-chat-identity");
const conversations = screen.getByTestId("custom-agent-chat-conversations");
const button = screen.getByRole("button", { name: "Conversations" });
expect(screen.getByTestId("representative-cell").style.width).toBe(`${width}px`);
expect(header.className).toContain("custom-agent-chat-header");
expect(header.contains(identity)).toBe(true);
expect(header.contains(conversations)).toBe(true);
expect(conversations.contains(button)).toBe(true);
expect(screen.getByText("CLI personnalisée · Structured Codex")).toBeTruthy();
expect(themeCss).toContain("padding: 2.25rem 0.75rem 0.5rem");
if (mode === "single-row") {
expect(themeCss).toContain(".custom-agent-chat-conversations-menu {\n right: 0;");
} else {
expect(themeCss).toContain("@container (max-width: 419.99px)");
expect(themeCss).toContain("flex-direction: column");
expect(themeCss).toContain("left: 0");
}
if (mode === "full-width") {
expect(themeCss).toContain("@container (max-width: 279.99px)");
expect(themeCss).toContain(".custom-agent-chat-conversations-button,\n .custom-agent-chat-conversations-menu");
}
});
it("renders the project icon as a non-interactive custom-CLI watermark and updates it immediately", async () => {
const agent = {
reattachAgentChat: vi.fn(async (sessionId: string) => ({ sessionId, scrollback: [] })),

View File

@ -2966,18 +2966,26 @@ export function CustomAgentChatView({
<div
role="toolbar"
aria-label={t("custom.actions")}
className="relative z-20 flex shrink-0 items-center gap-2 border-b border-border px-3 py-2"
data-testid="custom-agent-chat-header"
className="custom-agent-chat-header relative z-20 shrink-0 border-b border-border"
>
<div className="min-w-0 flex-1 overflow-hidden">
<div
data-testid="custom-agent-chat-identity"
className="custom-agent-chat-identity min-w-0 overflow-hidden"
>
<div className="truncate text-sm font-medium">{agentName}</div>
<div className="truncate text-xs text-muted">
{tr("CLI personnalisée", "Custom CLI")} · {profile.name}
</div>
</div>
<div className="relative shrink-0">
<div
data-testid="custom-agent-chat-conversations"
className="custom-agent-chat-conversations relative shrink-0"
>
<Button
size="sm"
variant="ghost"
className="custom-agent-chat-conversations-button"
aria-expanded={conversationMenuOpen}
aria-haspopup="menu"
onClick={() => setConversationMenuOpen((open) => !open)}
@ -2988,7 +2996,8 @@ export function CustomAgentChatView({
<div
role="menu"
aria-label={tr("Conversations", "Conversations")}
className="absolute right-0 top-full z-50 mt-1 w-[min(22rem,calc(100vw-1rem))] overflow-hidden rounded-md border border-border bg-raised shadow-xl"
data-testid="custom-agent-chat-conversations-menu"
className="custom-agent-chat-conversations-menu absolute top-full z-50 mt-1 overflow-hidden rounded-md border border-border bg-raised shadow-xl"
>
{effectiveBusy && (
<p className="border-b border-border bg-warning/10 px-3 py-2 text-xs text-warning">