fix(panel): unify fixed headers and collapsible auth sources

This commit is contained in:
Lemon-miaow committed 2026-10-05 15:43:57 +08:00
1 parent 34b450bf2c
commit d7ae1e66c1
15 files changed
+223 -63

No files matched your search

+1
View File
@@ -31,6 +31,7 @@ for (const [account, path] of [
["owner", "/"],
["owner", "/servers"],
["owner", "/admin/lobby?space=login"],
["owner", "/admin/auth-sources"],
["owner", "/admin/images"],
["owner", "/admin/builds"],
["owner", "/admin/submissions"],
+3 -2
View File
@@ -7,6 +7,7 @@ test("Owner manages durable authentication sources from the sidebar", async ({ p
await expect(page).toHaveURL(/\/admin\/auth-sources$/);
await expect(page.getByLabel(t("authSources:tag"))).toHaveValue("littleskin");
await expect(page.getByLabel(t("authSources:tag"))).toHaveAttribute("readonly", "");
await page.getByRole("button", { name: t("authSources:expand", { name: "littleskin" }) }).click();
await page.getByRole("button", { name: t("authSources:add"), exact: true }).click();
await page.getByLabel(t("authSources:tag")).nth(1).fill("custom");
await page.getByLabel(t("authSources:prefix")).nth(1).fill("CS");
@@ -14,12 +15,12 @@ test("Owner manages durable authentication sources from the sidebar", async ({ p
await page.getByRole("button", { name: t("authSources:test"), exact: true }).nth(1).click();
await expect(page.getByText(t("authSources:test_ok", { ms: 20 }), { exact: true })).toBeVisible();
await page.getByRole("button", { name: t("authSources:move_up"), exact: true }).nth(1).click();
await page.getByRole("checkbox", { name: t("authSources:enabled"), exact: true }).nth(1).uncheck();
await page.getByRole("switch", { name: t("authSources:enabled"), exact: true }).nth(1).uncheck();
await page.getByRole("button", { name: t("authSources:save"), exact: true }).click();
await expect(page.getByText(t("authSources:saved"), { exact: true })).toBeVisible();
await page.reload();
await expect(page.getByLabel(t("authSources:tag")).nth(0)).toHaveValue("custom");
await expect(page.getByRole("checkbox", { name: t("authSources:enabled"), exact: true }).nth(1)).not.toBeChecked();
await expect(page.getByRole("switch", { name: t("authSources:enabled"), exact: true }).nth(1)).not.toBeChecked();
await expect(page.getByRole("button", { name: t("authSources:remove"), exact: true })).toHaveCount(0);
const available = await page.request.get("/api/v1/account/link/sources");
expect((await available.json()).sources.map((source: { tag: string }) => source.tag)).toEqual(["mojang", "custom"]);
+12
View File
@@ -36,6 +36,18 @@ export const test = base.extend<{ signIn: (account: Account) => Promise<void> }>
export { expect };
export async function expectPinnedHeading(page: Page, title: string, top: number) {
const heading = page.getByRole("heading", { name: title, exact: true });
const before = (await heading.boundingBox())!;
const pane = page.getByRole("main");
await pane.evaluate((el) => { el.scrollTop = el.scrollHeight; });
await expect.poll(() => pane.evaluate((el) => el.scrollTop)).toBeGreaterThan(100);
const after = (await heading.boundingBox())!;
expect(after.y).toBeCloseTo(before.y, 0);
expect(after.y).toBeGreaterThanOrEqual(top);
await expect(heading).toBeInViewport({ ratio: 1 });
}
/** expectFitsScreen fails when anything scrolls sideways or a visible element
* pokes past the viewport edge (an overflow-hidden parent would just cut it
* off). The shell scrolls in an overflow-y-auto pane, whose computed
+10 -1
View File
@@ -1,4 +1,4 @@
import { test, expect, t, expectFitsScreen } from "./fixtures";
import { test, expect, t, expectFitsScreen, expectPinnedHeading } from "./fixtures";
test("the menu drawer reaches the other sections", async ({ page, signIn }) => {
await signIn("linked");
@@ -71,3 +71,12 @@ test("authentication source drafts with long IDs fit a phone", async ({ page, si
await expectFitsScreen(page);
await expect(page.getByRole("button", { name: t("authSources:remove"), exact: true })).toBeVisible();
});
test("the mobile navigation and page heading stay visible above a scrolling form", async ({ page, signIn }) => {
await signIn("owner");
await page.goto("/admin/lobby");
await expect(page.getByLabel(t("lobby:bookTitle"))).toBeEnabled();
await expectPinnedHeading(page, t("lobby:title"), 56);
await expect(page.getByRole("button", { name: t("common:open_menu") })).toBeInViewport({ ratio: 1 });
await expectFitsScreen(page);
});
+54 -1
View File
@@ -1,4 +1,57 @@
import { test, expect, t, expectFitsScreen } from "./fixtures";
import { test, expect, t, expectFitsScreen, expectPinnedHeading } from "./fixtures";
test("admin headings share their size and gutters and stay pinned while content scrolls", async ({ page, signIn }) => {
await signIn("owner");
await page.setViewportSize({ width: 1440, height: 600 });
const metrics = [];
for (const [path, title] of [
["/admin/submissions", "admin:submissions_title"],
["/admin/lobby", "lobby:title"],
["/admin/auth-sources", "authSources:title"],
]) {
await page.goto(path);
const heading = page.getByRole("heading", { name: t(title), exact: true });
await expect(heading).toBeVisible();
metrics.push(await heading.evaluate((el) => ({ x: el.getBoundingClientRect().x, y: el.getBoundingClientRect().y, size: getComputedStyle(el).fontSize })));
expect(await page.getByRole("main").getByRole("heading", { level: 1 }).count()).toBe(0);
if (path === "/admin/auth-sources") {
await page.getByRole("button", { name: t("authSources:expand", { name: "littleskin" }) }).click();
await expectPinnedHeading(page, t(title), 0);
} else if (path === "/admin/lobby") {
await expect(page.getByLabel(t("lobby:bookTitle"))).toBeEnabled();
await expectPinnedHeading(page, t(title), 0);
}
}
expect(metrics[1]).toEqual(metrics[0]);
expect(metrics[2]).toEqual(metrics[0]);
});
test("saved authentication sources start closed and switches leave them closed", async ({ page, signIn }) => {
await signIn("owner");
await page.goto("/admin/auth-sources");
const expand = page.getByRole("button", { name: t("authSources:expand", { name: "littleskin" }) });
await expect(expand).toHaveAttribute("aria-expanded", "false");
await expect(page.getByLabel(t("authSources:prefix"))).toBeHidden();
const enabled = page.getByRole("switch", { name: t("authSources:enabled"), exact: true });
await enabled.focus();
await page.keyboard.press("Space");
await expect(enabled).toHaveAttribute("aria-checked", "false");
await expect(expand).toHaveAttribute("aria-expanded", "false");
await page.getByRole("button", { name: t("authSources:save"), exact: true }).click();
await expect(page.getByText(t("authSources:saved"), { exact: true })).toBeVisible();
await expand.click();
await expect(page.getByLabel(t("authSources:prefix"))).toBeVisible();
});
test("the console keeps a bounded log pane below the fixed heading", async ({ page, signIn }) => {
await signIn("owner");
await page.setViewportSize({ width: 1440, height: 960 });
await page.goto("/servers/lobby");
await expect(page.getByRole("textbox", { name: t("servers:command_label") })).toBeInViewport({ ratio: 1 });
await expect(page.getByRole("button", { name: t("servers:log_clear_btn") })).toBeInViewport({ ratio: 1 });
await expect(page.getByRole("heading", { name: "Hub Lobby", exact: true })).toBeInViewport({ ratio: 1 });
expect(await page.getByRole("main").evaluate((el) => el.scrollTop)).toBe(0);
});
test("space settings keep the form visible while reading configuration", async ({ page, signIn }) => {
await signIn("owner");