Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion internal/webapp/frontend/e2e/browse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,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}`);
Expand Down
4 changes: 4 additions & 0 deletions internal/webapp/frontend/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
29 changes: 29 additions & 0 deletions internal/webapp/frontend/e2e/layout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 8 additions & 7 deletions internal/webapp/frontend/src/components/ProjectSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -219,6 +220,10 @@ export function ProjectSettings({
</CardContent>
</Card>

<PublicLinks project={project} />

<People project={project} org={org} />

<Card>
<CardHeader>
<CardTitle>About</CardTitle>
Expand Down Expand Up @@ -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">
Expand Down
22 changes: 21 additions & 1 deletion internal/webapp/frontend/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,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); }
Expand Down
Loading
Loading