Define semantic page archetypes
This commit is contained in:
@@ -33,29 +33,29 @@ import type { AuthInfo } from "../src/types";
|
||||
|
||||
export default function ConformanceApp() {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [editorDirty, setEditorDirty] = useState(true);
|
||||
|
||||
return (
|
||||
<main className="conformance-root" data-conformance-id="shared-ui-lab">
|
||||
<PageLayout
|
||||
archetype="overview"
|
||||
mode="embedded"
|
||||
title="Zentrale GovOPlaN-Oberflächen"
|
||||
description="Diese Prüfseite rendert reale gemeinsame Komponenten mit langen deutschen Beschriftungen, Zuständen und responsiven Zusammensetzungen."
|
||||
actions={<Button variant="primary" data-testid="open-dialog" onClick={() => setDialogOpen(true)}>Prüfdialog öffnen</Button>}
|
||||
actions={<PageActionBar variant="overview" primaryActions={<Button variant="primary" data-testid="open-dialog" onClick={() => setDialogOpen(true)}>Prüfdialog öffnen</Button>} />}
|
||||
>
|
||||
<section className="conformance-section" aria-labelledby="actions-heading">
|
||||
<h2 id="actions-heading">Aktionen, Filter und Status</h2>
|
||||
<PageActionBar
|
||||
variant="editor"
|
||||
refreshable
|
||||
dirty={editorDirty}
|
||||
label="Bearbeitungsaktionen"
|
||||
reloadAction={<Button variant="ghost">Neu laden</Button>}
|
||||
contextActions={(
|
||||
<>
|
||||
<Button>Vorschau öffnen</Button>
|
||||
<Button variant="danger" disabledReason="Nur die federführende Stelle darf diesen Vorgang endgültig löschen.">Löschen</Button>
|
||||
</>
|
||||
)}
|
||||
discardAction={<Button variant="ghost">Verwerfen</Button>}
|
||||
saveAction={<Button variant="primary">Änderungen speichern</Button>}
|
||||
contextActions={<Button>Vorschau öffnen</Button>}
|
||||
destructiveActions={<Button variant="danger" disabledReason="Nur die federführende Stelle darf diesen Vorgang endgültig löschen.">Löschen</Button>}
|
||||
discardAction={{ label: "Verwerfen", onClick: () => setEditorDirty(false) }}
|
||||
saveAction={{ label: "Änderungen speichern", onClick: () => setEditorDirty(false) }}
|
||||
/>
|
||||
<FilterBar surface="control" aria-label="Vorgangsliste filtern">
|
||||
<label>Suche<input type="search" placeholder="Aktenzeichen oder verantwortliche Stelle" /></label>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 232 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 230 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 214 KiB |
@@ -25,6 +25,20 @@ test("shared components remain accessible and keyboard operable", async ({ page
|
||||
await expectNoAccessibilityViolations(page);
|
||||
|
||||
const editorActions = page.getByRole("toolbar", { name: "Bearbeitungsaktionen" });
|
||||
await expect(page.locator("[data-page-archetype='overview']")).toBeVisible();
|
||||
await expect(editorActions).toHaveAttribute("data-page-refreshable", "true");
|
||||
await expect(editorActions).toHaveAttribute("data-page-dirty", "true");
|
||||
await expect(editorActions.getByRole("status")).toHaveText("Unsaved changes");
|
||||
await expect(editorActions.locator("[data-page-action-separation='destructive']")).toHaveCount(1);
|
||||
await expect(editorActions.locator("[data-page-action-separation='destructive']")).toHaveCSS("border-left-width", "2px");
|
||||
const actionSlots = await editorActions.locator("[data-page-action-slot]").evaluateAll((elements) => elements.map((element) => element.getAttribute("data-page-action-slot")));
|
||||
expect(actionSlots).toEqual([
|
||||
"reload",
|
||||
"context",
|
||||
"destructive",
|
||||
"discard",
|
||||
"save"
|
||||
]);
|
||||
await expect(editorActions.getByRole("button")).toHaveText([
|
||||
"Neu laden",
|
||||
"Vorschau öffnen",
|
||||
@@ -42,6 +56,11 @@ test("shared components remain accessible and keyboard operable", async ({ page
|
||||
await expect(editorActions.getByRole("button", { name: "Verwerfen" })).toBeFocused();
|
||||
await page.keyboard.press("Tab");
|
||||
await expect(editorActions.getByRole("button", { name: "Änderungen speichern" })).toBeFocused();
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(editorActions).toHaveAttribute("data-page-dirty", "false");
|
||||
await expect(editorActions.getByRole("status")).toHaveText("Saved");
|
||||
await expect(editorActions.getByRole("button", { name: "Verwerfen" })).toBeDisabled();
|
||||
await expect(editorActions.getByRole("button", { name: "Änderungen speichern" })).toBeDisabled();
|
||||
|
||||
const opener = page.getByTestId("open-dialog");
|
||||
await opener.focus();
|
||||
@@ -92,5 +111,8 @@ test("narrow layout preserves task order without horizontal overflow", async ({
|
||||
expect(overflowing).toEqual([]);
|
||||
await expect(page.locator("[data-page-action-archetype='editor'] [data-page-action-group='trailing']"))
|
||||
.toHaveCSS("justify-content", "flex-end");
|
||||
const saveRightEdge = await page.locator("[data-page-action-archetype='editor'] [data-page-action-slot='save']").evaluate((element) => element.getBoundingClientRect().right);
|
||||
const actionBarRightEdge = await page.locator("[data-page-action-archetype='editor']").evaluate((element) => element.getBoundingClientRect().right);
|
||||
expect(Math.abs(actionBarRightEdge - saveRightEdge)).toBeLessThanOrEqual(1);
|
||||
await expect(page.locator("[data-conformance-id='shared-ui-lab']")).toHaveScreenshot("shared-ui-light-narrow.png", { animations: "disabled", maxDiffPixelRatio: 0.005 });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user