fix(a11y): keyboard-reachable admin log rows, range-bar marker, heatmap cells

Admin app-log rows opened their JSON detail dialog through a click-only
<tr> with no keyboard path; the Action cell now wraps a real <button>
that carries the same handler plus a descriptive aria-label, leaving
the row onClick as a pointer convenience.

The target range-bar's current-value marker showed value + band only
in a hover-only tooltip on a non-focusable div. It is now focusable
(tabIndex, role=img) with an aria-label that reuses the same value /
range / delta copy the tooltip already renders, so focus opens the
tooltip and a screen reader gets the text alternative either way.

Mood and medication-compliance heatmap day cells were pointer-only SVG
rects. Each cell is now focusable with its own per-day aria-label
(reusing the existing tooltip copy), and Escape dismisses any open
tooltip — pinned (touch) or focus-shown.

Reference: 2026-07-17 a11y audit, H1 + M1 + M2.
This commit is contained in:
Marc-André Bombeck committed 2026-07-18 01:23:37 +02:00
1 parent 247bc4f33c
commit 4c436d7dfc
15 files changed
+243 -27

No files matched your search

@@ -98,3 +98,16 @@ describe("AppLogPreviewSection — summary lives outside the scroll wrapper", ()
expect(wrapper![0]).not.toContain("app-log-preview-summary");
});
});
describe("AppLogPreviewSection — row detail is keyboard-reachable (2026-07-17 a11y audit H1)", () => {
// The row itself stays a pointer-only convenience (`onClick` on the
// `<tr>`); a real `<button>` in the Action cell gives keyboard / switch
// users a focusable, activatable path to the same detail dialog.
it("renders a real button carrying the action label and a descriptive aria-label", () => {
const html = render();
expect(html).toMatch(
/<button[^>]*type="button"[^>]*>auth\.login\.success</,
);
expect(html).toContain('aria-label="View details for auth.login.success');
});
});