fix(tickets): dropdowns ne sortent plus du viewport en bas d'écran (#3)
Remplace les <select> natifs de la vue Tickets (statut, priorité, tri, lien, agent assigné/assistant, sprint) par TicketViewportSelect, qui repositionne intelligemment son overlay selon l'espace disponible dans la fenêtre au lieu de toujours ouvrir vers le bas. QA vert : 80/80 (frontend/src/features/tickets), tsc --noEmit propre. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -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)}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<select
|
||||
<TicketViewportSelect
|
||||
aria-label="new ticket priority"
|
||||
className={selectClass}
|
||||
value={newPriority}
|
||||
onChange={(e) => setNewPriority(e.target.value as TicketPriority)}
|
||||
>
|
||||
{TICKET_PRIORITIES.map((p) => (
|
||||
<option key={p} value={p}>
|
||||
{priorityLabel(p)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
options={TICKET_PRIORITIES.map((priority) => ({
|
||||
value: priority,
|
||||
label: priorityLabel(priority),
|
||||
}))}
|
||||
onChange={(next) => setNewPriority(next as TicketPriority)}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
size="sm"
|
||||
@ -211,24 +203,23 @@ export function TicketsPanel({ projectId, onOpen }: TicketsPanelProps) {
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<select
|
||||
<TicketViewportSelect
|
||||
aria-label="filter by assignee"
|
||||
className={selectClass}
|
||||
value={vm.query.assignedAgentId ?? ""}
|
||||
onChange={(e) =>
|
||||
options={[
|
||||
{ value: "", label: "All assignees" },
|
||||
...agents.map((agent) => ({
|
||||
value: agent.id,
|
||||
label: agent.name,
|
||||
})),
|
||||
]}
|
||||
onChange={(next) =>
|
||||
vm.setQuery({
|
||||
...vm.query,
|
||||
assignedAgentId: e.target.value || undefined,
|
||||
assignedAgentId: next || undefined,
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="">All assignees</option>
|
||||
{agents.map((a) => (
|
||||
<option key={a.id} value={a.id}>
|
||||
{a.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -363,21 +354,20 @@ function SprintSection({
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 pl-6">
|
||||
<select
|
||||
<TicketViewportSelect
|
||||
aria-label={`sprint for ${t.ref}`}
|
||||
className={selectClass}
|
||||
value={t.sprintId ?? ""}
|
||||
onChange={(e) =>
|
||||
onAssignSprint(t.ref, e.target.value || null)
|
||||
options={[
|
||||
{ value: "", label: "— No sprint —" },
|
||||
...sprints.map((sprint) => ({
|
||||
value: sprint.id,
|
||||
label: sprint.name,
|
||||
})),
|
||||
]}
|
||||
onChange={(next) =>
|
||||
onAssignSprint(t.ref, next || null)
|
||||
}
|
||||
>
|
||||
<option value="">— No sprint —</option>
|
||||
{sprints.map((s) => (
|
||||
<option key={s.id} value={s.id}>
|
||||
{s.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user