- Introduced PickerHelp component for contextual help in pickers. - Created PickerBrowserFixture for testing picker components in a browser-like environment. - Implemented a comprehensive regression test suite for picker UI using Playwright. - Added support for localization in picker components and tests.
19 KiB
L02.1 — Selectores tabulares, capacidad móvil y accesibilidad
Fecha: 22 de octubre de 2026. Implementación derivada de L01, limitada a selectores, identidad mínima y microcopy. No implementa L02.2 ni V05/V07/V08/V12.
1. Resumen de la implementación
Los selectores locales siguen utilizando FormDialog, FormBody y EntityTable. Se corrigió su cadena flex y se compuso una barra de herramientas y paginación apropiadas para el espacio de selección. Los listados generales conservan su presentación. La altura exterior, selección provisional, filtros, ordenación por cabecera y operaciones de confirmar/cancelar se mantienen.
La ayuda de campos utiliza PickerHelp, compuesto con Popover de Base UI/shadcn: un botón junto al pie permite consultar sección de destino e instrucciones completas, también con teclado. No hay focus trap ni listener Escape nuevos.
2. Hallazgos corregidos y alcance
| Hallazgo | Resultado y límites |
|---|---|
| L01-01 | Corregido el colapso de los selectores tabulares comprobados: campos de activos/recursos, opciones de perfil y activos del puesto. Los selectores remotos se verifican por separado. No se declara corregida la densidad inicial de listados generales señalada bajo el mismo ID en L01. |
| L01-02 | Selección e identidad protegidas en OptionPickerList; nombre protegido también en DashboardDetailsDialog y AssetRelatedInformation. No se hacen obligatorias todas las columnas. |
| L01-04 | AssetSelect en presentación de acción conserva como nombre accesible su contenido visible; la presentación de campo incluye etiqueta y valor. Rangos de selectores usan nombres de entidades y pluralización es/en según el número filtrado. |
Las medidas y reproducciones originales de L01 se conservan. V05, V07, V08 y V12 permanecen pendientes; el selector de columnas sigue sin búsqueda/grupos/restauración. No se modifica el estado de V01/V02/V03/V06/V09 ni otros hallazgos.
3. Causa técnica del colapso
La reproducción previa mediante MCP confirmó en 320×640:
| Nivel | Distribución efectiva antes del cambio |
|---|---|
| DialogContent | Flex columna; altura exterior 608 px, máximo 608; overflow hidden. |
| Contenido del diálogo | Flex columna, grow/shrink 1, min-height 0; alto 509 px y overflow hidden. |
| FormBody | Flex columna, grow/shrink 1, min-height 0, overflow auto; clientHeight 332 y scrollHeight 604. |
| EntityTable fill | Flex columna, grow 1, shrink 0 impuesto por el padre, mínimo 384 px; clientHeight 382, scrollHeight 387. |
| TableContainer | Grow/shrink 1, min-height 0, overflow auto; clientHeight 0, scrollHeight 950. |
| Pie de acciones | Alto aproximado 157 px. |
El mínimo de tabla no garantizaba filas: sus bandas no reducibles lo consumían. La ayuda y destino añadían altura al cuerpo, pero no eran la única causa. Desplazar FormBody no recuperaba el contenedor colapsado. before-results.json conserva display, dirección, grow/shrink, mínimos/máximos, overflow y geometría de cada ancestro.
4. Cambios de composición y presupuesto
- FormBody en contexto picker deja de imponer shrink 0 a todos sus hijos; conserva min-height 0, margen para foco y scroll exterior de contingencia. No cambia los modos content/stable ni su
contentLayout="fill". - OptionPickerList, AssetPickerList y el wrapper Tabs de AssetList propagan min-height 0. La tabla se encarga de su presupuesto, en lugar de sumar mínimos independientes en cada wrapper.
- EntityTable
layout="fill"agrupa búsqueda y configuración en una barra. En contenedores inferiores a 32 rem, el selector de orden redundante se omite; las cabeceras continúan permitiendo ordenar. Columnas permanece accesible. El listado generallayout="content"conserva las bandas previas. - El picker muestra un único rango en el pie, sin duplicarlo arriba. La paginación incluye tamaño de página etiquetado, anterior/siguiente y página actual/total. No se elimina la navegación de páginas ni se repagina una respuesta remota.
- Mínimo de tabla local: 18 rem, presupuesto para herramientas compactas, cabecera y opciones. No es altura fija de filas ni reducción de tipografía/controles. Las filas siguen creciendo por su contenido.
- FormActions en contexto picker distribuye Cancelar/Confirmar en la misma fila cuando caben; el alta relacionada conserva una fila propia en móvil. Cabecera y pie siguen fuera del scroll de resultados.
- PickerHelp conserva instrucciones completas y destino fuera del flujo que reparte las filas. Abrirlo no altera la altura exterior; su contenido extenso tiene desplazamiento limitado al espacio disponible.
En las ventanas ordinarias probadas solo los resultados necesitan scroll vertical. En ventanas de altura insuficiente o texto ampliado, el mínimo operativo permite scroll adicional de FormBody para alcanzar herramientas/resultados; overscroll-contain evita encadenar el gesto hacia el otro scroll. Este fallback es deliberado, no una promesa de dos filas en toda altura o longitud de texto.
5. Política de identidad mínima
hideable:false expresa la política existente. En OptionPickerList se protegen selection y name; description sigue siendo configurable. FieldPickerList y AssetPickerList ya protegían selección/nombre. La revisión de consumidores detectó además nombre ocultable en el resumen de compañías del dashboard y en los perfiles asociados a un activo: ambos se corrigen con la misma propiedad.
El hook de tabla ya impone las columnas protegidas sobre una preferencia false; no se cambia el formato de almacenamiento. IDs desconocidos no crean columnas ni rompen el render. Las preferencias de otros listados no se borran y los selectores sin clave de persistencia siguen sin ella. Las pruebas existentes de persistencia se amplían con un ID desconocido y las pruebas MCP comprueban que el checkbox de identidad está deshabilitado.
6. Microcopy y traducciones
- AssetSelect de acción deja de sobrescribir el texto visible con
aria-label={label}. «Seleccionar activos» se reconoce por su propio contenido; el signo + es decorativo. No se sustituye globalmente «Añadir» por «Seleccionar». - La presentación de campo mantiene contexto y valor en el nombre accesible, por ejemplo «Ordenador principal: Equipo 2».
EntityTable.entityLabeladmite la cadena anterior o una función del número filtrado. Es una capacidad presentacional, sin DTO ni conocimiento de entidades.OptionSelectionField/OptionPickerDialog/OptionPickerList recibenlabels.entity(count), independiente del título de acción. El módulo de perfiles aporta puestos/personas/cuentas y sus formas es/en.- Campos y activos aportan también singular/plural mediante i18n. El rango compacto es «1–1 de 1 puesto» / «1–1 of 1 workstation»; las pruebas distinguen entidad y acción. El formato general «Mostrando…» no cambia.
- La ayuda añade «Sección y ayuda» / «Section and help». No se introducen textos de negocio literales en los componentes genéricos.
7. Componentes y consumidores afectados
| Área | Archivos/responsabilidad |
|---|---|
| Composición propia compartida | FormDialog/FormBody/FormActions, EntityTable, EntityTableConfiguration y EntityListPagination. Defaults de listados generales conservados; presentación picker explícita en controles internos. |
| Selección y ayuda | FieldPickerDialog/List, OptionSelectionField, OptionPickerDialog/List y nuevo PickerHelp. |
| Activos | AssetSelect, AssetPickerList, AssetList, selectores de campos de plantilla y traducciones. |
| Recursos | Adaptadores de FieldPickerDialog/List y traducciones de campos. |
| Compañías | Etiquetas de selección de perfiles, identidad de dashboard y sublistado de perfiles del activo. |
| Pruebas | Tests SSR de selección/visibilidad/AssetSelect, scripts/picker-ui.test.py y picker-browser-fixture.tsx, importada únicamente por Vite desde el guion de navegador. |
No se cambian vendor, rutas, consultas HTTP, Sequelize, autenticación, permisos, paleta, fuentes ni arquitectura V01. La fixture React no es una ruta ni un consumidor de producción; solo monta componentes reales con datos temporales para medir geometría.
8. Mediciones antes y después
| Escenario 320×640 | Antes | Después |
|---|---|---|
| Campos de plantilla de activo | Modal 608 px; filas clientHeight 0. | Misma altura exterior; filas clientHeight 184 px con el catálogo real sintético y tres acciones. |
| Campos de plantilla de recurso | Misma composición susceptible de colapso. | Filas clientHeight 184 px en el recorrido real. No se atribuye una medida anterior no tomada a este consumidor. |
| Fixture de campos, 0/1/2 opciones cortas | No forma parte de L01. | Filas clientHeight 256 px; se ven 0/1/2 opciones completas respectivamente. Con doce opciones cortas, la muestra de campos ofrece 228 px y tres filas completas; la de opciones, 324 px y cinco. |
| Activos desde puesto | No se atribuye aquí una medida anterior no tomada. | Contenedor 212 px con doce activos y dos opciones completas; la primera deshabilitada conserva su explicación. Se verifican seis resoluciones, cancelación y confirmación. La separación entre pestañas y tabla se ajusta solo en selectores estrechos. |
| Nombres/descripciones extensos | L01 mostraba filas en DOM sin superficie útil. | Contenido íntegro y scroll utilizable; una opción puede superar por sí sola la ventana de resultados. No se ocultan descripciones para alcanzar una cifra. |
Objetivo de dos filas: se verifica con opciones cortas en los seis tamaños solicitados. En el catálogo real, las dos primeras opciones necesitan aproximadamente 206 px más 40 px de cabecera; el contenedor de 184 px deja una completa y parte de la segunda. Se adopta ese presupuesto como alternativa fundamentada: conserva tres acciones, contador, búsqueda, selección y ayuda, y permite recorrer íntegramente las opciones. No se declara cumplido un mínimo universal de dos opciones de longitud arbitraria.
Los JSON diferencian altura del modal, clientHeight/scrollHeight del scroller efectivo, clipping por ancestros, filas completas, foco, pie y overflow global. El selector remoto utiliza su contenedor real, no el wrapper horizontal de Table como sustituto de la superficie visible.
9. Pruebas funcionales
La suite MCP usa unittest y el cliente stdio existente de scripts/visual-audit.py, sin framework nuevo. Valida 0/1/2/12 opciones, resize con diálogo abierto, cero coincidencias, identidad, selección por radio/checkbox y fila, opciones deshabilitadas, paginación, confirmación y cancelación provisional.
Los flujos reales recorren plantilla de activo y de recurso, activos desde nuevo puesto, puesto desde nuevo perfil y selector de dominio. Cuando faltan sedes/puestos, el guion sustituye únicamente GET con relaciones sintéticas coherentes: una sede, doce activos y un puesto con ordenador y miembros consistentes. No se guardan formularios ni se realizan altas de negocio durante estos recorridos.
Los datos del catálogo y las relaciones de dominio proceden de la instancia desechable del preparador existente. La fixture de RemoteOptionPicker prueba su contrato con páginas aportadas por el consumidor; no demuestra por sí sola paginación de servidor ni publicaciones SMB.
10. Pruebas de accesibilidad
Se comprueban foco en búsqueda al abrir, Space en radio, nombres accesibles, columnas protegidas, contador de entidad, Escape, cancelación y retorno a disparador. En plantillas se abre y cancela el alta rápida anidada sin cerrar picker ni formulario padre. La ayuda es un Popover operable por teclado; PageDown desplaza las instrucciones extensas y Escape cierra esa capa conservando el picker.
Las comprobaciones de geometría no se sustituyen por snapshots. Se prueba altura reducida 320×480 y ampliación de texto CSS al 200 %; esta última no equivale al zoom nativo del navegador ni certifica WCAG. Lector de pantalla, contraste exhaustivo y zoom nativo 400 % no se acreditan en esta entrega.
11. Evidencias y reproducción Playwright
Directorio ignorado: .artifacts/playwright/l02-1. Incluye capturas antes/después, snapshots, medidas por ejecución (measurements*.json), consola y resultados técnicos. evidence-index.json relaciona archivos, dimensiones y SHA-256. Las capturas relevantes se abren para inspección visual antes del cierre.
La sesión de L01 se reutilizó para las reproducciones previas, pero caducó durante la instrumentación. Se comprobó la redirección al acceso y se preparó una instancia nueva mediante scripts/visual-audit.py; no se recuperaron credenciales ni se cambió seguridad. Las pruebas permanentes validan que la sesión proporcionada siga autorizada antes de navegar.
En otro entorno, seguir Playwright MCP, mantener el preparador abierto y utilizar su ruta privada de sesión:
PYTHONDONTWRITEBYTECODE=1 METIS_PLAYWRIGHT_STORAGE_STATE=.artifacts/playwright/authenticated-ID/session.json \
python3 scripts/picker-ui.test.py
No copiar sesiones ni credenciales a Git. El script no inicia servicios ni escribe entidades. Al cierre de L02.1 se retira únicamente la instancia visual nueva que se preparó para estas pruebas; la sesión caducada y las instalaciones previas no se modifican. Requiere Vite de desarrollo para importar la fixture de componentes. Las regresiones de asistentes/foco/editor de recursos utilizan scripts/visual-audit-ui.test.py con una carpeta de evidencia separada.
12. Resultados técnicos y regresiones
Los logs distinguen comprobaciones dirigidas, globales y guiones de navegador. Los intentos iniciales con importación incorrecta, sesión caducada o localizador que confundía el Popover con el diálogo se conservan y no se presentan como defectos de Metis ni pruebas correctas.
Los fallos globales anteriores se contrastan con V01.2 y V01.4: calendar del vendor en typecheck, reglas/formato ajenos en lint/check y dos expectativas antiguas de compañías en test. No se modifica ese código para conseguir un resultado global verde. La ejecución global de tests utiliza MariaDB nueva y desechable, exclusivamente metis_desk_test; no utiliza ni limpia la instancia visual o bases existentes.
Los resultados finales por comando y la regresión de navegador se registran en la tabla de cierre siguiente; un comando global fallido no se sustituye por el resultado de una prueba dirigida.
| Comprobación ejecutada | Resultado final |
|---|---|
pnpm --filter @repo/rdx-ui test |
Correcto: 11 archivos de prueba. |
pnpm --filter @metis/assets test |
Correcto: 27 archivos. Test de AssetSelect repetido tras completar su fixture tipada. |
pnpm --filter @metis/resources test |
Correcto: 11 archivos. |
| Tests dirigidos de perfiles y ficha de puesto | Correctos: dos archivos, incluida pluralización real de puestos/personas/cuentas es/en. |
| Typecheck dirigido de rdx-ui, activos, recursos y compañías | Correcto en los cuatro paquetes. |
| Typecheck de web | Correcto. |
| Biome dirigido | Correcto: 34 archivos propios TS/TSX/JSON, sin errores ni avisos. |
| ESLint de espaciado dirigido | Correcto sobre los archivos propios TS/TSX; se corrigieron tres separaciones entre sentencias sin cambios de comportamiento. |
pnpm typecheck |
Falla en vendor calendar.tsx:165, locale y exactOptionalPropertyTypes, ya documentado. Vendor intacto. |
pnpm lint |
Falla: 15 errores, un aviso y cuatro informaciones ajenos, coincidentes con el estado anterior. |
pnpm check |
Falla: 168 errores, un aviso y cuatro informaciones globales previos. Los archivos propios se comprueban separadamente. |
pnpm test, MariaDB aislada |
Falla en compañías: expectativas antiguas «Personas (1)» y «Esta persona no tiene acceso a Metis Desk»; 7 tareas correctas de 8. No acredita las suites que Turbo interrumpe. El contenedor exclusivo se retira en finally. |
pnpm build |
Correcto, dos tareas. Persiste el aviso de chunk web superior a 500 kB. |
python3 scripts/picker-ui.test.py |
Ocho pruebas correctas, 183,081 s en la ejecución integrada. Incluye seis tamaños, casos 0/1/2/12, catálogos, acciones, selección/foco y remoto. |
| Repetición ayuda/texto ampliado | Dos pruebas correctas; prueba de foco ampliado repetida con medición conservada por ejecución. Checkbox visible entre y=378–410 dentro de cuerpo y=250–538. |
| Aceptación adicional de activos/ayuda | Dos pruebas correctas, 54,319 s: dos opciones completas a 320×640 y PageDown de ayuda (scroll 336 px con foco dentro). |
Regresión visual-audit-ui.test.py |
Tres pruebas correctas, 162,850 s: geometría de asistentes de activo/recurso, retorno de foco y editor anidado de recursos. |
| Consola de las ejecuciones finales | Cero errores y cero advertencias. Ninguna escritura HTTP de negocio intentada en los recorridos. |
Se corrigieron errores del propio test (fixture múltiple sin maxSelection, inferencia de tuplas, localizador de output inerte y recuento de padres ocultos para accesibilidad). No eran fallos globales previos ni se dejaron supresiones. Las reproducciones finales con tipos estrictos y localizadores adecuados pasan.
13. Limitaciones y alcance pendiente
- Dos filas completas son un objetivo para opciones ordinarias, no para texto arbitrariamente largo. Se conserva todo el contenido con scroll, incluidos los catálogos reales de la muestra.
- La compactación de herramientas se limita a selectores. La altura inicial de datos en listados generales permanece pendiente bajo L01-01.
- Los cambios de identidad de dashboard y perfiles del activo se verifican en código y por el contrato común de visibilidad; no implican una auditoría completa de esas fichas.
- No se prueba escritura de altas rápidas ni gestión de publicaciones SMB; sí apertura/cancelación y las páginas aportadas al selector remoto.
- No hay certificación WCAG, prueba de lector de pantalla ni rendimiento de producción. Tampoco se declara resuelto V04 como proyecto independiente.
Skills: principal ux-inputs-and-forms; complementarias ux-tables, shadcn, ux-navigation y ux-microcopy. Se aplican a reservar superficie operativa, preservar borrador/confirmación y foco, reutilizar Popover/Dialog de Base UI y separar entidad de acción. vercel-composition-patterns orienta variantes genéricas sin flags por entidad; vercel-react-best-practices se consulta para estado derivado y fixtures sin efectos de sincronización. Las guías genéricas no autorizan cambiar tokens/vendor. Los auditores especializados ausentes no se presentan como ejecutados.
14. Preparación de L02.2
Quedan disponibles la superficie de resultados operativa, identidad protegida y entityLabel(count) traducible. L02.2 deberá tener encargo propio: V08 sigue pendiente de búsqueda/grupos/restauración de columnas y V12 de reserva de estructura durante carga inicial. V05/V07 mantienen su entrega independiente de contexto/búsqueda. No se adelantan esas implementaciones en L02.1.