diff --git a/components/ui/SlideOver.tsx b/components/ui/SlideOver.tsx index 8042a09..ea33f26 100644 --- a/components/ui/SlideOver.tsx +++ b/components/ui/SlideOver.tsx @@ -36,8 +36,27 @@ export function SlideOver({ open, onClose, title, subtitle, children, footer }: aria-modal="true" aria-labelledby={titleId} tabIndex={-1} - className={`absolute right-0 top-0 flex h-dvh w-full max-w-md flex-col bg-white shadow-[var(--shadow-overlay)] outline-none transition-transform duration-200 ${ - open ? "translate-x-0" : "translate-x-full" + // ── Warum der Schatten am geschlossenen Panel wegfällt ────────── + // + // Geschlossen steht das Panel per `translate-x-full` genau hinter dem + // rechten Bildschirmrand: die eigene linke Kante liegt auf dem Rand. + // Der Schatten ist das Einzige, was von dort noch ins Bild reicht — + // 32 px Weichzeichnung nach aussen, und „aussen" heisst hier: nach + // links, in die Seite hinein. + // + // Bei einem Panel fällt das nicht auf. Die Protokollseite rendert + // jedoch eines **je Zeile**, also fünfzig übereinander an derselben + // Stelle. Bei 18 % Deckkraft bleiben nach fünfzig Lagen rechnerisch + // 0,01 % Durchsicht — ein fast schwarzer Streifen neben der + // Bildlaufleiste, mit sichtbaren Stufen dort, wo sich die + // Weichzeichnungen der einzelnen Lagen überlagern. Genau das hat der + // Kunde gemeldet. + // + // Die Tiefe gehört zum geöffneten Panel, nicht zum weggeschobenen. + // Sie erst beim Öffnen zu setzen kostet nichts und nimmt dem Stapel + // seine Wirkung. + className={`absolute right-0 top-0 flex h-dvh w-full max-w-md flex-col bg-white outline-none transition-transform duration-200 ${ + open ? "translate-x-0 shadow-[var(--shadow-overlay)]" : "translate-x-full" }`} >
diff --git a/tests/components/SlideOver.test.tsx b/tests/components/SlideOver.test.tsx new file mode 100644 index 0000000..58c1ea9 --- /dev/null +++ b/tests/components/SlideOver.test.tsx @@ -0,0 +1,68 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { SlideOver } from "@/components/ui/SlideOver"; + +// Das Panel bleibt geschlossen im DOM, damit die Schiebebewegung laufen kann. +// Es steht dann per translate-x-full hinter dem rechten Bildschirmrand — die +// eigene linke Kante genau auf dem Rand. Ein Schatten reicht von dort noch +// ins Bild zurück, und die Protokollseite rendert ein Panel je Zeile: fünfzig +// Schatten an derselben Stelle ergaben einen fast schwarzen Streifen neben +// der Bildlaufleiste. + +function panel(container: HTMLElement): HTMLElement { + const el = container.querySelector('[role="dialog"]'); + expect(el, "Das Panel fehlt").not.toBeNull(); + return el as HTMLElement; +} + +describe("SlideOver", () => { + it("bleibt geschlossen im DOM", () => { + // Ohne das gäbe es keine Schiebebewegung beim Schliessen — und dieser + // Test hier hätte keinen Gegenstand. + const { container } = render( + {}} title="Titel"> + Inhalt + + ); + expect(panel(container).className).toContain("translate-x-full"); + }); + + it("trägt geschlossen keinen Schatten", () => { + const { container } = render( + {}} title="Titel"> + Inhalt + + ); + expect(panel(container).className).not.toContain("shadow-"); + }); + + it("trägt geöffnet einen Schatten", () => { + const { container } = render( + {}} title="Titel"> + Inhalt + + ); + expect(panel(container).className).toContain("shadow-[var(--shadow-overlay)]"); + expect(panel(container).className).toContain("translate-x-0"); + }); + + it("nimmt das geschlossene Panel aus der Bedienung", () => { + // `inert` und nicht aria-hidden: nur das entfernt die Felder auch aus der + // Tabulatorreihenfolge und blockiert Klicks. + const { container } = render( + {}} title="Titel"> + Inhalt + + ); + expect(panel(container).closest("[inert]")).not.toBeNull(); + }); + + it("benennt den Dialog nach seiner Überschrift", () => { + render( + {}} title="Anton Aigner"> + Inhalt + + ); + expect(screen.getByRole("dialog", { name: "Anton Aigner" })).toBeInTheDocument(); + }); +});