fix(notifications): a row is named by what it is about, not its kind alone

This commit is contained in:
2026-10-28 22:04:06 +01:00
parent 9dd56e94d4
commit d825aff30d
2 changed files with 26 additions and 3 deletions
@@ -223,7 +223,10 @@ describe("NotificationsPanel · order", () => {
await act(async () => rerender());
expect(listUnder("inbox.sectionNeedsYou:1")).toEqual(["Activity 2"]);
const button = screen.getByRole("button", { name: "inbox.markAllRead" });
expect(button.hasAttribute("disabled")).toBe(false);
// Unavailable reads either way: natively while unfocused, aria-disabled while it holds the focus.
expect(button.hasAttribute("disabled") || button.getAttribute("aria-disabled") === "true").toBe(
false,
);
fireEvent.click(button);
await act(async () => rerender());
expect(screen.queryByText(/inbox\.sectionNeedsYou/)).toBeNull();
@@ -266,4 +269,22 @@ describe("NotificationsPanel · order", () => {
);
await waitFor(() => expect(listUnder("inbox.sectionNeedsYou:1")).toEqual(["Ready 1"]));
});
it("names each row by what it is about, so two of one kind tell apart", async () => {
// A screen reader hears the name while the keyboard is on the row, and Back finds the row it
// left by it: "Download ready" alone was every download's name.
h.server = [server(1, 1 * H, true), server(2, 2 * H, true)];
h.client = [client(3, 3 * H, { title: "Something broke", message: "Unexpected error." })];
mount();
await loaded();
expect(
[...document.querySelectorAll("[data-nav-index]")].map((r) =>
r.getAttribute("data-nav-name"),
),
).toEqual([
"inbox.downloadDone.title: Ready 1",
"inbox.downloadDone.title: Ready 2",
"Something broke: Unexpected error.",
]);
});
});
@@ -690,7 +690,9 @@ function NotificationRow({
}
return (
<div
{...navItemAttrs(navIndex, title)}
// Named by what it is about, not its kind alone: every "Download ready" read the same to a
// screen reader, and Back could not tell the row it was opened from by its name.
{...navItemAttrs(navIndex, body ? `${title}: ${body}` : title)}
{...rowProps}
className={`${SURFACE.card} group p-3.5 flex items-start gap-3 transition ${
n.read ? "opacity-70" : ""
@@ -786,7 +788,7 @@ function ClientActivityRow({
const access = n.meta?.kind === "access-requests" ? n.meta : null;
return (
<div
{...navItemAttrs(navIndex, n.title || n.message)}
{...navItemAttrs(navIndex, n.title ? `${n.title}: ${n.message}` : n.message)}
{...rowProps}
className={`${SURFACE.card} group p-3.5 flex items-start gap-3 ${needsAction ? "ring-1 ring-accent/30" : ""}`}
>