Files
ApiCore/ApiCore-Frontend/src/components/ui/menu-items.spec.tsx
T
Nick.RosenloecherandClaude Opus 5.5 fce9d10192
Backend E2E / e2e (push) Failing after 13m2s
Backend Image / build-and-push (push) Successful in 7m55s
Frontend Image / build-and-push (push) Successful in 27m46s
feat(t5): ein Tabs-Baustein für Listen, Einstellungen und Statistik (T5a A8)
- ui/tabs.tsx nur noch Variante segment: Leiste auf --muted, aktiver Tab
  mit Kartenfläche, fetter Schrift und 3-px-Balken in --primary-ink;
  44 px bei Touch, kompakt mit Maus; inaktive Schrift ≥ 6,27:1.
- ActiveHistoryTabs als Vorlage auf Tabs für 10 Listen (Pfeiltasten
  schalten direkt, Daten sind geladen), mit toolbar und heading.
- Einstellungen (Automatisierung, Benutzer & Rollen) und Statistik mit
  manueller Aktivierung: Pfeil bewegt den Fokus, Enter/Leertaste wählt;
  ?tab= über router.replace. TabSwitcher entfernt.
- Menüeinträge (Dropdown, Select) mit innerem Fokusring ≥ 3:1.
- Aufgaben: der Prioritätsfilter lädt die Liste jetzt neu (queryKey).
- Wächter: keine eigenen tablist/tab/tabpanel außerhalb ui/tabs.tsx.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-16 20:08:36 +02:00

68 lines
2.6 KiB
TypeScript

import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "./dropdown-menu";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select";
/**
* Menu and select items keep `outline-hidden` (the item itself is the moving highlight), so the
* accent fill alone was the focus cue, and it stays below 3:1 on the popover. Each item now
* draws a 2 px inset ring in --ring while focused (≥ 3:1 in all four sets, contrast.spec.ts).
*/
const FOCUS_CUE = ["focus:inset-ring-2", "focus:inset-ring-ring"];
function expectFocusCue(element: HTMLElement) {
const classes = element.className.split(/\s+/);
for (const token of FOCUS_CUE) expect(classes, token).toContain(token);
}
describe("menu items show a focus cue of at least 3:1 (task A8)", () => {
it("every dropdown item kind draws the inset ring while focused", () => {
render(
<DropdownMenu open>
<DropdownMenuTrigger>Mehr</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Exportieren</DropdownMenuItem>
<DropdownMenuItem variant="destructive">Löschen</DropdownMenuItem>
<DropdownMenuCheckboxItem checked>Mit Details</DropdownMenuCheckboxItem>
<DropdownMenuRadioGroup value="a">
<DropdownMenuRadioItem value="a">Liste</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
<DropdownMenuSub>
<DropdownMenuSubTrigger>Weitere</DropdownMenuSubTrigger>
</DropdownMenuSub>
</DropdownMenuContent>
</DropdownMenu>,
);
expectFocusCue(screen.getByRole("menuitem", { name: "Exportieren" }));
expectFocusCue(screen.getByRole("menuitem", { name: "Löschen" }));
expectFocusCue(screen.getByRole("menuitemcheckbox", { name: "Mit Details" }));
expectFocusCue(screen.getByRole("menuitemradio", { name: "Liste" }));
expectFocusCue(screen.getByRole("menuitem", { name: "Weitere" }));
});
it("a select option draws the inset ring while focused", () => {
render(
<Select open value="a">
<SelectTrigger aria-label="Status">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="a">Aktiv</SelectItem>
<SelectItem value="b">Aufgelöst</SelectItem>
</SelectContent>
</Select>,
);
for (const option of screen.getAllByRole("option")) expectFocusCue(option);
});
});