mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
fix(hub): keep folder-row metadata on phones and name the heat dot (BEA-14) (#64)
Below 430px `.dl-meta` was `display:none`, so a folder listing on a phone showed a filename and an unexplained coloured dot — no read count, no size, no date. The comment justifying it assumed the dot carried the signal, but the dot's meaning lived entirely in a `title=` attribute: never shown on touch, never read by a screen reader on any viewport. - `.dl-row` wraps and `.dl-meta` takes a full-width second line at ≤430px, indented 27px to align under the filename. The name still wins line one and is never truncated; the full string fits at 360px, so no shortened variant is needed. - The heat dot gets `role="img"` + `aria-label` on every viewport, which also fixes desktop screen-reader users. - Playwright assertion in layout.spec.ts at 360/390/430: meta matches the desktop string, filename untruncated, rows ≥44px, no horizontal scroll, every dot has an accessible name. Desktop (≥431px) is unchanged — measured identical row heights and meta positions before and after. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
eba6fe1a64
commit
f773b0c6e7
@@ -110,3 +110,51 @@ test("the gutter belongs to the scroll container, not the column", async ({ page
|
||||
expect(r.childMax, `${path}: view sets its own max-width`).toBe("none");
|
||||
}
|
||||
});
|
||||
|
||||
/* Mobile folder rows used to drop .dl-meta entirely below 430px, leaving an
|
||||
unlabelled coloured dot as the only signal — and the dot's meaning lived in
|
||||
a title= that touch never shows and screen readers never read. The meta now
|
||||
wraps to a second line and the dot carries its own accessible name. */
|
||||
test("folder rows keep their metadata, and the heat dot a name, on a phone", async ({ page }) => {
|
||||
await login(page);
|
||||
const pid = await wikiId(page);
|
||||
const file = page.locator('.dl-row[title="notes/readme.md"]');
|
||||
const dir = page.locator('.dl-row[title="notes/deep"]');
|
||||
|
||||
await page.setViewportSize({ width: 431, height: 800 });
|
||||
await page.goto(`/${pid}/notes`);
|
||||
await page.waitForSelector(".dl-row");
|
||||
const desktopMeta = await file.locator(".dl-meta").textContent();
|
||||
|
||||
for (const width of [360, 390, 430]) {
|
||||
await page.setViewportSize({ width, height: 800 });
|
||||
await page.goto(`/${pid}/notes`);
|
||||
await page.waitForSelector(".dl-row");
|
||||
|
||||
// Same information as desktop: read count, size and date all present.
|
||||
await expect(file.locator(".dl-meta"), `${width}px: file meta`).toBeVisible();
|
||||
expect(await file.locator(".dl-meta").textContent(), `${width}px: file meta`).toBe(desktopMeta);
|
||||
await expect(file.locator(".dl-meta")).toContainText(/read/);
|
||||
await expect(dir.locator(".dl-meta"), `${width}px: folder meta`).toContainText("1 item");
|
||||
|
||||
const m = await page.evaluate(() => {
|
||||
const rows = [...document.querySelectorAll<HTMLElement>(".dl-row")];
|
||||
const name = rows.map((r) => r.querySelector(".dl-name") as HTMLElement);
|
||||
return {
|
||||
truncated: name.some((n) => n.scrollWidth > n.clientWidth + 1),
|
||||
minHeight: Math.min(...rows.map((r) => r.getBoundingClientRect().height)),
|
||||
overflow: document.documentElement.scrollWidth > document.documentElement.clientWidth,
|
||||
};
|
||||
});
|
||||
expect(m.truncated, `${width}px: filename truncated`).toBe(false);
|
||||
expect(m.minHeight, `${width}px: row tap target`).toBeGreaterThanOrEqual(44);
|
||||
expect(m.overflow, `${width}px: horizontal page scroll`).toBe(false);
|
||||
|
||||
// Accessibility tree, not pixels: every dot announces itself. (Read
|
||||
// counts accumulate across the shared hub, so match the shape not a
|
||||
// number — what matters is that no dot is nameless.)
|
||||
const dots = await page.locator(".heatdot").count();
|
||||
await expect(page.getByRole("img", { name: /read/ }), `${width}px: named dots`).toHaveCount(dots);
|
||||
await expect(file.locator(".heatdot")).toHaveAttribute("aria-label", /\d+ reads? .*in 30 days/);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -70,7 +70,16 @@ export function FolderListing(props: {
|
||||
<Icon name={c.dir ? "folder" : "doc"} />
|
||||
</span>
|
||||
<span className="dl-name">{c.name}</span>
|
||||
{he && <span className={"heatdot lvl" + heatLevel(he)} title={heatText(he) + " in 30 days"} />}
|
||||
{he && (
|
||||
/* title= needs hover, which touch never gives and screen
|
||||
readers never see — the dot carries its own name. */
|
||||
<span
|
||||
className={"heatdot lvl" + heatLevel(he)}
|
||||
role="img"
|
||||
aria-label={heatText(he) + " in 30 days"}
|
||||
title={heatText(he) + " in 30 days"}
|
||||
/>
|
||||
)}
|
||||
<span className="dl-meta">{meta}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -784,9 +784,11 @@ a.ai-main:hover { color: var(--accent); }
|
||||
.modal-actions .ai-del { margin-right: auto; }
|
||||
|
||||
@media (max-width: 430px) {
|
||||
/* The name wins the row: drop the verbose meta (the heat dot still
|
||||
conveys activity) instead of truncating "readme.md" to "readme...." */
|
||||
.dl-meta { display: none; }
|
||||
/* The name still wins the row — but the meta wraps under it instead of
|
||||
disappearing: hiding it left an unexplained coloured dot as the only
|
||||
signal. Indent 27px (16px icon + 11px gap) to align under the name. */
|
||||
.dl-row { flex-wrap: wrap; row-gap: 2px; }
|
||||
.dl-meta { flex: 1 1 100%; padding-left: 27px; }
|
||||
.ai-tag { font-size: 11px; }
|
||||
.htime { white-space: nowrap; font-size: 12px; }
|
||||
.hline { flex-wrap: wrap; }
|
||||
|
||||
+14
-14
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-CH2MFMbS.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CzGgqyHx.css">
|
||||
<script type="module" crossorigin src="/assets/index-Bbj9qBED.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DgC5ZhxZ.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Reference in New Issue
Block a user