mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
fix(hub): the history kind is a badge, not a fake disclosure toggle (BEA-17) (#67)
* fix(hub): the history kind is a badge, not a fake disclosure toggle (BEA-17) The +/x in a history row is the kind glyph (added/edited/deleted), but sitting leftmost inside a role="button" row it read as a tree disclosure control — clicked, it navigated away instead of expanding. Merge the glyph and its word into one text badge and vacate the toggle slot: the kind is now text (no icon shape, no colour-only meaning), and the row's only real expander stays the note, which keeps its own control and aria-expanded and now turns its chevron when open. * fix(hub): fit DELETED in the kind badge and align the row's meta under the path --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f35d889cfb
commit
411809a785
@@ -160,6 +160,7 @@ func seedE2E(t *testing.T, state, prefix, projectID string) {
|
||||
png := "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89" +
|
||||
"\x00\x00\x00\nIDATx\x9cc\x00\x01\x00\x00\x05\x00\x01\r\n-\xb4\x00\x00\x00\x00IEND\xaeB`\x82"
|
||||
put("assets/logo.png", png, 24*time.Hour)
|
||||
ops[2].Note = "expanded the guide — https://claude.ai/session/e2e" // the one row with a note expander
|
||||
// A second version of the same binary, so the history diff has a
|
||||
// predecessor to refuse to diff (the "binary — no diff" path).
|
||||
put("assets/logo.png", png+"\x00trailing", 3*time.Hour)
|
||||
|
||||
@@ -48,6 +48,33 @@ test("folder listing: counts, change feed, heat dot on a read file", async ({ pa
|
||||
await expect(page.locator('.dl-row[title="notes/readme.md"] .heatdot')).toBeVisible();
|
||||
});
|
||||
|
||||
// BEA-17: the kind glyph read as a disclosure toggle. It is now a text
|
||||
// badge, the row's only real expander is the note, and clicking the badge
|
||||
// navigates like the rest of the row — no dead zone, no second behavior.
|
||||
test("history row: kind is a badge, not a disclosure control", async ({ page }) => {
|
||||
await login(page);
|
||||
const pid = await wikiId(page);
|
||||
await page.goto(`/${pid}/history/guide.md`);
|
||||
const row = page.locator(".history .hentry").first();
|
||||
await expect(row).toBeVisible();
|
||||
// kind is conveyed as text, not by an icon shape
|
||||
await expect(row.locator(".hkind")).toHaveText("edited");
|
||||
await expect(row.locator(".hkind .ico")).toHaveCount(0);
|
||||
// a row announces kind, path and author without the icon
|
||||
await expect(page.getByRole("button", { name: /edited\s+guide\.md.*alice@x\.io/s })).toHaveCount(1);
|
||||
// only genuine expanders claim to expand: the note and the diff disclosure
|
||||
await expect(page.locator(".history .hnote[aria-expanded]")).toHaveCount(1);
|
||||
await expect(page.locator(".history [aria-expanded]:not(.hnote):not(.hdiff-btn)")).toHaveCount(0);
|
||||
// ...and it still expands in place, without navigating
|
||||
await row.locator(".hnote").click({ position: { x: 6, y: 6 } }); // off the note's link
|
||||
await expect(row.locator(".hnote")).toHaveClass(/open/);
|
||||
await expect(page).toHaveURL(`/${pid}/history/guide.md`);
|
||||
// clicking the badge does exactly what clicking the row does: it opens
|
||||
// the version the row describes (BEA-7), not a dead zone
|
||||
await row.locator(".hkind").click();
|
||||
await page.waitForURL(new RegExp(`/${pid}/guide\\.md\\?v=[0-9a-f]{64}$`));
|
||||
});
|
||||
|
||||
test("image file renders an <img>", async ({ page }) => {
|
||||
await login(page);
|
||||
const pid = await wikiId(page);
|
||||
|
||||
@@ -156,8 +156,9 @@ test("history: whole project, newest first, and per-file versions", async ({ pag
|
||||
await expect(page.locator("#crumb")).toContainText("History — guide.md");
|
||||
await expect(page.locator(".history .hentry")).toHaveCount(2);
|
||||
await expect(page.locator(".history .hentry").first()).toContainText("edited");
|
||||
// clicking an entry opens THAT version of the file (BEA-7)
|
||||
await page.click(".history .hentry.clickable >> nth=0");
|
||||
// clicking an entry opens THAT version of the file (BEA-7); aim at the
|
||||
// path cell — the row's center can land on its expandable note
|
||||
await page.click(".history .hentry.clickable >> nth=0 >> .hpath");
|
||||
await page.waitForURL(new RegExp(`/${pid}/guide\\.md\\?v=[0-9a-f]{64}$`));
|
||||
});
|
||||
|
||||
|
||||
@@ -5,8 +5,13 @@ import { Icon } from "./shell";
|
||||
import { DiffView } from "./DiffView";
|
||||
|
||||
/* One change as a row: what happened (added / edited / deleted), to which
|
||||
file, by whom, from where — with the note (session link) expandable. */
|
||||
const KIND_ICON: Record<string, string> = { add: "plus", edit: "edit", delete: "x" };
|
||||
file, by whom, from where — with the note (session link) expandable.
|
||||
|
||||
The kind is a word in a badge, never a +/✕ glyph in the row's leftmost
|
||||
slot: sitting there, inside a role="button" row, it read as a disclosure
|
||||
toggle it never was. The toggle slot stays free for a real per-row
|
||||
disclosure; the only genuine expansion here is the note, which owns its
|
||||
own control and its own aria-expanded. */
|
||||
const KIND_LABEL: Record<string, string> = { add: "added", edit: "edited", delete: "deleted" };
|
||||
|
||||
export function HistoryRow({
|
||||
@@ -50,11 +55,8 @@ export function HistoryRow({
|
||||
}}
|
||||
>
|
||||
<div className="hline">
|
||||
<span className="hkind">
|
||||
<Icon name={KIND_ICON[kind] || "dot"} />
|
||||
</span>
|
||||
<span className="hkind">{KIND_LABEL[kind] || kind}</span>
|
||||
<span className="hpath">{e.path}</span>
|
||||
<span className="htag">{KIND_LABEL[kind] || kind}</span>
|
||||
<span className="htime">{new Date(e.time).toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="hmeta">
|
||||
|
||||
@@ -631,27 +631,27 @@ a.ai-main:hover { color: var(--accent); }
|
||||
|
||||
/* ---- history ---- */
|
||||
/* .history width comes from .page (app) */
|
||||
.hentry { padding: 11px 12px; border-bottom: 1px solid var(--border); }
|
||||
.hentry { padding: 11px 12px; border-bottom: 1px solid var(--border); --hindent: 72px; }
|
||||
.hentry:hover { background: rgba(255,255,255,.015); }
|
||||
.hline { display: flex; gap: 10px; align-items: center; }
|
||||
.hkind { display: inline-flex; color: var(--add); }
|
||||
.hkind .ico { width: 13px; height: 13px; }
|
||||
.hentry.edit .hkind { color: var(--accent); }
|
||||
.hentry.delete .hkind { color: var(--del); }
|
||||
.htag { flex: none; font-size: 10px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600; padding: 1px 6px; border-radius: 4px; }
|
||||
.hentry.add .htag { color: var(--add); background: rgba(76,195,138,.12); }
|
||||
.hentry.edit .htag { color: var(--accent-bright); background: var(--glow); }
|
||||
.hentry.delete .htag { color: #ff8b8b; background: rgba(242,109,109,.12); }
|
||||
/* The kind is a status badge, not a control: a fixed-width pill of text,
|
||||
so nothing in the row's leftmost slot has the size or shape of a
|
||||
disclosure toggle. --hindent keeps meta and note aligned under the path. */
|
||||
.hkind { flex: none; width: 62px; white-space: nowrap; text-align: center; font-size: 10px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600; padding: 2px 6px; border-radius: 4px; color: var(--add); background: rgba(76,195,138,.12); }
|
||||
.hentry.edit .hkind { color: var(--accent-bright); background: var(--glow); }
|
||||
.hentry.delete .hkind { color: #ff8b8b; background: rgba(242,109,109,.12); }
|
||||
.hpath { font-weight: 500; cursor: pointer; color: var(--text); font-size: 13px; }
|
||||
.hpath:hover { color: var(--accent-bright); }
|
||||
.htime { margin-left: auto; color: var(--text-faint); font-size: 12px; font-variant-numeric: tabular-nums; }
|
||||
.hmeta { display: flex; align-items: center; gap: 14px; margin-top: 4px; padding-left: 23px; font-size: 12px; color: var(--text-dim); }
|
||||
.hmeta { display: flex; align-items: center; gap: 14px; margin-top: 4px; padding-left: var(--hindent); font-size: 12px; color: var(--text-dim); }
|
||||
.hdev, .hsize { color: var(--text-faint); }
|
||||
.hsize { font-variant-numeric: tabular-nums; white-space: nowrap; flex: none; }
|
||||
.hnote { margin-top: 4px; padding-left: 23px; font-size: 12px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
|
||||
.hnote { margin-top: 4px; padding-left: var(--hindent); font-size: 12px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
|
||||
.hnote:hover { color: var(--text); }
|
||||
.hnote.open { white-space: normal; overflow-wrap: anywhere; }
|
||||
.hnote::before { content: "› "; color: var(--text-ghost); }
|
||||
/* The one real disclosure in a row — so it gets the chevron, and turns. */
|
||||
.hnote::before { content: "›"; display: inline-block; margin-right: 5px; color: var(--text-ghost); transition: transform .12s; }
|
||||
.hnote.open::before { transform: rotate(90deg); }
|
||||
.hnote a { color: var(--accent-bright); text-decoration: none; }
|
||||
.hnote a:hover { text-decoration: underline; }
|
||||
|
||||
@@ -829,6 +829,9 @@ a.ai-main:hover { color: var(--accent); }
|
||||
.ai-tag { font-size: 11px; }
|
||||
.htime { white-space: nowrap; font-size: 12px; }
|
||||
.hline { flex-wrap: wrap; }
|
||||
/* 78px of indent is a fifth of a 360px row, and the wrapped .hline has
|
||||
nothing left to align under anyway. */
|
||||
.hentry { --hindent: 0px; }
|
||||
/* Four share-dialog buttons don't fit one row at 360 — the destructive
|
||||
Revoke takes its own line rather than sitting 9px from Done. */
|
||||
.modal-actions .ai-del { flex: 0 0 100%; justify-content: center; text-align: center; }
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+6
-6
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-BSJZ_rnL.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D8NgjNS2.css">
|
||||
<script type="module" crossorigin src="/assets/index-pwEy71bM.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D_6vQyDA.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Reference in New Issue
Block a user