fix(ui): unify heading help, table sizing and navigation contracts

Verified with the coordinated workspace changes by devkit full run
2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed).
This shared UI pass does not mark the individual module reviews complete.
This commit is contained in:
2026-09-09 02:03:16 +02:00
parent 6591aaa3fd
commit 6d37aa527f
54 changed files with 1728 additions and 117 deletions
+153
View File
@@ -0,0 +1,153 @@
import { expect, test, type Page } from "@playwright/test";
async function expectDashboardTitleHelp(page: Page) {
const title = page.getByRole("heading", { level: 1, name: new URL(page.url()).searchParams.get("language") === "de" ? "Übersicht" : "Dashboard", exact: true });
await expect(title).toBeVisible();
const anchor = title.locator("..");
const help = anchor.getByRole("link");
await expect(help).toBeVisible();
await expect(title.locator("a")).toHaveCount(0);
const headingBounds = await title.boundingBox();
const helpBounds = await help.boundingBox();
expect(helpBounds!.x - headingBounds!.x - headingBounds!.width).toBeGreaterThanOrEqual(4);
expect(helpBounds!.x - headingBounds!.x - headingBounds!.width).toBeLessThanOrEqual(8);
await expect(page.locator('[data-page-action-slot="help"], .page-action-bar-trailing .documentation-help-link')).toHaveCount(0);
}
async function dashboardFixture(page: Page) {
const writes: string[] = [];
const errors: string[] = [];
page.on("pageerror", error => errors.push(error.message));
await page.route(url => url.pathname.startsWith("/api/"), route => {
if (route.request().method() !== "GET") writes.push(new URL(route.request().url()).pathname);
return route.fulfill({ json: {
exists: false, view_id: null, layout_version: 1, revision: 0,
placements: [], known_widget_ids: [], updated_at: null
} });
});
return { writes, errors };
}
for (const language of ["en", "de"]) {
test(`Dashboard Cancel exits a clean configuration without saving (${language})`, async ({ page }) => {
const fixture = await dashboardFixture(page);
await page.goto(`/?dashboard-configuration&language=${language}`);
await expectDashboardTitleHelp(page);
const configure = page.locator('[data-page-action-archetype="overview"] [data-page-action-slot="primary"] button');
for (let attempt = 0; attempt < 2; attempt += 1) {
await expect(configure).toBeEnabled();
await configure.click();
await expectDashboardTitleHelp(page);
const editor = page.locator('[data-page-action-archetype="editor"]');
await expect(editor.locator('[data-page-action-slot="save"] button')).toBeDisabled();
const cancel = editor.locator('[data-page-action-slot="discard"] button');
await expect(cancel).toBeEnabled();
await expect(cancel).toHaveText(language === "de" ? "Abbrechen" : "Cancel");
await cancel.click();
await expect(configure).toBeVisible();
await expectDashboardTitleHelp(page);
await expect(page.getByRole("alertdialog")).toHaveCount(0);
}
expect(fixture.writes).toEqual([]);
expect(fixture.errors).toEqual([]);
});
}
test("Dashboard Cancel confirms dirty drafts and never saves when discarding", async ({ page }) => {
const fixture = await dashboardFixture(page);
await page.goto("/?dashboard-configuration&language=en");
const configure = page.locator('[data-page-action-archetype="overview"] [data-page-action-slot="primary"] button');
await configure.click();
await page.getByRole("button", { name: "Remove Active interface modules", exact: true }).click();
await expectDashboardTitleHelp(page);
const editor = page.locator('[data-page-action-archetype="editor"]');
await expect(editor.locator('[data-page-action-slot="save"] button')).toBeEnabled();
const cancel = editor.locator('[data-page-action-slot="discard"] button');
await cancel.click();
const confirmation = page.getByRole("alertdialog");
await expect(confirmation).toBeVisible();
await confirmation.getByRole("button", { name: "Cancel", exact: true }).click();
await expect(editor).toBeVisible();
await expect(editor.locator('[data-page-action-slot="save"] button')).toBeEnabled();
await cancel.click();
await confirmation.getByRole("button", { name: "Discard", exact: true }).click();
await expect(configure).toBeVisible();
await expect(page.getByRole("heading", { name: "Active interface modules", exact: true })).toBeVisible();
await configure.click();
await expect(editor.locator('[data-page-action-slot="save"] button')).toBeDisabled();
await expect(cancel).toBeEnabled();
expect(fixture.writes).toEqual([]);
expect(fixture.errors).toEqual([]);
});
test("Dashboard Cancel can save and leave without restoring the previous draft", async ({ page }) => {
const fixture = await dashboardFixture(page);
let releaseSave!: () => void;
const savePending = new Promise<void>(resolve => { releaseSave = resolve; });
let saved: Record<string, unknown> | null = null;
const updates: Record<string, unknown>[] = [];
await page.route("**/api/v1/dashboard/layout*", async route => {
if (route.request().method() === "PUT") {
const payload = route.request().postDataJSON();
updates.push(payload);
await savePending;
saved = { ...payload, exists: true, view_id: null, revision: 1, updated_at: "2026-09-08T12:00:00Z" };
await route.fulfill({ json: saved });
} else if (saved) {
await route.fulfill({ json: saved });
} else {
await route.fallback();
}
});
await page.goto("/?dashboard-configuration&language=en");
const configure = page.locator('[data-page-action-archetype="overview"] [data-page-action-slot="primary"] button');
const editor = page.locator('[data-page-action-archetype="editor"]');
await configure.click();
await page.getByRole("button", { name: "Remove Active interface modules", exact: true }).click();
await editor.locator('[data-page-action-slot="discard"] button').click();
const confirmation = page.getByRole("alertdialog");
await confirmation.getByRole("button", { name: "Save and leave", exact: true }).click();
await expect.poll(() => updates.length).toBe(1);
await expect(editor.locator('[data-page-action-slot="discard"] button')).toBeDisabled();
await expect(editor.locator('[data-page-action-slot="save"] button')).toBeDisabled();
await expect(confirmation.getByRole("button", { name: "Discard", exact: true })).toBeDisabled();
releaseSave();
await expect(configure).toBeVisible();
await expect(confirmation).toHaveCount(0);
expect(updates[0].placements).toEqual([]);
await page.reload();
await configure.click();
await expect(page.getByRole("button", { name: "Remove Active interface modules", exact: true })).toHaveCount(0);
await expect(editor.locator('[data-page-action-slot="save"] button')).toBeDisabled();
await expect(editor.locator('[data-page-action-slot="discard"] button')).toBeEnabled();
expect(updates).toHaveLength(1);
expect(fixture.writes).toEqual([]);
expect(fixture.errors).toEqual([]);
});
test("actual Scheduling widget contributes help at its Dashboard title without duplicating it", async ({ page }) => {
const fixture = await dashboardFixture(page);
await page.route("**/api/v1/dashboard/layout*", route => route.fulfill({ json: {
exists: true, view_id: null, layout_version: 1, revision: 1,
placements: [{ instance_id: "scheduling-widget", widget_id: "scheduling.open-requests", size: "medium", column_start: 1, configuration: {} }],
known_widget_ids: ["dashboard.installed-modules", "scheduling.open-requests"], updated_at: "2026-09-08T12:00:00Z"
} }));
await page.route("**/api/v1/scheduling/requests*", route => route.fulfill({ json: { requests: [] } }));
await page.goto("/?dashboard-configuration&widget-help&language=en");
await expectDashboardTitleHelp(page);
const widget = page.locator(".dashboard-widget").filter({ has: page.getByRole("heading", { level: 2, name: "Scheduling requests", exact: true }) });
await expect(widget).toHaveCount(1);
await expect(widget.getByRole("heading", { name: "Scheduling requests", exact: true })).toHaveCount(1);
const titleHelp = widget.locator(".card-title-with-help .documentation-help-link");
await expect(titleHelp).toHaveAttribute("href", /topic=scheduling\.find-and-decide-meeting-time/);
await expect(titleHelp).toBeVisible();
await expect(widget.locator(".card-body .documentation-help-link")).toHaveCount(0);
await widget.getByRole("button", { name: "Show header only", exact: true }).click();
await expect(titleHelp).toBeVisible();
await page.locator('[data-page-action-archetype="overview"] [data-page-action-slot="primary"] button').click();
await expectDashboardTitleHelp(page);
await expect(titleHelp).toBeVisible();
await expect(widget.locator(".card-actions .documentation-help-link")).toHaveCount(0);
expect(fixture.writes).toEqual([]);
expect(fixture.errors).toEqual([]);
});