fix(a11y): make confirmation messages keyboard scrollable

This commit is contained in:
cesnimda
2026-10-20 09:32:04 +02:00
parent e5aba4cf69
commit 985c71989f
3 changed files with 17 additions and 3 deletions
+4
View File
@@ -31,6 +31,10 @@ test('renders app-owned confirmation dialog', async () => {
expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument();
expect(screen.getByRole('dialog', { name: 'Delete job' })).toHaveAccessibleDescription('Are you sure?');
expect(screen.getByRole('button', { name: 'Cancel' })).toHaveFocus();
const messageRegion = screen.getByRole('region', { name: 'Delete job' });
expect(messageRegion).toHaveAttribute('tabindex', '0');
messageRegion.focus();
expect(messageRegion).toHaveFocus();
});
test('uses the selected language for shared dialog defaults', async () => {
+4 -3
View File
@@ -23,6 +23,7 @@ const ConfirmContext = createContext<ConfirmContextValue | null>(null);
export function ConfirmProvider({ children }: { children: React.ReactNode }) {
const i18n = useOptionalI18n();
const messageId = useId();
const titleId = useId();
const resolverRef = useRef<((value: boolean) => void) | null>(null);
const cancelButtonRef = useRef<HTMLButtonElement>(null);
const [state, setState] = useState<ConfirmState>({
@@ -60,13 +61,13 @@ export function ConfirmProvider({ children }: { children: React.ReactNode }) {
return (
<ConfirmContext.Provider value={value}>
{children}
<Dialog open={state.open} onClose={() => closeWith(false)} aria-describedby={messageId} fullWidth maxWidth="xs"
<Dialog open={state.open} onClose={() => closeWith(false)} aria-labelledby={titleId} aria-describedby={messageId} fullWidth maxWidth="xs"
slotProps={{
paper: { sx: { borderRadius: 3, border: 1, borderColor: "divider", backgroundImage: "none" } },
transition: { onEntered: () => cancelButtonRef.current?.focus() },
}}>
<DialogTitle sx={{ pb: 1, fontWeight: 800 }}>{state.title}</DialogTitle>
<DialogContent>
<DialogTitle id={titleId} sx={{ pb: 1, fontWeight: 800 }}>{state.title}</DialogTitle>
<DialogContent role="region" aria-labelledby={titleId} tabIndex={0}>
<Alert severity={state.destructive ? "warning" : "info"} variant="outlined" sx={{ mb: 2 }}>
{state.destructive
? i18n?.t("confirmDestructiveHint") ?? "This action may be hard to undo."