mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
v1.4.15 A2 hid columns + tightened card padding, but Marc still saw a scrollbar on prod at /admin/api-tokens on Pixel-5 (393 CSS px). The fundamental issue is that even with three columns visible, long permission lists or token names could push the scroll wrapper wide inside the card. Mirror the `<UserManagementSection>` pattern instead: gate the desktop `<table>` behind `hidden md:block` and render a real mobile card-list at <md. The mobile card-list: - One <li> per token, padding p-3, no horizontal scroll wrapper. - Token name uses `truncate break-all` so even a hex-suffixed name wraps within card width. - Permission badges sit in a `flex-wrap` row. - Last-used + Created collapse to short meta lines (no nowrap). - Status badge floats next to the name on the same flex-wrap row. Tests: - `api-token-overview-responsive.test.tsx` rewritten to assert the new invariants: hidden md:block on the desktop table; md:hidden on the card-list <ul> with `data-testid="admin-tokens-mobile-list"`; no overflow-x-auto inside the mobile container; meta paragraphs wrap. - New e2e spec `admin-api-tokens-mobile.spec.ts` mocks a worst-case payload (long username, multiple permissions, long token name) and asserts `documentElement.scrollWidth <= window.innerWidth + 1` at the Pixel-5 viewport on /admin/api-tokens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
78 lines
2.7 KiB
TypeScript
78 lines
2.7 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
import { STORAGE_STATE_PATH } from "./setup/global-setup";
|
|
|
|
/**
|
|
* v1.4.16 A3 — `/admin/api-tokens` must not produce horizontal page
|
|
* overflow on a Pixel-5 viewport (393 CSS px). v1.4.15 added column-
|
|
* hide breakpoints + tighter card padding, but Marc still saw a
|
|
* scrollbar on prod. The v1.4.16 fix swaps the desktop `<table>` for a
|
|
* mobile card-list at <md, mirroring `<UserManagementSection>`.
|
|
*
|
|
* Mobile-only spec — the desktop project skips it.
|
|
*/
|
|
test.describe("/admin/api-tokens mobile no-overflow", () => {
|
|
test.use({ storageState: STORAGE_STATE_PATH });
|
|
|
|
test.beforeEach(({}, testInfo) => {
|
|
test.skip(testInfo.project.name !== "chromium-mobile", "mobile-only spec");
|
|
});
|
|
|
|
test("Pixel-5 viewport: no horizontal page scroll on /admin/api-tokens", async ({
|
|
page,
|
|
}) => {
|
|
// Mock the admin tokens endpoint with a moderately wide payload —
|
|
// a token with multiple permissions, a long name, and timestamps.
|
|
// This is the realistic worst case Marc hit on prod.
|
|
await page.route("**/api/admin/tokens", (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
data: [
|
|
{
|
|
id: "tok1",
|
|
name: "iOS app device-AABBCCDDEEFF",
|
|
permissions: ["measurements:write", "medications:read", "*"],
|
|
lastUsedAt: "2026-05-08T12:00:00Z",
|
|
expiresAt: null,
|
|
createdAt: "2026-05-01T08:00:00Z",
|
|
revoked: false,
|
|
user: { id: "u1", username: "marc-the-very-long-username" },
|
|
},
|
|
{
|
|
id: "tok2",
|
|
name: "Native client xyz",
|
|
permissions: ["*"],
|
|
lastUsedAt: null,
|
|
expiresAt: "2026-08-01T00:00:00Z",
|
|
createdAt: "2026-04-15T08:00:00Z",
|
|
revoked: false,
|
|
user: { id: "u1", username: "marc-the-very-long-username" },
|
|
},
|
|
],
|
|
error: null,
|
|
}),
|
|
}),
|
|
);
|
|
|
|
await page.goto("/admin/api-tokens", { waitUntil: "domcontentloaded" });
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
// The mobile card-list must be visible and the desktop table must
|
|
// be display:none at this viewport.
|
|
await expect(
|
|
page.getByTestId("admin-tokens-mobile-list"),
|
|
).toBeVisible({ timeout: 10_000 });
|
|
|
|
const dims = await page.evaluate(() => ({
|
|
scrollWidth: document.documentElement.scrollWidth,
|
|
innerWidth: window.innerWidth,
|
|
}));
|
|
expect(
|
|
dims.scrollWidth,
|
|
`scrollWidth=${dims.scrollWidth}, innerWidth=${dims.innerWidth}`,
|
|
).toBeLessThanOrEqual(dims.innerWidth + 1);
|
|
});
|
|
});
|