From f19e9a0bfcc2ec74008d8b7b55fe8e48a102577c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:46:23 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Improve=20V2=20naviga?= =?UTF-8?q?tion=20and=20progress=20bar=20accessibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: godie <227743+godie@users.noreply.github.com> --- .Jules/palette.md | 4 ++ src/components/custom/CustomProgressBar.jsx | 14 ++++++- .../custom/CustomProgressBar.test.jsx | 38 +++++++++++++++++++ src/v2/components/V2Navi.jsx | 34 ++++++++++------- src/v2/pages/V2Examen.jsx | 20 +++++++--- src/v2/pages/V2Landing.jsx | 1 - 6 files changed, 89 insertions(+), 22 deletions(-) create mode 100644 src/components/custom/CustomProgressBar.test.jsx diff --git a/.Jules/palette.md b/.Jules/palette.md index ee2d8f0..c0a74ac 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -94,3 +94,7 @@ ## 2025-06-16 - [Functional Matchers for Complex Buttons and Utility-First Layout] **Learning:** React Testing Library's `findByText` can fail on buttons containing Materialize icons due to text node fragmentation and the icon's name being part of the `textContent`. Using a functional matcher that checks for partial content and tag name is more robust. Additionally, strictly adhering to utility classes (e.g., 'center-align') instead of inline styles ensures compliance with repository constraints and theme consistency. **Action:** Always use functional matchers for testing interactive elements with icons and avoid inline styles by leveraging existing CSS utility classes. + +## 2025-06-17 - [Accessible Progress Indicators and V2 Navigation Polish] +**Learning:** For progress indicators to be truly accessible, they must include `role="progressbar"` along with `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` attributes, allowing screen readers to accurately report status. In side-navigation rails, ensuring decorative icons are marked with `aria-hidden="true"` and the active link has `aria-current="page"` significantly improves screen reader navigation. Additionally, maintaining localization consistency (e.g., 'Inicio' vs 'Home') in new UI versions (V2) is essential for a cohesive user experience. +**Action:** Always implement full ARIA attributes for progress components and audit new navigation structures for both accessibility markers and language consistency. diff --git a/src/components/custom/CustomProgressBar.jsx b/src/components/custom/CustomProgressBar.jsx index c2eb1c4..121330d 100644 --- a/src/components/custom/CustomProgressBar.jsx +++ b/src/components/custom/CustomProgressBar.jsx @@ -18,13 +18,22 @@ const CustomProgressBar = ({ wrapperColor = 'grey lighten-3', className = '', style = EMPTY_STYLE, - height + height, + 'aria-label': ariaLabel }) => { const isIndeterminate = progress === undefined || progress === null; const finalStyle = height ? { ...style, height } : style; return ( -
+
{ + it('renders correctly with progress and ARIA attributes', () => { + render(); + + const progressbar = screen.getByRole('progressbar'); + expect(progressbar).toBeInTheDocument(); + expect(progressbar).toHaveAttribute('aria-label', 'Test Progress'); + expect(progressbar).toHaveAttribute('aria-valuenow', '45'); + expect(progressbar).toHaveAttribute('aria-valuemin', '0'); + expect(progressbar).toHaveAttribute('aria-valuemax', '100'); + + const determinatePart = progressbar.querySelector('.determinate'); + expect(determinatePart).toBeInTheDocument(); + expect(determinatePart).toHaveStyle({ width: '45%' }); + }); + + it('renders indeterminate state when progress is null', () => { + render(); + + const progressbar = screen.getByRole('progressbar'); + expect(progressbar).toBeInTheDocument(); + expect(progressbar).not.toHaveAttribute('aria-valuenow'); + + const indeterminatePart = progressbar.querySelector('.indeterminate'); + expect(indeterminatePart).toBeInTheDocument(); + }); + + it('rounds progress for aria-valuenow', () => { + render(); + + const progressbar = screen.getByRole('progressbar'); + expect(progressbar).toHaveAttribute('aria-valuenow', '68'); + }); +}); diff --git a/src/v2/components/V2Navi.jsx b/src/v2/components/V2Navi.jsx index a7bbeb8..9cf000f 100644 --- a/src/v2/components/V2Navi.jsx +++ b/src/v2/components/V2Navi.jsx @@ -5,7 +5,7 @@ const V2Navi = () => { const isActive = (path) => location.pathname === path; const navItems = [ - { label: "Home", icon: "home", path: "/v2/dashboard" }, + { label: "Inicio", icon: "home", path: "/v2/dashboard" }, { label: "Práctica", icon: "medical_services", path: "/v2/practica" }, { label: "Contribuir", icon: "add_circle", path: "/v2/contribuir" }, { label: "Mis Casos", icon: "history", path: "/v2/mis-contribuciones" }, @@ -14,20 +14,28 @@ const V2Navi = () => { ]; return ( -
@@ -112,7 +120,7 @@ const V2Examen = () => { style={{ width: 'auto', padding: '0 32px', height: '56px', borderRadius: '16px' }} onClick={handleSubmit} > - Enviar Respuesta check + Enviar Respuesta
) : ( @@ -122,12 +130,12 @@ const V2Examen = () => { color: isCorrect ? 'var(--md-sys-color-on-primary-container)' : 'var(--md-sys-color-on-error-container)', marginBottom: '24px', display: 'flex', alignItems: 'center', gap: '12px' }}> - {isCorrect ? 'check_circle' : 'error'} + {isCorrect ? '¡Correcto!' : 'Incorrecto'}

- lightbulb + Perla Médica: Explicación

@@ -140,7 +148,7 @@ const V2Examen = () => { style={{ width: 'auto', padding: '0 32px', height: '56px', borderRadius: '16px' }} onClick={() => history.push('/v2/dashboard')} > - Siguiente Caso arrow_forward + Siguiente Caso

diff --git a/src/v2/pages/V2Landing.jsx b/src/v2/pages/V2Landing.jsx index c488068..906510a 100644 --- a/src/v2/pages/V2Landing.jsx +++ b/src/v2/pages/V2Landing.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Link } from 'react-router-dom'; import '../styles/v2-theme.css';