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:
Executable
+102
@@ -0,0 +1,102 @@
|
||||
import { expect, test, type Locator } from "@playwright/test";
|
||||
|
||||
async function expectHelpBesideHeading(anchor: Locator) {
|
||||
const heading = anchor.locator(":scope > :is(h1,h2,h3)");
|
||||
const link = anchor.getByRole("link");
|
||||
await expect(heading).toBeVisible();
|
||||
await expect(link).toBeVisible();
|
||||
await expect(heading.locator("a,button,[role=status]")).toHaveCount(0);
|
||||
const geometry = await anchor.evaluate(element => {
|
||||
const heading = element.querySelector(":scope > :is(h1,h2,h3)")!.getBoundingClientRect();
|
||||
const link = element.querySelector("a")!.getBoundingClientRect();
|
||||
return { gap: link.left - heading.right, right: link.right, viewport: window.innerWidth };
|
||||
});
|
||||
expect(geometry.gap).toBeGreaterThanOrEqual(4);
|
||||
expect(geometry.gap).toBeLessThanOrEqual(8);
|
||||
expect(geometry.right).toBeLessThanOrEqual(geometry.viewport);
|
||||
}
|
||||
|
||||
for (const width of [390, 3085]) {
|
||||
for (const language of ["en", "de"]) {
|
||||
test(`documentation remains beside headings during loading and long-title wrapping (${width}px, ${language})`, async ({ page }) => {
|
||||
await page.setViewportSize({ width, height: 1100 });
|
||||
await page.goto(`/?heading-help&language=${language}`);
|
||||
const pageTitle = page.locator(".page-title-with-loader");
|
||||
await expect(page.getByRole("heading", { level: 1, name: "Dashboard", exact: true })).toBeVisible();
|
||||
await expect(pageTitle.getByRole("status")).toBeVisible();
|
||||
await expect(pageTitle.getByRole("link")).toHaveAccessibleName(language === "de" ? "Benutzerdokumentation öffnen" : "Open user documentation");
|
||||
for (const anchor of await page.locator(".text-with-help:has(> :is(h1,h2,h3))").all()) await expectHelpBesideHeading(anchor);
|
||||
const rawLabel = page.getByTestId("raw-text-help");
|
||||
const rawGeometry = await rawLabel.evaluate(element => ({ width: element.clientWidth, scrollWidth: element.scrollWidth, right: element.getBoundingClientRect().right, helpRight: element.querySelector("a")!.getBoundingClientRect().right }));
|
||||
expect(rawGeometry.scrollWidth).toBeLessThanOrEqual(rawGeometry.width);
|
||||
expect(rawGeometry.helpRight).toBeLessThanOrEqual(rawGeometry.right);
|
||||
await page.getByRole("button", { name: "Toggle loading", exact: true }).click();
|
||||
await expect(pageTitle.getByRole("status")).toHaveCount(0);
|
||||
await expectHelpBesideHeading(pageTitle.locator(".text-with-help"));
|
||||
await expect(page.locator('[data-page-action-slot="help"]')).toHaveCount(0);
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBeLessThanOrEqual(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
test("card help stays available when collapsed and opening it does not toggle content", async ({ page, context }) => {
|
||||
await context.route("https://govoplan.add-ideas.de/**", route => route.fulfill({ body: "Documentation fixture" }));
|
||||
await page.goto("/?heading-help&language=en");
|
||||
const card = page.getByTestId("help-card");
|
||||
const popupPromise = page.waitForEvent("popup");
|
||||
await card.getByRole("link").click();
|
||||
const popup = await popupPromise;
|
||||
await popup.close();
|
||||
await expect(page.getByTestId("help-card-content")).toBeVisible();
|
||||
await card.getByRole("button", { name: "Show header only", exact: true }).click();
|
||||
await expect(page.getByTestId("help-card-content")).toHaveCount(0);
|
||||
await expectHelpBesideHeading(card.locator(".text-with-help"));
|
||||
await card.getByRole("button", { name: "Show content", exact: true }).click();
|
||||
await expect(page.getByTestId("help-card-content")).toBeVisible();
|
||||
});
|
||||
|
||||
test("dialog help has its own name, remains in keyboard order, and does not dismiss the dialog", async ({ page, context }) => {
|
||||
await context.route("https://govoplan.add-ideas.de/**", route => route.fulfill({ body: "Documentation fixture" }));
|
||||
await page.setViewportSize({ width: 390, height: 1000 });
|
||||
await page.goto("/?heading-help&language=en");
|
||||
await page.getByTestId("open-help-dialog").click();
|
||||
const dialog = page.getByRole("dialog");
|
||||
await expect(dialog).toHaveAccessibleName(/^Dashboard DokumentationsüberschriftOhneLeerzeichen/);
|
||||
await expectHelpBesideHeading(dialog.locator(".text-with-help"));
|
||||
const help = dialog.getByRole("link");
|
||||
await expect(help).toBeFocused();
|
||||
await help.press("Shift+Tab");
|
||||
await expect(page.getByTestId("dialog-last-action")).toBeFocused();
|
||||
await page.getByTestId("dialog-last-action").press("Tab");
|
||||
await expect(help).toBeFocused();
|
||||
const popupPromise = page.waitForEvent("popup");
|
||||
await help.click();
|
||||
const popup = await popupPromise;
|
||||
await popup.close();
|
||||
await expect(dialog).toBeVisible();
|
||||
await dialog.getByRole("button", { name: "Close", exact: true }).click();
|
||||
await expect(dialog).toHaveCount(0);
|
||||
await expect(page.getByTestId("open-help-dialog")).toBeFocused();
|
||||
});
|
||||
|
||||
test("widget documentation stays beside its one existing title in display, configuration, and drag markup", async ({ page }) => {
|
||||
await page.goto("/?heading-help&widgets&language=en");
|
||||
const title = page.getByRole("heading", { name: "Scheduling requests", exact: true });
|
||||
const anchor = page.locator(".card-title-with-help");
|
||||
await expect(title).toHaveCount(1);
|
||||
await expectHelpBesideHeading(anchor);
|
||||
await expect(anchor.getByRole("link")).toHaveAttribute("href", "/docs?type=user&topic=scheduling.find-and-decide-meeting-time");
|
||||
await page.getByRole("button", { name: "Toggle configuration", exact: true }).click();
|
||||
await expect(title).toHaveCount(1);
|
||||
await expectHelpBesideHeading(anchor);
|
||||
await expect(page.locator(".card-actions .documentation-help-link")).toHaveCount(0);
|
||||
await page.getByRole("button", { name: "Toggle drag preview", exact: true }).click();
|
||||
// A drag placeholder retains hidden content only to preserve the widget height.
|
||||
const preview = page.locator(".dashboard-widget-placeholder-content");
|
||||
await expect(preview.locator("h2")).toHaveCount(1);
|
||||
await expect(preview.locator(".card-title-with-help .documentation-help-link")).toHaveAttribute("href", "/docs?type=user&topic=scheduling.find-and-decide-meeting-time");
|
||||
await expect(preview).toBeHidden();
|
||||
await page.getByRole("button", { name: "Toggle drag preview", exact: true }).click();
|
||||
await expect(title).toHaveCount(1);
|
||||
await expectHelpBesideHeading(anchor);
|
||||
});
|
||||
Reference in New Issue
Block a user