diff --git a/ApiCore-Frontend/src/components/notification-bell.spec.tsx b/ApiCore-Frontend/src/components/notification-bell.spec.tsx
index 3756bac..0b00367 100644
--- a/ApiCore-Frontend/src/components/notification-bell.spec.tsx
+++ b/ApiCore-Frontend/src/components/notification-bell.spec.tsx
@@ -60,7 +60,7 @@ describe("NotificationBell", () => {
notification({ id: "n-2", message: "Ohne Ziel", relatedType: "Colony", relatedId: "c-9" }),
]);
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");
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" }),
]);
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(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 () => {
vi.mocked(fetchNotifications).mockResolvedValue([notification({ read: true })]);
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(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" });
});
- 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" })]);
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();
});
diff --git a/ApiCore-Frontend/src/components/notification-bell.tsx b/ApiCore-Frontend/src/components/notification-bell.tsx
index 47fd2db..78535df 100644
--- a/ApiCore-Frontend/src/components/notification-bell.tsx
+++ b/ApiCore-Frontend/src/components/notification-bell.tsx
@@ -109,19 +109,22 @@ export function NotificationBell() {
variant="ghost"
size="icon"
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"
+ }
/>
}
>
{unreadCount > 0 ? (
-
-
- {unreadCount > 9 ? "9+" : unreadCount}
-
-
- {unreadCount} ungelesene Benachrichtigungen
-
+
+ {unreadCount > 9 ? "9+" : unreadCount}
) : null}