From 7429cff0096918fb0fe9dc2a55506a93ee5ee89e Mon Sep 17 00:00:00 2001 From: "Zimin A.N." Date: Mon, 11 May 2026 22:25:06 +0300 Subject: [PATCH] fix(a11y): add sr-only DialogTitle/Description to record/export/timetravel modals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Radix Dialog warns when DialogContent отсутствует accessible name/description. RecordDrawer (Sheet) уже имел Title, добавили sr-only Description. ConflictDiffModal/ExportModal/TimeTravelModal — кастомные visible headers, теперь имеют sr-only Title + Description для screen readers + чтобы заглушить dev warnings. --- .../src/components/editor/ExportModal.tsx | 12 +++++++++++- .../src/components/record/ConflictDiffModal.tsx | 13 ++++++++++++- .../src/components/record/RecordDrawer.tsx | 6 ++++++ .../src/components/timetravel/TimeTravelModal.tsx | 12 +++++++++++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/ordinis-admin-ui/src/components/editor/ExportModal.tsx b/ordinis-admin-ui/src/components/editor/ExportModal.tsx index b816b1d..8f6a99d 100644 --- a/ordinis-admin-ui/src/components/editor/ExportModal.tsx +++ b/ordinis-admin-ui/src/components/editor/ExportModal.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Dialog, DialogContent } from '@/ui' +import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/ui' import type { DictionaryDetail, FlattenedRecord } from '@/api/client' import { cn } from '@/lib/utils' @@ -175,6 +175,16 @@ export function ExportModal({ return ( !v && onClose()}> + {/* Radix a11y: visually-hidden Title + Description. Visible heading + * рендерится ниже как кастомный layout. */} + + {t('export.title', { defaultValue: 'Экспорт' })} — {detail.name} + + + {t('export.description', { + defaultValue: 'Выбор формата, объёма и колонок для выгрузки.', + })} + {/* Header */}
diff --git a/ordinis-admin-ui/src/components/record/ConflictDiffModal.tsx b/ordinis-admin-ui/src/components/record/ConflictDiffModal.tsx index 455162f..1390b5e 100644 --- a/ordinis-admin-ui/src/components/record/ConflictDiffModal.tsx +++ b/ordinis-admin-ui/src/components/record/ConflictDiffModal.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { ArrowsClockwiseIcon, WarningIcon } from '@phosphor-icons/react' -import { Dialog, DialogContent } from '@/ui' +import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/ui' import { useRecordRaw } from '@/api/queries' import type { CreateRecordRequest } from '@/api/client' import { cn } from '@/lib/utils' @@ -75,6 +75,17 @@ export function ConflictDiffModal({ return ( !v && onClose()}> + {/* Radix a11y: visually-hidden Title + Description satisfy + * DialogContent requirements. Visible header — custom layout ниже. */} + + {t('conflict.title', { defaultValue: 'Запись изменена другим пользователем' })} + + + {t('conflict.description', { + defaultValue: + 'Сравнение локальных правок с актуальным состоянием на сервере.', + })} + {/* Header */}
diff --git a/ordinis-admin-ui/src/components/record/RecordDrawer.tsx b/ordinis-admin-ui/src/components/record/RecordDrawer.tsx index a2efc2d..9977019 100644 --- a/ordinis-admin-ui/src/components/record/RecordDrawer.tsx +++ b/ordinis-admin-ui/src/components/record/RecordDrawer.tsx @@ -156,6 +156,12 @@ export function RecordDrawer({ {caption} + {/* sr-only Description чтобы Radix не ругался на missing + * `aria-describedby` (a11y warning). Visible description нам не + * нужен — caption + recordId уже описывают drawer. */} + + {caption}{recordId ? ` (${recordId})` : ''} + {recordId && (
{recordId}
)} diff --git a/ordinis-admin-ui/src/components/timetravel/TimeTravelModal.tsx b/ordinis-admin-ui/src/components/timetravel/TimeTravelModal.tsx index aaec01b..2860e21 100644 --- a/ordinis-admin-ui/src/components/timetravel/TimeTravelModal.tsx +++ b/ordinis-admin-ui/src/components/timetravel/TimeTravelModal.tsx @@ -1,7 +1,7 @@ import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { ClockCounterClockwiseIcon, CaretLeftIcon, CaretRightIcon, XIcon } from '@phosphor-icons/react' -import { Dialog, DialogContent } from '@/ui' +import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/ui' import type { DictionaryDetail } from '@/api/client' import { cn } from '@/lib/utils' @@ -127,6 +127,16 @@ export function TimeTravelModal({ return ( !v && onClose()}> + {/* Radix a11y: sr-only Title + Description. Visible header — custom layout ниже. */} + + {t('timeTravel.title', { defaultValue: 'Состояние справочника на момент времени' })} + + + {t('timeTravel.description', { + defaultValue: + 'Сравнение текущего состояния справочника с выбранной точкой во времени.', + })} + {/* Header */}