mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
fix(webapp): #content shows it scrolls, Public links moves up (BEA-79) (#128)
Project settings at 1440x900 ended at People. Public links — the section that answers "is anything of ours public right now?" — was below the fold of #content, the app's only scroll container, and with macOS overlay scrollbars nothing at rest said there was more. #content now takes a real scrollbar. The plan called for scrollbar-width + scrollbar-color alongside ::-webkit-scrollbar; measured, that combination is a no-op: Chromium drops every ::-webkit-scrollbar rule as soon as either standard property is set on the same element, and the standard properties alone leave the macOS overlay bar, which takes no width and is invisible at rest. So the standard properties sit behind @supports not selector(::-webkit-scrollbar) — Firefox only. Verified: 10px reserved in Chromium, 0 on touch. Public links also moves from fourth card to second (General → Public links → People → About → Danger zone), so the security answer is above the fold even without the cue. browse.spec reached the shares table by .last(), which the reorder would have silently repointed at the members table — both render through AdminTable. It selects .shares-table now. Known gap: Firefox on macOS always uses overlay scrollbars and no CSS overrides it, so the bar there stays hidden at rest — that is an OS setting, not something this can fix.
This commit is contained in:
@@ -435,7 +435,8 @@ test("public links: banner and settings table fit a 390px viewport", async ({ pa
|
||||
await expect(page.locator(".admin-item", { hasText: "guide.md" })).toBeVisible();
|
||||
expect(await sideways()).toBe(false);
|
||||
// The table takes its own horizontal scroll rather than widening the page.
|
||||
const box = page.locator(".project-settings .admin-card-table").last();
|
||||
// By class, not by position: People renders through the same AdminTable.
|
||||
const box = page.locator(".project-settings .shares-table");
|
||||
expect(await box.evaluate((el) => getComputedStyle(el).overflowX)).toBe("auto");
|
||||
|
||||
await page.request.delete(`/api/shares/${made.token}`);
|
||||
|
||||
@@ -31,6 +31,10 @@ export async function login(page: Page, email: string = ADMIN) {
|
||||
sessions.set(email, await page.context().cookies());
|
||||
}
|
||||
|
||||
// Note for anything that scrolls or screenshots a route: the document itself
|
||||
// never scrolls — #content is the one scroll container. A full-page screenshot
|
||||
// stops at the viewport and window.scrollTo does nothing; scroll #content.
|
||||
|
||||
export async function wikiId(page: Page): Promise<string> {
|
||||
const out = await (await page.request.get("/api/projects")).json();
|
||||
return out.projects.find((p: { name: string }) => p.name === "wiki").id;
|
||||
|
||||
@@ -111,6 +111,35 @@ test("the gutter belongs to the scroll container, not the column", async ({ page
|
||||
}
|
||||
});
|
||||
|
||||
/* BEA-79: Public links was invisible at 1440x900 — below the fold of #content,
|
||||
which is the app's only scroll container, and with overlay scrollbars nothing
|
||||
at rest said there was more. Two halves: the container now takes a real
|
||||
scrollbar (so it consumes width, so it is visible), and the security section
|
||||
sits second instead of fourth. */
|
||||
|
||||
test("settings shows Public links high, and #content shows it scrolls", async ({ page }) => {
|
||||
await login(page);
|
||||
const pid = await wikiId(page);
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await page.goto(`http://localhost:8993/${pid}/settings`);
|
||||
await expect(page.locator(".project-settings [data-slot=card-title]")).toHaveText([
|
||||
"General",
|
||||
"Public links",
|
||||
"People",
|
||||
"About",
|
||||
"Danger zone",
|
||||
]);
|
||||
|
||||
// A classic scrollbar takes layout width; an overlay one doesn't. This is
|
||||
// the affordance — no hover, no scroll event.
|
||||
const bar = await page.evaluate(() => {
|
||||
const c = document.querySelector("#content") as HTMLElement;
|
||||
return { gap: c.offsetWidth - c.clientWidth, overflows: c.scrollHeight > c.clientHeight };
|
||||
});
|
||||
expect(bar.overflows, "settings must overflow at 1440x900").toBe(true);
|
||||
expect(bar.gap, "#content reserves a visible scrollbar").toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
/* The who/when/how-hot line is what the product is differentiated by, and a
|
||||
phone is exactly when you're catching up — but ≤900px used to `display:
|
||||
none` it on the file view and ellipsise it to `claude-…` / `Alice <ali…` in
|
||||
|
||||
@@ -26,9 +26,10 @@ import { atLeast } from "../api/types";
|
||||
import type { Org, PermLevel, Project, ProjectPerms } from "../api/types";
|
||||
|
||||
// Settings for the open project (sidebar menu): General edits the name,
|
||||
// description and icon; About holds the identity facts; People says who can
|
||||
// do what; the danger zone deletes. Install/connect lives on the Installation
|
||||
// page.
|
||||
// description and icon; Public links answers "is anything of ours public right
|
||||
// now?" and sits high for that reason; People says who can do what; About
|
||||
// holds the identity facts; the danger zone deletes. Install/connect lives on
|
||||
// the Installation page.
|
||||
|
||||
const MAX_DESC = 280;
|
||||
|
||||
@@ -219,6 +220,10 @@ export function ProjectSettings({
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<PublicLinks project={project} />
|
||||
|
||||
<People project={project} org={org} />
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>About</CardTitle>
|
||||
@@ -246,10 +251,6 @@ export function ProjectSettings({
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<People project={project} org={org} />
|
||||
|
||||
<PublicLinks project={project} />
|
||||
|
||||
{/* Admin-only, and only as UX: handleProjectDelete enforces it too. */}
|
||||
{mayEdit && (
|
||||
<Card className="ps-danger">
|
||||
|
||||
@@ -400,7 +400,27 @@ button, input, a.btn { font-family: inherit; }
|
||||
/* The scroll container owns scrolling and the page gutter — never a width.
|
||||
Width and centering belong to .page (one per view), so every route shares
|
||||
the same column edges and the gutter never eats into the reading measure. */
|
||||
#content { flex: 1; overflow-y: auto; padding: 44px 40px 110px; scroll-behavior: smooth; }
|
||||
#content { flex: 1; overflow-y: auto; padding: 44px 40px 110px; scroll-behavior: smooth; scrollbar-gutter: stable; }
|
||||
/* #content is the only scroller in the app, so with the platform's overlay
|
||||
scrollbars nothing at rest says a route has more below (that's how Public
|
||||
links went missing on settings). Giving ::-webkit-scrollbar an explicit
|
||||
width turns it into a classic, always-visible bar — which is also what makes
|
||||
scrollbar-gutter mean anything on macOS. Pointer-fine only: touch keeps
|
||||
overlay scrollbars and the mobile layout is untouched. */
|
||||
@media (pointer: fine) {
|
||||
#content::-webkit-scrollbar { width: 10px; }
|
||||
#content::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 5px; }
|
||||
#content::-webkit-scrollbar-track { background: transparent; }
|
||||
/* Chromium drops every ::-webkit-scrollbar rule the moment scrollbar-width
|
||||
or scrollbar-color is set on the same element — and the standard
|
||||
properties alone leave macOS's overlay bar, which is invisible at rest and
|
||||
takes no width. So the standard properties are for engines with no webkit
|
||||
pseudo-element (Firefox) only. Setting both is the trap: it silently
|
||||
restores the bug. */
|
||||
@supports not selector(::-webkit-scrollbar) {
|
||||
#content { scrollbar-width: thin; scrollbar-color: var(--border-2) transparent; }
|
||||
}
|
||||
}
|
||||
.page { width: 100%; max-width: var(--page-app); margin-inline: auto; min-width: 0; }
|
||||
.page.read { max-width: var(--page-read); }
|
||||
.page.wide { max-width: var(--page-wide); }
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -5,8 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>BearDrive</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='%23f5a623'><rect x='4' y='4' width='5.6' height='24'/><rect x='11.2' y='4' width='14.4' height='11.2'/><rect x='11.2' y='16.8' width='16.8' height='11.2'/></svg>">
|
||||
<script type="module" crossorigin src="/assets/index-BeL6RCAd.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-C62PcDae.css">
|
||||
<script type="module" crossorigin src="/assets/index-wL8nzVtO.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BdCy9HmN.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Reference in New Issue
Block a user