diff --git a/frontend/src/features/projects/ProjectsView.docking.test.tsx b/frontend/src/features/projects/ProjectsView.docking.test.tsx index de40a12..a58ab75 100644 --- a/frontend/src/features/projects/ProjectsView.docking.test.tsx +++ b/frontend/src/features/projects/ProjectsView.docking.test.tsx @@ -68,7 +68,7 @@ describe("ProjectsView view docking (#22)", () => { expect(dialog).toBeTruthy(); // Dock it to the left via the header control. - fireEvent.click(within(dialog).getByRole("button", { name: "dock Git left" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Ancré à gauche — Git" })); // The modal window is gone; the Git view now lives in the left dock column. await waitFor(() => @@ -85,13 +85,13 @@ describe("ProjectsView view docking (#22)", () => { openPanelFloating("Git"); const dialog = await screen.findByRole("dialog", { name: "Git" }); - fireEvent.click(within(dialog).getByRole("button", { name: "dock Git left" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Ancré à gauche — Git" })); const leftDock = await screen.findByRole("complementary", { name: "left dock" }); const leftHeader = within(leftDock).getByLabelText("Git panel"); // Move it to the right dock from the docked header control. fireEvent.click( - within(leftHeader).getByRole("button", { name: "dock Git right" }), + within(leftHeader).getByRole("button", { name: "Ancré à droite — Git" }), ); await waitFor(() => @@ -109,7 +109,7 @@ describe("ProjectsView view docking (#22)", () => { fireEvent.click( within(await screen.findByRole("dialog", { name: "Git" })).getByRole( "button", - { name: "dock Git left" }, + { name: "Ancré à gauche — Git" }, ), ); // Dock Agents right. @@ -117,7 +117,7 @@ describe("ProjectsView view docking (#22)", () => { fireEvent.click( within(await screen.findByRole("dialog", { name: "Agents" })).getByRole( "button", - { name: "dock Agents right" }, + { name: "Ancré à droite — Agents" }, ), ); diff --git a/frontend/src/features/projects/ProjectsView.tsx b/frontend/src/features/projects/ProjectsView.tsx index 0cb247e..f37fc25 100644 --- a/frontend/src/features/projects/ProjectsView.tsx +++ b/frontend/src/features/projects/ProjectsView.tsx @@ -40,9 +40,11 @@ import { Button, DockRegion, FloatingWindow, + IconButton, Input, MenuBar, Panel, + cn, zIndex, type FloatingWindowSize, type MenuBarItem, @@ -469,58 +471,72 @@ export function ProjectsView() { const rightPanels = panelsDockedTo(placements, "right"); const floatingList = floatingPanels(placements); - // Placement toggle buttons for a view's header — the "flottant ↔ ancré - // (gauche/droite)" control from the contract. The button for the current slot - // is disabled so the active placement reads at a glance. + // Placement controls for a view's header — labelled icon-buttons aligned 1:1 + // with the « Panneaux » menu wording (#42). Order: ⇤ gauche · ⇥ droite · □ + // flottant · ↗ fenêtre détachée · × fermé. The *current* placement reads as + // ACTIVE (aria-pressed + ring), never disabled — a disabled control loses its + // tooltip/keyboard focus and conflates "impossible" with "already selected". + // Only genuinely-impossible actions are disabled (detach with no active + // project). All labelling flows through `title` + `aria-label`; no visible + // text. The detach control is hidden for the main-window-only `projects` view. function DockControls({ panel }: { panel: PanelId }) { const placement = placementOf(placements, panel); const title = PANEL_TITLE[panel]; + // Active (current-placement) styling — visually distinct, still clickable. + const activeClass = "bg-raised text-content ring-1 ring-border-strong"; return (