fix(notifications): preserve action state and accessible recovery

This commit is contained in:
cesnimda
2026-10-16 23:14:33 +02:00
parent 9dfea1226f
commit 40cfe04df5
22 changed files with 5963 additions and 66 deletions
+10 -3
View File
@@ -1,4 +1,5 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import React, { useEffect, useId, useMemo, useRef, useState } from "react";
import { visuallyHidden } from "@mui/utils";
import {
AppBar,
@@ -135,6 +136,7 @@ export default function AppShell({
children: React.ReactNode;
}) {
const { language, setLanguage, t } = useI18n();
const notificationCountId = useId();
const isMobile = useMediaQuery((theme: Theme) => theme.breakpoints.down("md"));
const [desktopNavCollapsed, setDesktopNavCollapsed] = useState(() => {
try {
@@ -361,6 +363,9 @@ export default function AppShell({
return (
<Box sx={{ display: "flex", minHeight: "100vh" }}>
<Box component="span" id={notificationCountId} role="status" aria-live="polite" aria-atomic="true" sx={visuallyHidden}>
{notificationsCount === undefined ? t("notificationsUnreadCountUnavailable") : t("notificationsUnreadCount", { count: notificationsCount })}
</Box>
<Box component="a" href="#app-main-content" sx={{ position: "fixed", top: 8, left: 8, zIndex: (theme) => theme.zIndex.tooltip + 1, px: 2, py: 1, borderRadius: 1, bgcolor: "background.paper", color: "text.primary", transform: "translateY(-160%)", "&:focus": { transform: "translateY(0)" } }}>
{t("skipToMainContent")}
</Box>
@@ -431,10 +436,11 @@ export default function AppShell({
size="small"
title={t("notifications")}
aria-label={t("notifications")}
aria-describedby={notificationCountId}
onClick={(event) => onOpenNotifications?.(event.currentTarget)}
sx={{ border: "1px solid", borderColor: "divider", borderRadius: 2.5, width: 42, height: 42 }}
>
<Badge color="primary" badgeContent={notificationsCount || 0} max={99}>
<Badge color="primary" badgeContent={notificationsCount || 0} max={99} slotProps={{ badge: { "aria-hidden": true } }}>
<NotificationsNoneIcon fontSize="small" />
</Badge>
</IconButton>
@@ -497,10 +503,11 @@ export default function AppShell({
size="small"
title={t("notifications")}
aria-label={t("notifications")}
aria-describedby={notificationCountId}
onClick={(event) => onOpenNotifications?.(event.currentTarget)}
sx={{ border: "1px solid", borderColor: "divider", borderRadius: 2 }}
>
<Badge color="primary" badgeContent={notificationsCount || 0} max={99}>
<Badge color="primary" badgeContent={notificationsCount || 0} max={99} slotProps={{ badge: { "aria-hidden": true } }}>
<NotificationsNoneIcon fontSize="small" />
</Badge>
</IconButton>