Review rodiny Dialog — nálezy, bug list, rozhodnutí pro AD
Datum: 4. 9. 2026 · zdroj pravdy: origin/main acrm (/root/acrm-main, ea77d2cdb) · web: <http://192.168.230.131:3777/ds/components/dialog/dialog?preview=ds-overlays-stories>
0. Shrnutí
Dialog je jedna z nejlépe napsaných komponent v systému: žebřík velikostí, zásobník Esc, stavy okna bez ztráty obsahu, dok minimalizovaných oken, mobilní chování — všechno v jednom souboru a odůvodněné. Vady jsou tři a všechny jsou v přístupnosti a chování, ne ve vzhledu: chybí lapač fokusu, chybí zámek scrollu a plocha pod modálem není pro čtečky vypnutá. Vedle toho existuje jeden ručně postavený modál (GlobalSearch) a formulářový drawer neumí říct, PROČ uložení selhalo.
1. Nálezy per podkomponenta (ověřeno v kódu)
1.1 Dialog (packages/ui/src/components/Dialog.tsx)
- Velikosti
s400 /m640 /lfluid (strop 1200). Starší číselnéwidthje označené jako deprecated — a legacy storyDialogStoryho pořád používá (width={480}). - Fokus: při otevření se přesune do panelu (první
[autofocus]/pole, jinak panel). Lapač fokusu ale neexistuje — Tab z panelu odejde do stránky za scrimem. - Zámek scrollu neexistuje (
document.body.style.overflowse nikde nesahá): stránka pod modálem jde rolovat kolečkem i klávesnicí. - Panel se neportáluje (jen lišta doku). Hostitel s
transform/filter/containtedy může posunoutfixedpanel jinam, než kam patří. - Esc řeší sdílený zásobník
escStack— správně: vyhraje nejvyšší okno, modeless okno klávesu spolkne, aby nezavřelo drawer pod sebou.
1.2 Confirm (Confirm.tsx)
useConfirm()vrací příslib — správný tvar API, 53 volajících souborů.- Potvrzovací tlačítko není autofokusované (u destruktivní otázky správně) a nemá klávesovou zkratku.
- Texty tlačítek dodává hostitel (
labels); komponenta nemá fallback — appka, která je zapomene, vykreslí prázdná tlačítka.
1.3 Window states (Dialog.tsx + lib/dialog-dock.ts + ds/dialog-window.css)
- Kontrakt „přepnutí stavu obsah nezruší" je dodržený i vysvětlený (wrapper se mění na
display: contents,minpanel jen skryje). Dok je jeden kontejner v<body>, počítaný referencemi, publikuje počet lišt jako--acrm-dialog-dock-n. - Žádná story to dosud neukazovala, přestože na to existuje test (
apps/web/src/components/__tests__/dialog-window-states.test.tsx). - Změna stavu není nijak oznámena (žádná live region), minimalizace fokus zahodí.
1.4 Drawer form (DrawerForm.tsx)
- Sdílí plášť
.pdrs Record drawerem a nepřidává žádné CSS — čistý vztah. errorje boolean s jednou natvrdo napsanou českou větou; komponenta neumí říct, co se pokazilo, a chybová věta není live region ani navázaná na pole.- Nemá Esc ani lapač fokusu, ale hlásí se jako
role="dialog".
1.5 Record drawer (components/drawer/RecordDrawer.tsx, ds/record-drawer.css)
- Vědomě NENÍ dialog (bez scrimu, bez
role="dialog") — správně, výpis vedle zůstává živý. - Žádná story neexistovala, přestože je to jeden z nejpoužívanějších plášťů (15 souborů).
.pdr-rail/.pdr-sec/.pdr-rowpatří jen sem; Drawer form je konzument jen vnějšího pláště.
2. Bug list — DS (web, registr, stories, docs)
| # | Nález | Stav |
|---|---|---|
| D1 | Rodina Dialog neměla per-sekci stories (jen tři legacy přehledy) | OPRAVENO (5 souborů, 23 stories) |
| D2 | Žádná ukázka compact/min/tall/size="l"/subject/headerActions/DrawerForm error | OPRAVENO |
| D3 | Chyběly usage.md a a11y.md pro všech pět podkomponent | OPRAVENO (10 souborů) |
| D4 | Katalog Ladle nenačítal dialog-window.css → stavy okna nešlo ukázat | OPRAVENO (provider) |
| D5 | Legacy story DialogStory používá deprecated width={480} místo size | NEOPRAVENO — má vizuální baseline; oprava = změna baseline (rozhodnutí D-A5) |
| D6 | Registr popisoval Record drawer jen jako „chrome"; neřekl, že React je v appce a co je čí grammar | OPRAVENO (summary + notes) |
3. Bug list — aplikace (kanon v kódu)
| # | Nález | Návrh | Kdo rozhodne | |
|---|---|---|---|---|
| A1 | Modál nemá lapač fokusu (Tab uteče za scrim) | doplnit do Dialog.tsx (cyklus Tab uvnitř panelu) | AD ano/ne, FE provede | |
| A2 | Není zámek scrollu — stránka pod modálem roluje | zamknout body po dobu modálu | AD ano/ne | |
| A3 | Plocha pod modálem není inert/aria-hidden | doplnit spolu s A1 | AD ano/ne | |
| A4 | GlobalSearch.tsx staví .acrm-dialog-scrim + .acrm-dialog ručně | buď rozšířit Dialog o to, co paletě chybí, nebo výjimku zapsat do DS jako vědomou | AD | |
| A5 | Legacy DialogStory na deprecated width | přepsat na size="m" a přefotit baseline | AD (mění baseline) | |
| A6 | DrawerForm.error neumí zprávu | změnit na `error?: string \ | boolean` | AD ano/ne |
| A7 | Panel dialogu se neportáluje | portálovat do <body> jako Popover | AD (riziko regrese, samostatný MR) |
4. Rozhodnutí pro AD
- G1 — Dialog, Confirm, Window states, Drawer form, Record drawer jako kanon. Schválit rodinu podle ukázek na webu. Doporučuji ano.
- G2 — přístupnost modálu (A1+A2+A3 jako jeden balík). Lapač fokusu, zámek scrollu a
inertpozadí. Doporučuji ano — jsou to tři vady jednoho typu a opravují se společně; vizuálně se nic nemění, takže to nepotřebuje tvoje oko, jen souhlas. - G3 —
GlobalSearch(A4). Doporučuji rozšířit Dialog a paletu na něj převést; pokud to vyjde draho, zapsat výjimku do DS, ať to nikdo nekopíruje jako vzor. - G4 —
DrawerForm.errorse zprávou (A6). Doporučuji ano, jinak drawer nikdy neřekne, co se pokazilo. - G5 — legacy story na
width(A5). Doporučuji přepsat při nejbližší vlně baseline, ne teď — každá změna baseline musí být vidět samostatně.
5. Co teď
- MR do acrm
polish/ds-overlays-stories(Dialog + Popover dohromady, jedna kategorie) se zámkemno-auto-merge. - DS repo: registr, docs, tenhle review.
- Po schválení rodiny:
approvedAt, pak G2 jako samostatný MR (chování, ne vzhled).
Source: docs/REVIEW-dialog-family.md · GitLab