mirror of
https://github.com/Donkie/Spoolman.git
synced 2026-10-22 01:52:17 -05:00
Let dashboard cards use the screen height
Card bodies were capped at min(52vh, 420px), so a group of more than about eight spools scrolled inside its card while most of a desktop screen stayed empty below it. The cap now reaches down to the footer for a single row of cards, but stops at 1000px. A card only fetches its next page when its list is scrolled, so a body taller than one page of chips (about 1500px) shows no scrollbar and never loads the rest; the cap proposed in the issue, calc(100dvh - 220px), did exactly that on a 2160px tall screen. Narrow touch layouts keep the old cap so a card can't swallow the page's scroll. A Playwright spec pins the paging on a tall screen.
This commit is contained in:
1 parent
629b8228d8
commit
a31beee46b
2 files changed
+56
-2
No files matched your search
@@ -1012,8 +1012,13 @@
|
||||
* scrolling it to the bottom fetches the next page. */
|
||||
.card-body {
|
||||
/* Single source of truth for the height cap: the reserved min-height (set inline from
|
||||
* the spool count) is clamped to this too, so it never exceeds max-height. */
|
||||
--card-body-cap: min(52vh, 420px);
|
||||
* the spool count) is clamped to this too, so it never exceeds max-height.
|
||||
*
|
||||
* A single row of cards reaches down to the footer (#1242). The 1000px ceiling is
|
||||
* not cosmetic: the next page only loads when the body is scrolled, so the body
|
||||
* must stay shorter than one page of chips (PAGE × CHIP_H, about 1500px) or a tall
|
||||
* screen would show the first page with no scrollbar and never fetch the rest. */
|
||||
--card-body-cap: clamp(min(52vh, 420px), 100dvh - 240px, 1000px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
@@ -1127,6 +1132,11 @@
|
||||
.ctrl-label {
|
||||
display: none;
|
||||
}
|
||||
/* Cards stack on a touch screen, and a body nearly as tall as the screen would
|
||||
* swallow the swipes meant for scrolling the page past it. */
|
||||
.card-body {
|
||||
--card-body-cap: min(52vh, 420px);
|
||||
}
|
||||
}
|
||||
/* Phone: keep the controls together on the first row (title · group-by · new group)
|
||||
and give the hint the full width of the row below them. */
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { expect, test } from "./fixtures";
|
||||
import { unique } from "./helpers";
|
||||
|
||||
/**
|
||||
* A dashboard card shows its spools a page at a time and fetches the next page
|
||||
* when its list is scrolled to the bottom. The card's height cap has to stay
|
||||
* below one page of spools for that to work: on a tall screen a card that fits
|
||||
* its whole first page has no scrollbar, so the rest would never load (#1242).
|
||||
*/
|
||||
|
||||
const API_BASE =
|
||||
process.env.SPOOLMAN_API_URL ?? `${process.env.SPOOLMAN_BASE_URL ?? "http://localhost:8001"}/api/v1`;
|
||||
|
||||
test.use({ viewport: { width: 1920, height: 2160 } });
|
||||
|
||||
test("a big group's card loads every spool on a tall screen", async ({ page, request }) => {
|
||||
const location = unique("Rack");
|
||||
const res = await request.post(`${API_BASE}/filament`, {
|
||||
data: { name: unique("Filament"), density: 1.24, diameter: 1.75 },
|
||||
});
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const filamentId = (await res.json()).id;
|
||||
// More than one page of spools (30).
|
||||
const ids: number[] = [];
|
||||
for (let i = 0; i < 40; i++) {
|
||||
const spool = await request.post(`${API_BASE}/spool`, { data: { filament_id: filamentId, location } });
|
||||
expect(spool.ok()).toBeTruthy();
|
||||
ids.push((await spool.json()).id);
|
||||
}
|
||||
|
||||
try {
|
||||
await page.goto("/dashboard?by=location");
|
||||
const card = page.locator(".card").filter({ has: page.getByText(location, { exact: true }) });
|
||||
const body = card.locator(".card-body");
|
||||
await expect(body.locator(".chip")).toHaveCount(30);
|
||||
|
||||
await body.evaluate((el) => el.scrollTo(0, el.scrollHeight));
|
||||
await expect(body.locator(".chip")).toHaveCount(40);
|
||||
} finally {
|
||||
// Leave the shared database as the other specs expect it.
|
||||
for (const id of ids) await request.delete(`${API_BASE}/spool/${id}`);
|
||||
await request.delete(`${API_BASE}/filament/${filamentId}`);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user