188 lines
7.9 KiB
Markdown
188 lines
7.9 KiB
Markdown
# Patrones de UI
|
|
|
|
> **Propósito:** explicar cómo implementar pantallas concretas. Este documento, junto con [Arquitectura frontend](./frontend-architecture.md) y [UI system](./ui-system.md), forma la fuente de verdad (SSOT) de frontend/UI.
|
|
|
|
Los componentes aquí citados se definen en `ui-system.md`; este documento explica cómo componerlos.
|
|
|
|
## 1. Listados
|
|
|
|
```tsx
|
|
<RdxPageFrame>
|
|
<RdxPageContainer width="full">
|
|
<RdxPageContent>
|
|
<RdxPageHeader
|
|
title={title}
|
|
description={description}
|
|
actions={<RdxPageActions primary={primaryAction} />}
|
|
/>
|
|
{refreshError}
|
|
<DataTable topContent={toolbarAndFilters} {...tableProps} />
|
|
</RdxPageContent>
|
|
</RdxPageContainer>
|
|
</RdxPageFrame>
|
|
```
|
|
|
|
- El CTA de creación permanece durante loading, empty y errores de refresh; puede desaparecer ante error inicial bloqueante.
|
|
- Búsqueda, filtros y controles viven en `DataTable.topContent`.
|
|
- TanStack Query conserva los datos remotos y la paginación server-side.
|
|
- Búsqueda, filtros, sorting y paginación reflejan su estado relevante en `searchParams` y reinician página cuando corresponda.
|
|
- No se calculan contadores globales a partir de una página parcial.
|
|
- El click de fila recibe la fila; la página/controller usa explícitamente `row.id`.
|
|
- Una vista de composición de dominio puede encapsular el shell si permanece pura.
|
|
|
|
El controller de página concentra URL, filtros, consulta, estados y navegación. La Page define columnas y conecta callbacks; no reproduce reglas de dominio.
|
|
|
|
## 2. Create / Update
|
|
|
|
```tsx
|
|
<RdxPageFrame
|
|
header={<RdxEditorHeader />}
|
|
footer={<RdxEditorActionBar />}
|
|
>
|
|
<RdxPageContainer width="content">
|
|
<RdxPageContent>
|
|
{pageError ? <ErrorAlert /> : null}
|
|
<Editor />
|
|
</RdxPageContent>
|
|
</RdxPageContainer>
|
|
</RdxPageFrame>
|
|
```
|
|
|
|
Los errores inline no definen su propio spacing estructural. Participan del spacing estándar de RdxPageContent igual que el resto de hijos principales.
|
|
|
|
Footer y aside son opcionales. `content` es adecuado para formularios focalizados; `full` para editores documentales amplios. Create y Update deben compartir geometría cuando solo cambian defaults, submit o navegación.
|
|
|
|
Loading y error bloqueante usan también `RdxPageFrame` y `RdxPageContainer`; no reconstruyen el viewport.
|
|
|
|
## 3. Detail
|
|
|
|
Un detalle normal usa `RdxPageFrame`, `RdxEditorHeader` cuando es fullscreen y el container apropiado.
|
|
|
|
Un detalle documental especializado puede usar:
|
|
|
|
```text
|
|
RdxPageFrame content overflow-hidden
|
|
├── document pane → scroll interno
|
|
└── inspector → scroll interno
|
|
```
|
|
|
|
Solo se usa esta excepción cuando los panes necesitan permanecer visibles simultáneamente. No se crea un layout genérico de detalle si no existe repetición real.
|
|
|
|
## 4. Settings
|
|
|
|
Settings posee un layout funcional bajo un único frame:
|
|
|
|
```text
|
|
RdxPageFrame
|
|
└── SettingsLayout
|
|
├── header
|
|
├── navigation
|
|
└── Outlet
|
|
```
|
|
|
|
Las páginas hijas no añaden otro `RdxPageFrame` cuando el padre ya controla shell y scroll. El layout de Settings se conserva porque aporta navegación y composición funcional.
|
|
|
|
## 5. Dialogs
|
|
|
|
Usar `Dialog` para una tarea breve y focalizada que debe volver al contexto actual. Usar `AlertDialog` para confirmaciones, especialmente destructivas. Una edición prolongada o documental necesita una ruta propia.
|
|
|
|
Orden recomendado:
|
|
|
|
```text
|
|
DialogContent
|
|
├── DialogHeader: título y descripción
|
|
├── campo de identidad, si existe
|
|
├── secciones agrupadas por intención
|
|
└── DialogFooter: cancelar y enviar
|
|
```
|
|
|
|
- La cabecera y el footer se separan visualmente del formulario.
|
|
- Los bloques usan `section` y `aria-labelledby` cuando tienen título.
|
|
- Campos equivalentes pueden usar dos columnas desde `sm`; en móvil se apilan.
|
|
- El botón submit puede apuntar al formulario mediante `form` si el footer está fuera de `<form>`.
|
|
- Durante submit se deshabilitan controles y acciones de cierre que puedan interrumpir la operación.
|
|
- El controller posee defaults, validación, mutation, reset, feedback y navegación posterior.
|
|
- Todo texto visible usa i18n en `es` y `en`.
|
|
|
|
## 6. Acciones de fila
|
|
|
|
`RdxRowActions` recibe acciones ya resueltas:
|
|
|
|
- los listados usan `RdxRowActions` para mantener una presentación uniforme;
|
|
- las frecuentes van en `primaryActions` como botones de icono con tooltip; no se usan botones de texto en la columna de acciones;
|
|
- las secundarias y destructivas van en `overflowActions`, accesibles desde el menú de más acciones;
|
|
- orden, `disabled`, `disabledReason`, permisos y callbacks pertenecen al consumidor;
|
|
- una acción deshabilitada puede seguir visible para explicar por qué no está disponible.
|
|
|
|
No se trasladan entidades, navegación, dialogs ni permisos a `rdx-ui`.
|
|
|
|
## 7. Loading, error y empty
|
|
|
|
| Estado | Patrón |
|
|
| --- | --- |
|
|
| Loading inicial de tabla | `DataTable loading` dentro del shell estable |
|
|
| Loading de editor | skeleton con el mismo frame/container que success |
|
|
| Error inicial bloqueante | `PageErrorState` dentro de frame y container |
|
|
| Error de refresh | `ErrorAlert` inline; mantiene datos y controles |
|
|
| Empty | mensaje/estado vacío en el stack del contenido |
|
|
|
|
No se crean sistemas alternativos de errores o skeletons cuando los componentes existentes cubren el caso.
|
|
|
|
## 8. Formularios
|
|
|
|
- React Hook Form y `FormProvider` pertenecen al consumidor.
|
|
- `RdxFormGrid` organiza únicamente campos.
|
|
- Errores de campo permanecen junto al control; errores de operación usan `ErrorAlert`.
|
|
- Dirty state, bloqueo de navegación y submit pertenecen al controller/provider correspondiente.
|
|
- La acción principal comunica loading y evita doble submit.
|
|
- Una única superficie editable es preferible a cards anidadas.
|
|
|
|
## 9. Main + aside
|
|
|
|
El aside es opcional y contiene metadata, resumen o contexto secundario. En desktop acompaña al main y en pantallas menores se apila debajo.
|
|
|
|
No usar el aside para equilibrar visualmente columnas, trasladar totales o fragmentar arbitrariamente el flujo del formulario. Tablas y line editors mantienen su layout especializado dentro del main.
|
|
|
|
## 10. Responsive
|
|
|
|
- Validar móvil, tablet y desktop.
|
|
- Evitar widths fijos en campos y controles localizados.
|
|
- Mantener acciones utilizables cuando se apilan.
|
|
- Confirmar `min-w-0`, truncado intencional y ausencia de overflow horizontal.
|
|
- En layouts especializados, comprobar pane sizes y scroll independiente.
|
|
|
|
## 11. Accesibilidad
|
|
|
|
- Controles nativos o primitives accesibles; nunca `div` clicables.
|
|
- Labels asociadas mediante `htmlFor`/`id`.
|
|
- Icon buttons con `aria-label` y tooltips cuando corresponda.
|
|
- Activación por teclado en filas y acciones.
|
|
- Foco visible, orden DOM lógico y errores anunciables.
|
|
- El color no sustituye texto, icono o estado semántico.
|
|
|
|
## 12. Testing
|
|
|
|
Usar tests de comportamiento con el runner vigente del workspace y Testing Library cuando existe interacción DOM. No probar detalles internos del controller ni clases visuales incidentales.
|
|
|
|
Cobertura mínima según patrón:
|
|
|
|
- listado: CTA, search, filtros, paginación, estados y navegación de fila;
|
|
- editor: header, contenido, acciones, dirty/submit y loading/error;
|
|
- dialog: título, campos, condicionales, cancelar/enviar y mutation;
|
|
- UI pública: contrato observable, accesibilidad y responsabilidades estructurales.
|
|
|
|
Las traducciones productivas deben probarse sin sustituirlas por un diccionario que oculte claves ausentes.
|
|
|
|
## 13. Checklist
|
|
|
|
- [ ] La ruta usa el shell real declarado en `handle.layout`.
|
|
- [ ] Existe un único `RdxPageFrame` propietario del scroll.
|
|
- [ ] Header editorial o fullscreen elegido por responsabilidad.
|
|
- [ ] Width y padding pertenecen a `RdxPageContainer`.
|
|
- [ ] El aside es funcional y opcional.
|
|
- [ ] No hay viewport o scroll principal manual en la Page.
|
|
- [ ] Loading, error, empty y success conservan estructura coherente.
|
|
- [ ] Acciones, permisos y navegación permanecen en el consumidor.
|
|
- [ ] Responsive, teclado, foco, i18n y light/dark están validados.
|
|
- [ ] Implementación, documentación, UI Kit y tests siguen sincronizados.
|