From b152e33b606a9119436c8bf32db822524ab6f957 Mon Sep 17 00:00:00 2001 From: Blomios Date: Mon, 13 Jul 2026 12:42:31 +0200 Subject: [PATCH] fix(projects): rendre visible le bouton + d'ajout d'onglet projet (#46) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le bouton + de la barre d'onglets projets était invisible (pas de variant d'affichage). Il est désormais rendu en permanence via un variant secondary, avec aria-pressed et title pour l'accessibilité, permettant d'ajouter un projet en onglet. Couvert par un nouveau test (ProjectTabs.test.tsx). Co-Authored-By: Claude Opus 4.8 --- .../features/projects/ProjectTabs.test.tsx | 76 +++++++++++++++++++ .../src/features/projects/ProjectTabs.tsx | 13 +++- 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 frontend/src/features/projects/ProjectTabs.test.tsx diff --git a/frontend/src/features/projects/ProjectTabs.test.tsx b/frontend/src/features/projects/ProjectTabs.test.tsx new file mode 100644 index 0000000..45ac3e5 --- /dev/null +++ b/frontend/src/features/projects/ProjectTabs.test.tsx @@ -0,0 +1,76 @@ +/** + * #46 — the project tab bar's "+" add-project button. It must be present, + * enabled and visibly a button in EVERY state (notably with zero open projects, + * where it is the only way to add a project as a tab), and clicking it opens the + * Projects panel. The visibility regression it guards against: the button used + * to render with the `ghost` variant (no border/background, muted text) so it + * read as faint text rather than a control. + */ +import { describe, it, expect, vi } from "vitest"; +import { render, screen, fireEvent } from "@testing-library/react"; + +import type { TabItem } from "@/shared"; +import { ProjectTabs } from "./ProjectTabs"; + +const TABS: TabItem[] = [ + { id: "a", label: "alpha" }, + { id: "b", label: "beta" }, +]; + +function renderTabs(props: Partial> = {}) { + const onOpenProjectsPanel = vi.fn(); + render( + , + ); + return { onOpenProjectsPanel }; +} + +describe("ProjectTabs add-project button (#46)", () => { + it("renders the + button when projects are open", () => { + renderTabs({ items: TABS }); + const btn = screen.getByRole("button", { name: "open projects panel" }); + expect(btn).toBeTruthy(); + expect(btn.textContent).toContain("+"); + expect((btn as HTMLButtonElement).disabled).toBe(false); + }); + + it("still renders the + button with ZERO open projects (only way to add one)", () => { + renderTabs({ items: [], activeTabId: null }); + // The empty placeholder is shown… + expect(screen.getByText("No open tabs.")).toBeTruthy(); + // …and the add-project button is still present and enabled. + const btn = screen.getByRole("button", { name: "open projects panel" }); + expect(btn).toBeTruthy(); + expect((btn as HTMLButtonElement).disabled).toBe(false); + }); + + it("is a visible bordered control, not the near-invisible ghost glyph", () => { + // Regression guard for #46: the secondary variant carries a border; the old + // ghost variant did not. Asserting the border class keeps the button from + // silently reverting to the invisible style. + renderTabs({ items: [] }); + const btn = screen.getByRole("button", { name: "open projects panel" }); + expect(btn.className).toContain("border"); + expect(btn.className).not.toContain("text-muted"); + }); + + it("reflects the panel-open state via aria-pressed", () => { + const { onOpenProjectsPanel } = renderTabs({ projectsPanelOpen: true }); + const btn = screen.getByRole("button", { name: "open projects panel" }); + expect(btn.getAttribute("aria-pressed")).toBe("true"); + expect(onOpenProjectsPanel).not.toHaveBeenCalled(); + }); + + it("opens the Projects panel on click, independent of any active project", () => { + const { onOpenProjectsPanel } = renderTabs({ items: [], activeTabId: null }); + fireEvent.click(screen.getByRole("button", { name: "open projects panel" })); + expect(onOpenProjectsPanel).toHaveBeenCalledTimes(1); + }); +}); diff --git a/frontend/src/features/projects/ProjectTabs.tsx b/frontend/src/features/projects/ProjectTabs.tsx index 2bec108..ecc6eb3 100644 --- a/frontend/src/features/projects/ProjectTabs.tsx +++ b/frontend/src/features/projects/ProjectTabs.tsx @@ -52,12 +52,21 @@ export function ProjectTabs({ className="flex-1" /> )} + {/* Add-project affordance (#46): always a bordered `secondary` button so + it reads as a clickable control in every state — including zero open + projects — instead of the near-invisible `ghost` glyph it used to be. + When the Projects panel is open it also shows the pressed ring. */}