feat(webui): govern actions, quick access, and metrics

Refs #264, #285, #289
This commit is contained in:
2026-08-19 18:47:46 +02:00
parent 41db78c201
commit ffaab543d2
31 changed files with 753 additions and 125 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 227 KiB

@@ -62,6 +62,11 @@ test("shared components remain accessible and keyboard operable", async ({ page
await expect(editorActions.getByRole("button", { name: "Verwerfen" })).toBeDisabled();
await expect(editorActions.getByRole("button", { name: "Änderungen speichern" })).toBeDisabled();
const metricDrilldown = page.getByRole("button", { name: "Offene Aufgaben prüfen" });
await metricDrilldown.focus();
await page.keyboard.press("Enter");
await expect(page.getByTestId("metric-drilldown-result")).toHaveText("18 offene Aufgaben");
const opener = page.getByTestId("open-dialog");
await opener.focus();
await page.keyboard.press("Enter");
@@ -82,6 +87,64 @@ test("full-page tool launch preserves a bounded return context", async ({ page }
await expect(page).toHaveURL(/\/?\?theme=light$/);
});
test("Quick Access preserves focus and adapts to a narrow viewport", async ({ page }) => {
await page.route("**/api/v1/quick-access/effective*", async (route) => {
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({
categories: [{
id: "files",
label: "Files",
description: "Files beside the current task",
icon: "files",
order: 10,
enabled: true,
forced: false,
locked_by: null,
tools: [{
contract_version: "1",
id: "files.recent",
module_id: "files",
category_id: "files",
label: "Recent files",
description: "Select an authorized file without leaving this case.",
icon: "files",
surface_id: "files.route.files",
full_page_path: "/files",
required_all: [],
required_any: [],
order: 10,
default_enabled: true,
modes: ["select"],
availability: "global",
accepted_reference_kinds: [],
returned_reference_kinds: ["files.file-version"],
help_context_id: "files.quick_access.files",
enabled: true,
forced: false,
locked_by: null
}]
}],
diagnostics: []
})
});
});
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/?theme=light&quick-access=1");
const filesTrigger = page.getByRole("button", { name: "Files" });
await expect(filesTrigger).toBeVisible();
await filesTrigger.click();
const drawer = page.getByRole("dialog", { name: "Files" });
await expect(drawer).toBeVisible();
await expect(drawer.getByRole("button", { name: "Close" })).toBeFocused();
const drawerBounds = await drawer.boundingBox();
expect(drawerBounds?.x).toBeGreaterThanOrEqual(0);
expect((drawerBounds?.x ?? 0) + (drawerBounds?.width ?? 0)).toBeLessThanOrEqual(390);
await page.keyboard.press("Escape");
await expect(drawer).toBeHidden();
await expect(filesTrigger).toBeFocused();
});
test("light and dark desktop geometry remains stable", async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 1000 });
await page.goto("/?theme=light");