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]>
This commit is contained in:
1 parent
759d85dc67
commit
fce9d10192
26 files changed
+1137
-606
No files matched your search
@@ -144,6 +144,15 @@ const RULES: GuardRule[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
// Task A8: one tabs kit for the whole app (spec 10.4, 10.7). Base UI draws role tablist, tab
|
||||
// and tabpanel inside ui/tabs.tsx, so no source file writes them by hand and the list of
|
||||
// exceptions stays empty.
|
||||
{
|
||||
id: "no role tablist, tab or tabpanel outside ui/tabs.tsx",
|
||||
pattern: ownRole("role", "tab(?:list|panel)?"),
|
||||
scope: "src/**/*.{ts,tsx}",
|
||||
allow: [],
|
||||
},
|
||||
];
|
||||
|
||||
const SCOPE_FILE: Record<GuardScope, RegExp> = {
|
||||
@@ -206,6 +215,20 @@ describe("baukasten guard (spec 10.7)", () => {
|
||||
expect(users.map(({ file }) => file)).toEqual([]);
|
||||
});
|
||||
|
||||
it("TabSwitcher is gone (the settings pages use Tabs directly, spec 10.4)", () => {
|
||||
expect(existsSync(join(SRC, "components", "tab-switcher.tsx"))).toBe(false);
|
||||
const users = FILES.filter(({ text }) => /\bTabSwitcher\b|components\/tab-switcher/.test(text));
|
||||
expect(users.map(({ file }) => file)).toEqual([]);
|
||||
});
|
||||
|
||||
it("the tabs kit has one variant, segment (the line variant is gone)", () => {
|
||||
const tabs = FILES.find(({ file }) => file === "src/components/ui/tabs.tsx")?.text ?? "";
|
||||
expect(tabs).toMatch(/segment/);
|
||||
expect(tabs).not.toMatch(/variant=line|\bline:/);
|
||||
const lineUsers = FILES.filter(({ text }) => /<TabsList\b[^>]*variant=["'{]/.test(text));
|
||||
expect(lineUsers.map(({ file }) => file)).toEqual([]);
|
||||
});
|
||||
|
||||
it("a list of CheckboxFields keeps neighbouring targets at least 4 px apart (spec 4.5)", () => {
|
||||
// `.map(… => <CheckboxField` (optionally inside an <li>) and the container opened before it.
|
||||
const lists = FILES.filter(({ file }) => file.endsWith(".tsx")).flatMap(({ file, text }) =>
|
||||
@@ -287,6 +310,16 @@ describe("baukasten guard (spec 10.7)", () => {
|
||||
// The CheckboxField exemption ends with its own tag: a later element is caught again.
|
||||
expect(toggle.test('<CheckboxField label="A" />\n<input role="switch" />')).toBe(true);
|
||||
expect(checkbox.test('<CheckboxField label="A" /><span role="checkbox" />')).toBe(true);
|
||||
const tabs = rule("no role tablist, tab or tabpanel outside ui/tabs.tsx").pattern;
|
||||
expect(tabs.test('<div role="tablist" className="flex">')).toBe(true);
|
||||
expect(tabs.test('<button type="button" role="tab" aria-selected={on}>')).toBe(true);
|
||||
expect(tabs.test("<section role={'tabpanel'}>")).toBe(true);
|
||||
expect(tabs.test('{ role: "tab" }')).toBe(true);
|
||||
expect(tabs.test('<div role={wide ? "tablist" : undefined}>')).toBe(true);
|
||||
expect(tabs.test('node.setAttribute("role", "tab")')).toBe(true);
|
||||
expect(tabs.test('<Table role="table">')).toBe(false);
|
||||
expect(tabs.test(`querySelector('[role="tab"]')`)).toBe(false);
|
||||
expect(tabs.test('getAllByRole("tab")')).toBe(false);
|
||||
expect(rule("no text-white").pattern.test('"text-white/80"')).toBe(true);
|
||||
expect(rule("no gradient prop or gradient token").pattern.test("<PageHeader gradient=\"honey\" />")).toBe(true);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user