From 05402c75443da8f8b6bfb4d4721f7aed13db0118 Mon Sep 17 00:00:00 2001 From: Blomios Date: Tue, 28 Jul 2026 18:29:17 +0200 Subject: [PATCH] =?UTF-8?q?fix(tickets):=20dropdowns=20ne=20sortent=20plus?= =?UTF-8?q?=20du=20viewport=20en=20bas=20d'=C3=A9cran=20(#3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remplace les setProfileId(e.target.value)} - > - - {vm.profiles.map((p) => ( - - ))} - + options={[ + { + value: "", + label: + vm.profiles.length === 0 + ? "Aucun profil disponible" + : "Choisir un profil IA…", + }, + ...vm.profiles.map((profile) => ({ + value: profile.id, + label: profile.name, + })), + ]} + onChange={setProfileId} + /> + {open && + position && + createPortal( +
+ {options.map((option) => ( + + ))} +
, + document.body, + )} + + ); +} diff --git a/frontend/src/features/tickets/TicketsPanel.tsx b/frontend/src/features/tickets/TicketsPanel.tsx index b733b0d..cca7727 100644 --- a/frontend/src/features/tickets/TicketsPanel.tsx +++ b/frontend/src/features/tickets/TicketsPanel.tsx @@ -10,12 +10,13 @@ import { useEffect, useState } from "react"; import type { Sprint, TicketPriority, TicketSummary } from "@/domain"; -import { Button, Input, Panel, Spinner, cn } from "@/shared"; +import { Button, Input, Panel, Spinner } from "@/shared"; import { useTickets } from "./useTickets"; import { useProjectAgents } from "./useProjectAgents"; import { SprintManager } from "./SprintManager"; import { SprintPicker } from "./SprintPicker"; import { TicketFacetsBar } from "./TicketFacetsBar"; +import { TicketViewportSelect } from "./TicketViewportSelect"; import { PriorityBadge, StatusBadge, @@ -24,12 +25,6 @@ import { priorityLabel, } from "./ticketMeta"; -const selectClass = cn( - "h-8 rounded-md bg-raised px-2 text-xs text-content", - "border border-border outline-none transition-colors", - "focus:border-primary disabled:cursor-not-allowed disabled:opacity-50", -); - export interface TicketsPanelProps { projectId: string; /** Opens the detail overlay for the given `#ref` (F7). */ @@ -151,18 +146,15 @@ export function TicketsPanel({ projectId, onOpen }: TicketsPanelProps) { onChange={(e) => setNewTitle(e.target.value)} />
- + options={TICKET_PRIORITIES.map((priority) => ({ + value: priority, + label: priorityLabel(priority), + }))} + onChange={(next) => setNewPriority(next as TicketPriority)} + />
@@ -363,21 +354,20 @@ function SprintSection({
- + />
))} diff --git a/frontend/src/features/tickets/tickets.test.tsx b/frontend/src/features/tickets/tickets.test.tsx index cd4190f..ffad360 100644 --- a/frontend/src/features/tickets/tickets.test.tsx +++ b/frontend/src/features/tickets/tickets.test.tsx @@ -353,9 +353,8 @@ describe("TicketsView", () => { expect(listSpy.mock.calls.at(-1)?.[1]?.sort).toBeUndefined(); // Choose a field ⇒ ascending by default, relayed in the query. - fireEvent.change(screen.getByLabelText("sort tickets by"), { - target: { value: "title" }, - }); + fireEvent.click(screen.getByLabelText("sort tickets by")); + fireEvent.click(await screen.findByRole("option", { name: "Titre" })); await waitFor(() => expect(listSpy.mock.calls.at(-1)?.[1]?.sort).toEqual({ field: "title", @@ -375,9 +374,8 @@ describe("TicketsView", () => { ); // Back to « Par défaut » ⇒ `sort` dropped from the query again. - fireEvent.change(screen.getByLabelText("sort tickets by"), { - target: { value: "" }, - }); + fireEvent.click(screen.getByLabelText("sort tickets by")); + fireEvent.click(await screen.findByRole("option", { name: "Par défaut" })); await waitFor(() => expect(listSpy.mock.calls.at(-1)?.[1]?.sort).toBeUndefined(), ); @@ -390,8 +388,8 @@ describe("TicketsView", () => { fireEvent.click(await screen.findByText("Editable")); const dialog = await screen.findByRole("dialog"); - const statusSelect = within(dialog).getByLabelText("ticket status"); - fireEvent.change(statusSelect, { target: { value: "inProgress" } }); + fireEvent.click(within(dialog).getByLabelText("ticket status")); + fireEvent.click(await screen.findByRole("option", { name: "In progress" })); await waitFor(async () => { const fresh = await ticket.read(PROJECT_ID, t.ref); @@ -426,9 +424,8 @@ describe("TicketsView", () => { fireEvent.click(await screen.findByText("Assignable")); const dialog = await screen.findByRole("dialog"); - fireEvent.change(within(dialog).getByLabelText("assign agent"), { - target: { value: known.id }, - }); + fireEvent.click(within(dialog).getByLabelText("assign agent")); + fireEvent.click(await screen.findByRole("option", { name: "Backend" })); fireEvent.click(within(dialog).getByText("Assign")); await waitFor(async () => { @@ -495,9 +492,8 @@ describe("TicketsView", () => { // Change the priority — an immediate-apply mutation that re-fetches the ticket // and bumps its version. The unsaved draft must survive. - fireEvent.change(within(dialog).getByLabelText("ticket priority"), { - target: { value: "high" }, - }); + fireEvent.click(within(dialog).getByLabelText("ticket priority")); + fireEvent.click(await screen.findByRole("option", { name: "High" })); // The backend applied the priority bump… await waitFor(async () => { @@ -643,9 +639,8 @@ describe("TicketsView", () => { expect(within(bucket).getByText("Movable")).toBeTruthy(); // Pick the sprint in the row selector → assign it. - fireEvent.change(screen.getByLabelText(`sprint for ${t.ref}`), { - target: { value: "s1" }, - }); + fireEvent.click(screen.getByLabelText(`sprint for ${t.ref}`)); + fireEvent.click(await screen.findByRole("option", { name: "Sprint One" })); // The gateway recorded the membership… await waitFor(async () => { @@ -777,7 +772,8 @@ describe("TicketsView", () => { ); expect(within(assistant).getByText("Ouvrir la conversation")).toBeTruthy(); - fireEvent.change(profileSelect, { target: { value: "qa-assistant" } }); + fireEvent.click(profileSelect); + fireEvent.click(await screen.findByRole("option", { name: "QA Assistant" })); fireEvent.click(within(assistant).getByText("Ouvrir la conversation")); expect( @@ -837,9 +833,8 @@ describe("TicketsView", () => { expect(within(detail).queryByLabelText("link target ref")).toBeNull(); // Choose the link kind, then open the picker and select the target ticket. - fireEvent.change(within(detail).getByLabelText("link kind"), { - target: { value: "blocks" }, - }); + fireEvent.click(within(detail).getByLabelText("link kind")); + fireEvent.click(await screen.findByRole("option", { name: "blocks" })); fireEvent.click(within(detail).getByLabelText("add link")); // The picker excludes the ticket itself; the target is offered.