feat: consolidate shared UI and harden browser authority for release
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
|
||||
const actionCell = (page: Page) => page.locator('.data-grid-body-cell[data-column-id="actions"]');
|
||||
const header = (page: Page, id: string) => page.locator(`.data-grid-header-cell[data-column-id="${id}"]`);
|
||||
const width = (page: Page, id: string) => header(page, id).evaluate((element) => element.getBoundingClientRect().width);
|
||||
|
||||
async function expectActionsUnclipped(page: Page) {
|
||||
await expect.poll(async () => actionCell(page).evaluate((cell) => {
|
||||
const bounds = cell.getBoundingClientRect();
|
||||
const scroller = cell.closest(".data-grid-scroll-region")!;
|
||||
const viewport = scroller.getBoundingClientRect();
|
||||
return Array.from(cell.querySelectorAll("button")).every((button) => {
|
||||
const rect = button.getBoundingClientRect();
|
||||
return rect.width >= 35 && rect.left >= bounds.left && rect.right <= bounds.right
|
||||
&& rect.top >= bounds.top && rect.bottom <= bounds.bottom
|
||||
&& rect.left >= viewport.left && rect.right <= viewport.right;
|
||||
});
|
||||
})).toBe(true);
|
||||
}
|
||||
|
||||
test("undersized action tracks fit real controls and stay visible through narrow horizontal scrolling", async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
page.on("pageerror", (error) => errors.push(error.message));
|
||||
await page.goto("/?data-grid-layout");
|
||||
await expect.poll(() => width(page, "actions")).toBeGreaterThanOrEqual(181);
|
||||
await expectActionsUnclipped(page);
|
||||
await page.getByRole("button", { name: "Narrow grid", exact: true }).click();
|
||||
await expect.poll(() => width(page, "actions")).toBeLessThanOrEqual(160);
|
||||
const scroller = page.locator(".data-grid-scroll-region");
|
||||
await expectActionsUnclipped(page);
|
||||
await scroller.evaluate((element) => { element.scrollLeft = element.scrollWidth / 2; });
|
||||
await expectActionsUnclipped(page);
|
||||
await scroller.evaluate((element) => { element.scrollLeft = element.scrollWidth; });
|
||||
await expectActionsUnclipped(page);
|
||||
await page.getByRole("button", { name: "Inspect Alpha", exact: true }).click();
|
||||
await expect(page.getByTestId("clicked-action")).toHaveText("Inspect Alpha");
|
||||
await scroller.focus();
|
||||
await expect(scroller).toBeFocused();
|
||||
// The application shell's supported viewport floor is 320px; its padded
|
||||
// content area is narrower and must still contain the complete action set.
|
||||
await page.setViewportSize({ width: 320, height: 720 });
|
||||
await expectActionsUnclipped(page);
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
test("pointer and keyboard resizing persists, cancels safely, and adapts to container resize", async ({ page }) => {
|
||||
await page.goto("/?data-grid-layout");
|
||||
await expectActionsUnclipped(page);
|
||||
const handle = header(page, "name").getByRole("separator");
|
||||
await expect(handle).toHaveAttribute("aria-orientation", "vertical");
|
||||
const initial = await width(page, "name");
|
||||
await handle.focus();
|
||||
await handle.press("Shift+ArrowRight");
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 40, 0);
|
||||
const box = (await handle.boundingBox())!;
|
||||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(box.x + box.width / 2 + 70, box.y + box.height / 2, { steps: 5 });
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 110, 0);
|
||||
await page.keyboard.press("Escape");
|
||||
await page.mouse.up();
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 40, 0);
|
||||
await page.getByRole("button", { name: "Toggle grid mount", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Toggle grid mount", exact: true }).click();
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 40, 0);
|
||||
await page.getByRole("button", { name: "Narrow grid", exact: true }).click();
|
||||
await expect.poll(() => width(page, "name")).toBeLessThan(initial + 40);
|
||||
await expectActionsUnclipped(page);
|
||||
await page.getByRole("button", { name: "Wide grid", exact: true }).click();
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 40, 0);
|
||||
await handle.press("Enter");
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial, 0);
|
||||
const resetBox = (await handle.boundingBox())!;
|
||||
await page.mouse.move(resetBox.x + resetBox.width / 2, resetBox.y + resetBox.height / 2);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(resetBox.x + resetBox.width / 2 + 50, resetBox.y + resetBox.height / 2, { steps: 5 });
|
||||
await page.mouse.up();
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 50, 0);
|
||||
await page.getByRole("button", { name: "Toggle grid mount", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Toggle grid mount", exact: true }).click();
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 50, 0);
|
||||
});
|
||||
|
||||
test("free content tracks retain explicit user resizing on remount", async ({ page }) => {
|
||||
await page.goto("/?data-grid-layout&mode=free");
|
||||
const handle = header(page, "name").getByRole("separator");
|
||||
await expectActionsUnclipped(page);
|
||||
const initial = await width(page, "name");
|
||||
await handle.press("ArrowRight");
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 10, 0);
|
||||
await page.getByRole("button", { name: "Toggle grid mount", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Toggle grid mount", exact: true }).click();
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 10, 0);
|
||||
});
|
||||
|
||||
test("empty and changing action sets reserve the same complete slots", async ({ page }) => {
|
||||
await page.goto("/?data-grid-layout");
|
||||
await expectActionsUnclipped(page);
|
||||
const initial = await width(page, "actions");
|
||||
await page.getByRole("button", { name: "Toggle empty rows", exact: true }).click();
|
||||
await expectActionsUnclipped(page);
|
||||
await expect.poll(() => width(page, "actions")).toBeCloseTo(initial, 0);
|
||||
await page.getByRole("button", { name: "Toggle empty rows", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Toggle extra action", exact: true }).click();
|
||||
await expect.poll(() => width(page, "actions")).toBeGreaterThan(initial + 35);
|
||||
await expectActionsUnclipped(page);
|
||||
});
|
||||
|
||||
test("constrained resizing redistributes tracks without introducing overflow", async ({ page }) => {
|
||||
await page.goto("/?data-grid-layout&mode=constrained");
|
||||
await expectActionsUnclipped(page);
|
||||
const initial = await width(page, "name");
|
||||
await header(page, "name").getByRole("separator").press("Shift+ArrowRight");
|
||||
await expect.poll(() => width(page, "name")).toBeCloseTo(initial + 40, 0);
|
||||
expect(await page.locator(".data-grid-scroll-region").evaluate((element) => element.scrollWidth - element.clientWidth)).toBeLessThanOrEqual(1);
|
||||
await expectActionsUnclipped(page);
|
||||
});
|
||||
|
||||
test("explicit composite action groups include outer controls in their measured minimum", async ({ page }) => {
|
||||
await page.goto("/?data-grid-layout&mode=composite");
|
||||
await expect.poll(() => width(page, "actions")).toBeGreaterThanOrEqual(221);
|
||||
await expectActionsUnclipped(page);
|
||||
await page.getByRole("button", { name: "Narrow grid", exact: true }).click();
|
||||
await expectActionsUnclipped(page);
|
||||
await page.getByRole("button", { name: "Extra control", exact: true }).click();
|
||||
await expect(page.getByTestId("clicked-action")).toHaveText("Extra control");
|
||||
});
|
||||
|
||||
test("oversized explicit sticky minima release stickiness instead of obscuring every data column", async ({ page }) => {
|
||||
await page.goto("/?data-grid-layout&mode=oversized");
|
||||
await expectActionsUnclipped(page);
|
||||
await page.getByRole("button", { name: "Narrow grid", exact: true }).click();
|
||||
await expect(page.locator(".data-grid-shell")).toHaveClass(/data-grid-release-sticky/);
|
||||
const name = page.locator('.data-grid-body-cell[data-column-id="name"]');
|
||||
await expect(name).toBeInViewport();
|
||||
await page.locator(".data-grid-scroll-region").evaluate((element) => { element.scrollLeft = element.scrollWidth; });
|
||||
await expectActionsUnclipped(page);
|
||||
await page.getByRole("button", { name: "Wide grid", exact: true }).click();
|
||||
await expect(page.locator(".data-grid-shell")).not.toHaveClass(/data-grid-release-sticky/);
|
||||
});
|
||||
Reference in New Issue
Block a user