fix(a11y): Name der Glocke enthält die sichtbare Zahl (Lighthouse label-content-name-mismatch)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
427969d8b9
commit
d8efbc34fa
2 files changed
+17
-12
No files matched your search
@@ -60,7 +60,7 @@ describe("NotificationBell", () => {
|
|||||||
notification({ id: "n-2", message: "Ohne Ziel", relatedType: "Colony", relatedId: "c-9" }),
|
notification({ id: "n-2", message: "Ohne Ziel", relatedType: "Colony", relatedId: "c-9" }),
|
||||||
]);
|
]);
|
||||||
renderBell();
|
renderBell();
|
||||||
await userEvent.click(await screen.findByRole("button", { name: "Benachrichtigungen" }));
|
await userEvent.click(await screen.findByRole("button", { name: /^Benachrichtigungen/ }));
|
||||||
|
|
||||||
const linked = await screen.findByText("Wartezeit-Konflikt bei B3");
|
const linked = await screen.findByText("Wartezeit-Konflikt bei B3");
|
||||||
expect(linked.closest("a")).toHaveAttribute("href", "/harvests/h-1");
|
expect(linked.closest("a")).toHaveAttribute("href", "/harvests/h-1");
|
||||||
@@ -76,7 +76,7 @@ describe("NotificationBell", () => {
|
|||||||
notification({ id: "n-2", message: "Schema", link: "https://evil.example" }),
|
notification({ id: "n-2", message: "Schema", link: "https://evil.example" }),
|
||||||
]);
|
]);
|
||||||
renderBell();
|
renderBell();
|
||||||
await userEvent.click(await screen.findByRole("button", { name: "Benachrichtigungen" }));
|
await userEvent.click(await screen.findByRole("button", { name: /^Benachrichtigungen/ }));
|
||||||
expect((await screen.findByText("Wartezeit-Konflikt bei B3")).closest("a")).toBeNull();
|
expect((await screen.findByText("Wartezeit-Konflikt bei B3")).closest("a")).toBeNull();
|
||||||
expect(screen.getByText("Schema").closest("a")).toBeNull();
|
expect(screen.getByText("Schema").closest("a")).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -84,7 +84,7 @@ describe("NotificationBell", () => {
|
|||||||
it("hides „Alle gelesen“ when nothing is unread and names the category in words", async () => {
|
it("hides „Alle gelesen“ when nothing is unread and names the category in words", async () => {
|
||||||
vi.mocked(fetchNotifications).mockResolvedValue([notification({ read: true })]);
|
vi.mocked(fetchNotifications).mockResolvedValue([notification({ read: true })]);
|
||||||
renderBell();
|
renderBell();
|
||||||
await userEvent.click(await screen.findByRole("button", { name: "Benachrichtigungen" }));
|
await userEvent.click(await screen.findByRole("button", { name: /^Benachrichtigungen/ }));
|
||||||
expect(await screen.findByText("Wartezeit")).toBeInTheDocument();
|
expect(await screen.findByText("Wartezeit")).toBeInTheDocument();
|
||||||
expect(screen.queryByRole("menuitem", { name: /Alle gelesen/ })).not.toBeInTheDocument();
|
expect(screen.queryByRole("menuitem", { name: /Alle gelesen/ })).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@@ -120,10 +120,12 @@ describe("NotificationBell", () => {
|
|||||||
expect(vi.mocked(toast.error).mock.calls[0][1]).toEqual({ id: "notification-read-error" });
|
expect(vi.mocked(toast.error).mock.calls[0][1]).toEqual({ id: "notification-read-error" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("the badge is announced with the unread count, the digit is hidden from readers", async () => {
|
it("the button name carries the visible badge text, the digit itself is hidden from readers", async () => {
|
||||||
vi.mocked(fetchNotifications).mockResolvedValue([notification({}), notification({ id: "n-2" })]);
|
vi.mocked(fetchNotifications).mockResolvedValue([notification({}), notification({ id: "n-2" })]);
|
||||||
renderBell();
|
renderBell();
|
||||||
expect(await screen.findByText("2 ungelesene Benachrichtigungen")).toHaveClass("sr-only");
|
expect(
|
||||||
|
await screen.findByRole("button", { name: "Benachrichtigungen, 2 ungelesen" }),
|
||||||
|
).toBeInTheDocument();
|
||||||
expect(screen.getByText("2", { selector: "[aria-hidden='true']" })).toBeInTheDocument();
|
expect(screen.getByText("2", { selector: "[aria-hidden='true']" })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -109,20 +109,23 @@ export function NotificationBell() {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="relative"
|
className="relative"
|
||||||
aria-label="Benachrichtigungen"
|
// The name contains the visible badge text (WCAG 2.5.3).
|
||||||
|
aria-label={
|
||||||
|
unreadCount > 0
|
||||||
|
? `Benachrichtigungen, ${unreadCount > 9 ? "9+" : unreadCount} ungelesen`
|
||||||
|
: "Benachrichtigungen"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Bell className="size-4.5" />
|
<Bell className="size-4.5" />
|
||||||
{unreadCount > 0 ? (
|
{unreadCount > 0 ? (
|
||||||
<span className="absolute -top-1 -right-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-destructive px-1 text-xs leading-none font-medium text-destructive-foreground">
|
<span
|
||||||
<span aria-hidden="true">
|
aria-hidden="true"
|
||||||
|
className="absolute -top-1 -right-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-destructive px-1 text-xs leading-none font-medium text-destructive-foreground"
|
||||||
|
>
|
||||||
{unreadCount > 9 ? "9+" : unreadCount}
|
{unreadCount > 9 ? "9+" : unreadCount}
|
||||||
</span>
|
</span>
|
||||||
<span className="sr-only">
|
|
||||||
{unreadCount} ungelesene Benachrichtigungen
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
) : null}
|
) : null}
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end" className="w-80">
|
<DropdownMenuContent align="end" className="w-80">
|
||||||
|
|||||||
Reference in new issue
Block a user