From b9f6e971356e961be8f6c7b60f9c181414d6735e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 1 Mar 2026 16:22:24 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Standardize=20CustomC?= =?UTF-8?q?heckbox=20with=20grid=20and=20accessibility=20props?= 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> --- src/components/PlayerCasoContainer.jsx | 1 - src/components/Profile.jsx | 19 ++-- src/components/admin/AnswerForm.jsx | 37 ++++--- src/components/custom/CustomCheckbox.jsx | 98 +++++++++++++++---- src/components/custom/CustomCheckbox.test.jsx | 85 ++++++++++++++++ 5 files changed, 195 insertions(+), 45 deletions(-) create mode 100644 src/components/custom/CustomCheckbox.test.jsx diff --git a/src/components/PlayerCasoContainer.jsx b/src/components/PlayerCasoContainer.jsx index 44ffcf7..e8d138c 100644 --- a/src/components/PlayerCasoContainer.jsx +++ b/src/components/PlayerCasoContainer.jsx @@ -5,7 +5,6 @@ import ExamService from "../services/ExamService"; import { useHistory } from 'react-router-dom'; import { alertError, alertSuccess } from "../services/AlertService"; import CasoContext from "../context/CasoContext"; -import { CustomButton } from "./custom"; import EnarmUtil from "../modules/EnarmUtil"; import ContributionTypeSelector from "./ContributionTypeSelector"; import ContributionsSummary from "./ContributionsSummary"; diff --git a/src/components/Profile.jsx b/src/components/Profile.jsx index 70cda21..642ade9 100644 --- a/src/components/Profile.jsx +++ b/src/components/Profile.jsx @@ -302,14 +302,17 @@ const Profile = () => { {categories.map(cat => { const isSelected = selectedSpecialties.includes(cat.id); return ( -
- toggleSpecialty(cat.id)} - /> -
+ toggleSpecialty(cat.id)} + s={12} + m={6} + l={4} + style={{ marginBottom: '1rem' }} + /> ); })} diff --git a/src/components/admin/AnswerForm.jsx b/src/components/admin/AnswerForm.jsx index faa8cce..e99ff50 100644 --- a/src/components/admin/AnswerForm.jsx +++ b/src/components/admin/AnswerForm.jsx @@ -25,25 +25,24 @@ const AnswerForm = ({ return ( - -
- { - onChangeAnswer( - questionIndex, - answerIndex, - "is_correct", - event - ); - }} - name={answerIsCorrectName} - value="true" - /> -
-
+ { + onChangeAnswer( + questionIndex, + answerIndex, + "is_correct", + event + ); + }} + name={answerIsCorrectName} + value="true" + /> { const inputRef = useRef(null); @@ -26,22 +34,70 @@ const CustomCheckbox = ({ inputClasses += ' indeterminate-checkbox'; } - // The main wrapper is the label for Materialize checkboxes - return ( - + // Construct grid and wrapper classes + const hasGrid = s || m || l || xl || offset || wrapperClassName; + let finalWrapperClasses = wrapperClassName; + if (s) finalWrapperClasses += ` col s${s}`; + if (m) finalWrapperClasses += ` col m${m}`; + if (l) finalWrapperClasses += ` col l${l}`; + if (xl) finalWrapperClasses += ` col xl${xl}`; + if (offset) { + offset.split(' ').forEach(off => { + if (off) finalWrapperClasses += ` offset-${off}`; + }); + } + + const helperTextId = helperText ? `${id}-helper` : undefined; + + const checkboxContent = ( + <> + + {helperText && {helperText}} + ); + + if (hasGrid) { + return ( +
+ {checkboxContent} +
+ ); + } + + return checkboxContent; }; CustomCheckbox.propTypes = { @@ -54,6 +110,14 @@ CustomCheckbox.propTypes = { labelClassName: PropTypes.string, // For the label element indeterminate: PropTypes.bool, value: PropTypes.string, // HTML value attribute for the checkbox + s: PropTypes.number, + m: PropTypes.number, + l: PropTypes.number, + xl: PropTypes.number, + offset: PropTypes.string, + required: PropTypes.bool, + helperText: PropTypes.string, + wrapperClassName: PropTypes.string, }; export default CustomCheckbox; diff --git a/src/components/custom/CustomCheckbox.test.jsx b/src/components/custom/CustomCheckbox.test.jsx new file mode 100644 index 0000000..dfc413d --- /dev/null +++ b/src/components/custom/CustomCheckbox.test.jsx @@ -0,0 +1,85 @@ +import { render, screen } from '@testing-library/react'; +import { describe, test, expect } from 'vitest'; +import CustomCheckbox from './CustomCheckbox'; + +describe('CustomCheckbox', () => { + test('renders correctly with label', () => { + render(); + expect(screen.getByLabelText(/Test Label/)).toBeInTheDocument(); + }); + + test('applies grid classes and style to wrapper div', () => { + const { container } = render( + + ); + + const wrapper = container.firstChild; + expect(wrapper.tagName).toBe('DIV'); + expect(wrapper).toHaveClass('col'); + expect(wrapper).toHaveClass('s12'); + expect(wrapper).toHaveClass('m6'); + expect(wrapper).toHaveClass('offset-s1'); + expect(wrapper).toHaveStyle('margin-bottom: 16px'); + expect(wrapper).toHaveAttribute('data-testid', 'outer-wrapper'); + }); + + test('applies style and custom props to label if not wrapped in div', () => { + const { container } = render( + + ); + const label = container.firstChild; + expect(label.tagName).toBe('LABEL'); + expect(label).toHaveStyle('color: red'); + expect(label).toHaveAttribute('data-custom', 'test'); + }); + + test('renders required indicator and aria-required', () => { + render(); + + const input = screen.getByLabelText(/Required Label/); + expect(input).toHaveAttribute('aria-required', 'true'); + + const asterisk = screen.getByTitle('Obligatorio'); + expect(asterisk).toBeInTheDocument(); + expect(asterisk).toHaveClass('red-text'); + expect(asterisk.textContent).toBe('*'); + }); + + test('renders helper text and links with aria-describedby', () => { + render(); + + const helper = screen.getByText('Be careful'); + expect(helper).toBeInTheDocument(); + expect(helper).toHaveClass('helper-text'); + expect(helper).toHaveAttribute('id', 'help-check-helper'); + + const input = screen.getByLabelText(/Help Label/); + expect(input).toHaveAttribute('aria-describedby', 'help-check-helper'); + }); + + test('does not wrap in div if no grid or wrapperClassName provided', () => { + const { container } = render(); + expect(container.firstChild.tagName).toBe('LABEL'); + }); + + test('wraps in div if wrapperClassName is provided', () => { + const { container } = render( + + ); + expect(container.firstChild.tagName).toBe('DIV'); + expect(container.firstChild).toHaveClass('custom-wrap'); + }); +});