feat(ui): clarification des contrôles d'en-tête DockControls (#42)
Refinement UX suite #26 : clarifie les contrôles DockControls dans l'en-tête des panneaux. Frontend-pur. QA vert : tsc --noEmit exit 0, vitest 62 fichiers / 616 tests passés. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -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" },
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@ -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 (
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<Button
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
aria-label={`dock ${title} left`}
|
||||
disabled={isDockedTo(placement, "left")}
|
||||
title="Ancré à gauche"
|
||||
aria-label={`Ancré à gauche — ${title}`}
|
||||
aria-pressed={isDockedTo(placement, "left")}
|
||||
className={cn(isDockedTo(placement, "left") && activeClass)}
|
||||
onClick={() => setPlacement(panel, { dock: "left" })}
|
||||
>
|
||||
◧
|
||||
</Button>
|
||||
<Button
|
||||
⇤
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
aria-label={`dock ${title} right`}
|
||||
disabled={isDockedTo(placement, "right")}
|
||||
title="Ancré à droite"
|
||||
aria-label={`Ancré à droite — ${title}`}
|
||||
aria-pressed={isDockedTo(placement, "right")}
|
||||
className={cn(isDockedTo(placement, "right") && activeClass)}
|
||||
onClick={() => setPlacement(panel, { dock: "right" })}
|
||||
>
|
||||
◨
|
||||
</Button>
|
||||
<Button
|
||||
⇥
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
aria-label={`float ${title}`}
|
||||
disabled={placement === "floating"}
|
||||
title="Flottant"
|
||||
aria-label={`Flottant — ${title}`}
|
||||
aria-pressed={placement === "floating"}
|
||||
className={cn(placement === "floating" && activeClass)}
|
||||
onClick={() => setPlacement(panel, "floating")}
|
||||
>
|
||||
⧉
|
||||
</Button>
|
||||
<Button
|
||||
□
|
||||
</IconButton>
|
||||
{panel !== "projects" && (
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
aria-label={`open ${title} in new window`}
|
||||
disabled={!active || isDetached(placement)}
|
||||
title="Fenêtre détachée"
|
||||
aria-label={`Fenêtre détachée — ${title}`}
|
||||
aria-pressed={isDetached(placement)}
|
||||
disabled={!active}
|
||||
className={cn(isDetached(placement) && activeClass)}
|
||||
onClick={() => detachPanel(panel)}
|
||||
>
|
||||
⤢
|
||||
</Button>
|
||||
<Button
|
||||
↗
|
||||
</IconButton>
|
||||
)}
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
aria-label={`close ${title}`}
|
||||
title="Fermé"
|
||||
aria-label={`Fermé — ${title}`}
|
||||
onClick={() => closePanel(panel)}
|
||||
>
|
||||
✕
|
||||
</Button>
|
||||
×
|
||||
</IconButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user