Files
suaveplan/openspec/changes/enterprise-react-modal/proposal.md
2026-05-27 23:29:53 +01:00

2.7 KiB

OpenSpec Change: enterprise-react-modal

  • Change ID: enterprise-react-modal
  • Package: @suaveplan/react-modal
  • Category: react
  • Target path: react/ui/react-modal
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/react-modal.md

Summary

Deliver @suaveplan/react-modal on genesis from scratch at enterprise quality. A focused React modal/dialog system: base Modal (portal, focus-trap, ESC-close, focus-restoration), Dialog (title/actions slots), ConfirmDialog (confirm/cancel preset), and ModalProvider/ModalContext for stacked modals by ID. Every component uses @suaveplan/react-translate for i18n. Genesis adds Playwright real-browser focus-trap + scroll-lock tests, @suaveplan/error-backed ModalError, and OTel span events.


Current-state audit (genesis)

Package does not exist on genesis — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) ModalError
@suaveplan/react-translate (workspace:^) i18n default copy
zod (catalog:forms) Props schemas
@opentelemetry/api (catalog:observability) modal.open / modal.close span

External: tslib catalog:build. React 18 peer.


Preserved public API surface

Modal            — portal + focus-trap + ESC close + scroll-lock + focus-restore
Dialog           — title / description / actions slots on top of Modal
ConfirmDialog    — confirm / cancel preset with onConfirm / onCancel callbacks
ModalContext     — open(id, element) / close(id) / closeAll() / isOpen(id)
ModalProvider    — stacked modal management

// Types
ModalProps, DialogProps, ConfirmDialogProps, ModalContextValue, ModalProviderProps

New additions for genesis

  1. ModalError with code MODAL_CONTEXT_MISSING (thrown when hooks used outside provider).
  2. OTel modal.open / modal.close span events — attribute modal.id.
  3. Scroll-lock utility (disableBodyScroll / enableBodyScroll) tested in isolation.
  4. Playwright E2E — focus-trap cycling, ESC close, scroll-lock verification, stacked modals (open two, close inner, outer still open).

File layout

react/ui/react-modal/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    modal/ (tsx + types + md + test + stories + index)
    dialog/ (tsx + types + md + test + stories + index)
    confirm-dialog/ (tsx + types + md + test + stories + index)
    modal-context/ (tsx + types + md + test + stories + index)
    utils/
      scroll-lock/ (ts + md + test + index)
      focus-trap/ (ts + md + test + index)
  e2e/
    modal.spec.ts