merge(frontend): fix #365 — repositionne le bouton Conversations de CustomAgentChatView hors des contrôles de choix d'agents (QA verte)
This commit is contained in:
@ -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: [] })),
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -415,9 +415,12 @@ describe("LayoutGrid — ticket #48 cell control layering", () => {
|
||||
const customToolbar = screen.getByRole("toolbar", {
|
||||
name: "custom agent chat actions",
|
||||
});
|
||||
const conversations = screen.getByRole("button", { name: "Conversations" });
|
||||
const mutate = vi.spyOn(setup.layout, "mutateLayout");
|
||||
|
||||
expect(customToolbar.className).toContain("z-20");
|
||||
expect(customToolbar.className).toContain("custom-agent-chat-header");
|
||||
expect(customToolbar.contains(conversations)).toBe(true);
|
||||
expect(screen.getByLabelText(`Sélecteur d’agent ${setup.bId}`)).toBeTruthy();
|
||||
expect(screen.getByRole("group", {
|
||||
name: `Mode CLI de l’agent ${setup.bId}`,
|
||||
|
||||
@ -107,6 +107,55 @@
|
||||
container-type: size;
|
||||
}
|
||||
|
||||
/* The LayoutGrid cell controls occupy the top strip as an absolute sibling.
|
||||
Keep the custom-chat header below that strip, inside the chat's local
|
||||
stacking context, so its conversation affordance can never sit underneath
|
||||
the agent selector/split/close controls. */
|
||||
.custom-agent-chat-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 2.25rem 0.75rem 0.5rem;
|
||||
}
|
||||
|
||||
.custom-agent-chat-identity {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.custom-agent-chat-conversations-menu {
|
||||
right: 0;
|
||||
width: 22rem;
|
||||
max-width: calc(100cqw - 1.5rem);
|
||||
}
|
||||
|
||||
/* Medium split cells put the action on its own left-aligned row while
|
||||
keeping the menu constrained to the cell rather than the viewport. */
|
||||
@container (max-width: 419.99px) {
|
||||
.custom-agent-chat-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.custom-agent-chat-identity {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-agent-chat-conversations-menu {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Very narrow cells retain a usable, non-overlapping full-width target. */
|
||||
@container (max-width: 279.99px) {
|
||||
.custom-agent-chat-conversations,
|
||||
.custom-agent-chat-conversations-button,
|
||||
.custom-agent-chat-conversations-menu {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.project-cell-illustration-area {
|
||||
container-type: size;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user