From 4cf7763c164109d3b39747c4ab117963e71258a2 Mon Sep 17 00:00:00 2001 From: Yordan Date: Wed, 1 Oct 2025 10:19:12 +0200 Subject: [PATCH 1/4] fix: update changelog --- .../checkbox-radio-selection-web/CHANGELOG.md | 4 ++++ .../src/components/CheckboxSelection/CheckboxSelection.tsx | 4 ++++ .../src/components/RadioSelection/RadioSelection.tsx | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md b/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md index b0cfd176d8..3622a1c456 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- We added a missing validation alert. + ## [1.0.0] - 2025-08-25 ### Added diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx index 3fa5178d3c..909ef64b09 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx @@ -2,6 +2,7 @@ import classNames from "classnames"; import { createElement, MouseEvent, ReactElement } from "react"; import { MultiSelector, SelectionBaseProps } from "../../helpers/types"; import { CaptionContent } from "../CaptionContent"; +import { ValidationAlert } from "@mendix/widget-plugin-component-kit/Alert"; import { Placeholder } from "../Placeholder"; export function CheckboxSelection({ @@ -18,6 +19,8 @@ export function CheckboxSelection({ const isReadOnly = selector.readOnly; const name = groupName?.value ?? inputId; + const validation = selector.validation; + const handleChange = (optionId: string, checked: boolean): void => { if (!isReadOnly) { const newSelection = checked ? [...currentIds, optionId] : currentIds.filter(id => id !== optionId); @@ -71,6 +74,7 @@ export function CheckboxSelection({ ); })} {options.length === 0 && } + {validation && {validation}} ); } diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx index 2840a13ee1..c11e7b7258 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx @@ -2,6 +2,7 @@ import classNames from "classnames"; import { ChangeEvent, createElement, MouseEvent, ReactElement } from "react"; import { SelectionBaseProps, SingleSelector } from "../../helpers/types"; import { CaptionContent } from "../CaptionContent"; +import { ValidationAlert } from "@mendix/widget-plugin-component-kit/Alert"; import { Placeholder } from "../Placeholder"; import { If } from "@mendix/widget-plugin-component-kit/If"; @@ -24,6 +25,8 @@ export function RadioSelection({ const isReadOnly = selector.readOnly; const name = groupName?.value ?? inputId; + const validation = selector.validation; + const handleChange = (e: ChangeEvent): void => { if (isReadOnly) { return; @@ -89,6 +92,7 @@ export function RadioSelection({ ); })} {options.length === 0 && } + {validation && {validation}} ); } From 5ad1eefa96b5b5573a253e997163a411d729a876 Mon Sep 17 00:00:00 2001 From: Yordan Date: Thu, 2 Oct 2025 11:16:58 +0200 Subject: [PATCH 2/4] fix: add validation error ID to CheckboxSelection and RadioSelection --- .../src/components/CheckboxSelection/CheckboxSelection.tsx | 4 +++- .../src/components/RadioSelection/RadioSelection.tsx | 4 +++- .../checkbox-radio-selection-web/src/helpers/utils.ts | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx index 909ef64b09..b47ffb7da3 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx @@ -1,6 +1,7 @@ import classNames from "classnames"; import { createElement, MouseEvent, ReactElement } from "react"; import { MultiSelector, SelectionBaseProps } from "../../helpers/types"; +import { getValidationErrorId } from "../../helpers/utils"; import { CaptionContent } from "../CaptionContent"; import { ValidationAlert } from "@mendix/widget-plugin-component-kit/Alert"; import { Placeholder } from "../Placeholder"; @@ -20,6 +21,7 @@ export function CheckboxSelection({ const name = groupName?.value ?? inputId; const validation = selector.validation; + const errorId = getValidationErrorId(inputId); const handleChange = (optionId: string, checked: boolean): void => { if (!isReadOnly) { @@ -74,7 +76,7 @@ export function CheckboxSelection({ ); })} {options.length === 0 && } - {validation && {validation}} + {validation && {validation}} ); } diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx index c11e7b7258..e952fd3b19 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx @@ -1,6 +1,7 @@ import classNames from "classnames"; import { ChangeEvent, createElement, MouseEvent, ReactElement } from "react"; import { SelectionBaseProps, SingleSelector } from "../../helpers/types"; +import { getValidationErrorId } from "../../helpers/utils"; import { CaptionContent } from "../CaptionContent"; import { ValidationAlert } from "@mendix/widget-plugin-component-kit/Alert"; import { Placeholder } from "../Placeholder"; @@ -26,6 +27,7 @@ export function RadioSelection({ const name = groupName?.value ?? inputId; const validation = selector.validation; + const errorId = getValidationErrorId(inputId); const handleChange = (e: ChangeEvent): void => { if (isReadOnly) { @@ -92,7 +94,7 @@ export function RadioSelection({ ); })} {options.length === 0 && } - {validation && {validation}} + {validation && {validation}} ); } diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/utils.ts b/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/utils.ts index 1d9bb631a2..c05390081e 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/utils.ts +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/utils.ts @@ -55,3 +55,7 @@ export function getCustomCaption(values: CheckboxRadioSelectionPreviewProps): st } return emptyStringFormat; } + +export function getValidationErrorId(inputId?: string): string | undefined { + return inputId ? inputId + "-validation-message" : undefined; +} From f98d886cc737b00e27a6936d4098b969243cc8c9 Mon Sep 17 00:00:00 2001 From: Yordan Date: Fri, 3 Oct 2025 15:46:41 +0200 Subject: [PATCH 3/4] chore: apply fix to radioSelection as well --- .../src/components/CheckboxSelection/CheckboxSelection.tsx | 4 +++- .../src/components/RadioSelection/RadioSelection.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx index b47ffb7da3..e4341c2692 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx @@ -60,6 +60,8 @@ export function CheckboxSelection({ disabled={isReadOnly} tabIndex={tabIndex} onChange={e => handleChange(optionId, e.target.checked)} + aria-describedby={selector.validation ? errorId : undefined} + aria-invalid={selector.validation ? true : undefined} /> ) => { @@ -76,7 +78,7 @@ export function CheckboxSelection({ ); })} {options.length === 0 && } - {validation && {validation}} + {validation && {validation}} ); } diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx index e952fd3b19..390b8e8473 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx @@ -75,6 +75,8 @@ export function RadioSelection({ disabled={isReadOnly} tabIndex={tabIndex} onChange={handleChange} + aria-describedby={selector.validation ? errorId : undefined} + aria-invalid={selector.validation ? true : undefined} /> } - {validation && {validation}} + {validation && {validation}} ); } From c049e2b2db2a8964a191c232faeeed948467b2ea Mon Sep 17 00:00:00 2001 From: Yordan Date: Wed, 8 Oct 2025 15:49:59 +0200 Subject: [PATCH 4/4] fix: move aria-describedby and aria-invalid to appropriate elements per W3C accessibility guidelines --- .../components/CheckboxSelection/CheckboxSelection.tsx | 8 ++++++-- .../src/components/RadioSelection/RadioSelection.tsx | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx index e4341c2692..b7890b5e40 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx @@ -30,6 +30,8 @@ export function CheckboxSelection({ } }; + const isSingleCheckbox = options.length === 1; + return (
{options.map((optionId, index) => { const isSelected = currentIds.includes(optionId); @@ -60,8 +64,8 @@ export function CheckboxSelection({ disabled={isReadOnly} tabIndex={tabIndex} onChange={e => handleChange(optionId, e.target.checked)} - aria-describedby={selector.validation ? errorId : undefined} - aria-invalid={selector.validation ? true : undefined} + aria-describedby={isSingleCheckbox && selector.validation ? errorId : undefined} + aria-invalid={isSingleCheckbox && selector.validation ? true : undefined} /> ) => { diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx index 390b8e8473..85d54759b2 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/components/RadioSelection/RadioSelection.tsx @@ -50,6 +50,8 @@ export function RadioSelection({ role={asSingleCheckbox ? "group" : "radiogroup"} aria-labelledby={`${inputId}-label`} aria-required={ariaRequired?.value} + aria-describedby={!asSingleCheckbox && selector.validation ? errorId : undefined} + aria-invalid={!asSingleCheckbox && selector.validation ? true : undefined} > {options.map((optionId, index) => { const isSelected = currentId === optionId; @@ -75,8 +77,8 @@ export function RadioSelection({ disabled={isReadOnly} tabIndex={tabIndex} onChange={handleChange} - aria-describedby={selector.validation ? errorId : undefined} - aria-invalid={selector.validation ? true : undefined} + aria-describedby={asSingleCheckbox && selector.validation ? errorId : undefined} + aria-invalid={asSingleCheckbox && selector.validation ? true : undefined} />