From e05d76b43964480dc9bf8db3b6000db8421b09a9 Mon Sep 17 00:00:00 2001 From: Yordan Date: Wed, 1 Oct 2025 16:11:46 +0200 Subject: [PATCH 1/2] feat: enhance properties and add ariaLabel support --- .../checkbox-radio-selection-web/CHANGELOG.md | 7 ++ .../checkbox-radio-selection-web/package.json | 2 +- .../CheckboxRadioSelection.editorConfig.ts | 16 ++++ .../src/CheckboxRadioSelection.xml | 86 ++++++++++--------- .../__tests__/CheckboxRadioSelection.spec.tsx | 1 + .../src/package.xml | 2 +- .../typings/CheckboxRadioSelectionProps.d.ts | 22 ++--- 7 files changed, 83 insertions(+), 53 deletions(-) diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md b/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md index 3622a1c456..50815ba30a 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md @@ -9,6 +9,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added - We added a missing validation alert. +- Added aria-label property + +### Changed + +- The Caption property now appears below Entity. +- The Caption property is now only visible if Custom content is set to 'No'. +- Moved the Group name attribute to the General tab in the General property group. ## [1.0.0] - 2025-08-25 diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/package.json b/packages/pluggableWidgets/checkbox-radio-selection-web/package.json index 620806d00c..ecec75f255 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/package.json +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/package.json @@ -1,7 +1,7 @@ { "name": "@mendix/checkbox-radio-selection-web", "widgetName": "CheckboxRadioSelection", - "version": "1.0.0", + "version": "1.1.0", "description": "Configurable radio buttons and check box widget", "copyright": "© Mendix Technology BV 2025. All rights reserved.", "license": "Apache-2.0", diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorConfig.ts b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorConfig.ts index b890005ac6..a6de88c188 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorConfig.ts +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorConfig.ts @@ -133,6 +133,22 @@ export function getProperties( }); } + if (values.optionsSourceCustomContentType === "yes") { + if (values.source === "context" && values.optionsSourceType === "association") { + hidePropertiesIn(defaultProperties, values, [ + "optionsSourceAssociationCaptionType", + "optionsSourceAssociationCaptionAttribute", + "optionsSourceAssociationCaptionExpression" + ]); + } else if (values.source === "database") { + hidePropertiesIn(defaultProperties, values, [ + "optionsSourceDatabaseCaptionType", + "optionsSourceDatabaseCaptionAttribute", + "optionsSourceDatabaseCaptionExpression" + ]); + } + } + return defaultProperties; } diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.xml b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.xml index 24fab78561..68b33d7315 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.xml +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.xml @@ -59,6 +59,44 @@ + + + Value + + + + + + + + + + Target attribute + + + + + + + + + + + + + + Entity + + + + + + + + Selectable objects + + + @@ -103,44 +141,6 @@ - - - Value - - - - - - - - - - Target attribute - - - - - - - - - - - - - - Entity - - - - - - - - Selectable objects - - - @@ -214,6 +214,11 @@ Radio button + + Group name + Name for the group of associated inputs + + @@ -271,10 +276,9 @@ - - Group name + + Aria label - diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx index 0a4efa8030..bfaf0c608f 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx @@ -68,6 +68,7 @@ describe("CheckboxRadioSelection", () => { customEditabilityExpression: { status: "available", value: false } as any, readOnlyStyle: "bordered" as const, ariaRequired: { status: "available", value: false } as any, + ariaLabel: "", controlType: "checkbox" as const }; diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/package.xml b/packages/pluggableWidgets/checkbox-radio-selection-web/src/package.xml index 2d81ae1635..794be0ced8 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/package.xml +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/typings/CheckboxRadioSelectionProps.d.ts b/packages/pluggableWidgets/checkbox-radio-selection-web/typings/CheckboxRadioSelectionProps.d.ts index 73d66d4f20..13b22642e8 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/typings/CheckboxRadioSelectionProps.d.ts +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/typings/CheckboxRadioSelectionProps.d.ts @@ -45,16 +45,16 @@ export interface CheckboxRadioSelectionContainerProps { attributeBoolean: EditableValue; optionsSourceDatabaseDataSource?: ListValue; optionsSourceDatabaseItemSelection?: SelectionSingleValue | SelectionMultiValue; + optionsSourceDatabaseValueAttribute?: ListAttributeValue; + databaseAttributeString?: EditableValue; + attributeAssociation: ReferenceValue | ReferenceSetValue; + optionsSourceAssociationDataSource?: ListValue; optionsSourceAssociationCaptionType: OptionsSourceAssociationCaptionTypeEnum; optionsSourceDatabaseCaptionType: OptionsSourceDatabaseCaptionTypeEnum; optionsSourceAssociationCaptionAttribute?: ListAttributeValue; optionsSourceDatabaseCaptionAttribute?: ListAttributeValue; optionsSourceAssociationCaptionExpression?: ListExpressionValue; optionsSourceDatabaseCaptionExpression?: ListExpressionValue; - optionsSourceDatabaseValueAttribute?: ListAttributeValue; - databaseAttributeString?: EditableValue; - attributeAssociation: ReferenceValue | ReferenceSetValue; - optionsSourceAssociationDataSource?: ListValue; staticAttribute: EditableValue; optionsSourceStaticDataSource: OptionsSourceStaticDataSourceType[]; noOptionsText?: DynamicValue; @@ -62,12 +62,13 @@ export interface CheckboxRadioSelectionContainerProps { optionsSourceAssociationCustomContent?: ListWidgetValue; optionsSourceDatabaseCustomContent?: ListWidgetValue; controlType: ControlTypeEnum; + groupName?: DynamicValue; customEditability: CustomEditabilityEnum; customEditabilityExpression: DynamicValue; readOnlyStyle: ReadOnlyStyleEnum; onChangeEvent?: ActionValue; ariaRequired: DynamicValue; - groupName?: DynamicValue; + ariaLabel: string; } export interface CheckboxRadioSelectionPreviewProps { @@ -80,16 +81,16 @@ export interface CheckboxRadioSelectionPreviewProps { attributeBoolean: string; optionsSourceDatabaseDataSource: {} | { caption: string } | { type: string } | null; optionsSourceDatabaseItemSelection: "Single" | "Multi" | "None"; + optionsSourceDatabaseValueAttribute: string; + databaseAttributeString: string; + attributeAssociation: string; + optionsSourceAssociationDataSource: {} | { caption: string } | { type: string } | null; optionsSourceAssociationCaptionType: OptionsSourceAssociationCaptionTypeEnum; optionsSourceDatabaseCaptionType: OptionsSourceDatabaseCaptionTypeEnum; optionsSourceAssociationCaptionAttribute: string; optionsSourceDatabaseCaptionAttribute: string; optionsSourceAssociationCaptionExpression: string; optionsSourceDatabaseCaptionExpression: string; - optionsSourceDatabaseValueAttribute: string; - databaseAttributeString: string; - attributeAssociation: string; - optionsSourceAssociationDataSource: {} | { caption: string } | { type: string } | null; staticAttribute: string; optionsSourceStaticDataSource: OptionsSourceStaticDataSourcePreviewType[]; noOptionsText: string; @@ -97,11 +98,12 @@ export interface CheckboxRadioSelectionPreviewProps { optionsSourceAssociationCustomContent: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> }; optionsSourceDatabaseCustomContent: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> }; controlType: ControlTypeEnum; + groupName: string; customEditability: CustomEditabilityEnum; customEditabilityExpression: string; readOnlyStyle: ReadOnlyStyleEnum; onChangeEvent: {} | null; onChangeDatabaseEvent: {} | null; ariaRequired: string; - groupName: string; + ariaLabel: string; } From d3377e258367e823d3fd590f2d24678f06bd9f1c Mon Sep 17 00:00:00 2001 From: gjulivan Date: Wed, 15 Oct 2025 11:35:01 +0200 Subject: [PATCH 2/2] fix: update arialabel and wrapper props --- .../checkbox-radio-selection-web/CHANGELOG.md | 1 - .../CheckboxRadioSelection.editorConfig.ts | 16 --------- .../CheckboxRadioSelection.editorPreview.tsx | 1 + .../src/CheckboxRadioSelection.tsx | 1 + .../src/CheckboxRadioSelection.xml | 2 +- .../__tests__/CheckboxRadioSelection.spec.tsx | 2 +- .../CheckboxSelection/CheckboxSelection.tsx | 17 +++++----- .../RadioSelection/RadioSelection.tsx | 19 ++++++----- .../src/helpers/types.ts | 1 + .../src/helpers/utils.ts | 4 +++ .../src/hooks/useWrapperProps.ts | 34 +++++++++++++++++++ .../typings/CheckboxRadioSelectionProps.d.ts | 2 +- 12 files changed, 63 insertions(+), 37 deletions(-) create mode 100644 packages/pluggableWidgets/checkbox-radio-selection-web/src/hooks/useWrapperProps.ts diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md b/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md index 50815ba30a..21b5d83a19 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md @@ -14,7 +14,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Changed - The Caption property now appears below Entity. -- The Caption property is now only visible if Custom content is set to 'No'. - Moved the Group name attribute to the General tab in the General property group. ## [1.0.0] - 2025-08-25 diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorConfig.ts b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorConfig.ts index a6de88c188..b890005ac6 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorConfig.ts +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorConfig.ts @@ -133,22 +133,6 @@ export function getProperties( }); } - if (values.optionsSourceCustomContentType === "yes") { - if (values.source === "context" && values.optionsSourceType === "association") { - hidePropertiesIn(defaultProperties, values, [ - "optionsSourceAssociationCaptionType", - "optionsSourceAssociationCaptionAttribute", - "optionsSourceAssociationCaptionExpression" - ]); - } else if (values.source === "database") { - hidePropertiesIn(defaultProperties, values, [ - "optionsSourceDatabaseCaptionType", - "optionsSourceDatabaseCaptionAttribute", - "optionsSourceDatabaseCaptionExpression" - ]); - } - } - return defaultProperties; } diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorPreview.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorPreview.tsx index 07c5f6c9db..27cea5406b 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorPreview.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorPreview.tsx @@ -22,6 +22,7 @@ export const preview = (props: CheckboxRadioSelectionPreviewProps): ReactElement labelId: `${id}-label`, readOnlyStyle: props.readOnlyStyle, ariaRequired: dynamic(false), + ariaLabel: dynamic(""), groupName: dynamic(`${id}-group`), noOptionsText: "No options available" }; diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.tsx index de36ee9355..075e1437f1 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.tsx @@ -19,6 +19,7 @@ export default function CheckboxRadioSelection(props: CheckboxRadioSelectionCont readOnlyStyle: props.readOnlyStyle, ariaRequired: props.ariaRequired, groupName: props.groupName, + ariaLabel: props.ariaLabel, noOptionsText: props.noOptionsText?.value ?? "No options available" }; diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.xml b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.xml index 68b33d7315..9ec2a27136 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.xml +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.xml @@ -276,7 +276,7 @@ - + Aria label diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx index bfaf0c608f..517ef3f6d1 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx @@ -68,7 +68,7 @@ describe("CheckboxRadioSelection", () => { customEditabilityExpression: { status: "available", value: false } as any, readOnlyStyle: "bordered" as const, ariaRequired: { status: "available", value: false } as any, - ariaLabel: "", + ariaLabel: { status: "available", value: "" } as any, controlType: "checkbox" as const }; 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 a83060b90e..ec3604ee93 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 { MouseEvent, ReactElement } from "react"; import { MultiSelector, SelectionBaseProps } from "../../helpers/types"; import { getValidationErrorId } from "../../helpers/utils"; +import { useWrapperProps } from "../../hooks/useWrapperProps"; import { CaptionContent } from "../CaptionContent"; import { ValidationAlert } from "@mendix/widget-plugin-component-kit/Alert"; import { Placeholder } from "../Placeholder"; @@ -11,6 +12,7 @@ export function CheckboxSelection({ tabIndex = 0, inputId, ariaRequired, + ariaLabel, readOnlyStyle, groupName, noOptionsText @@ -34,15 +36,14 @@ export function CheckboxSelection({ return (
{options.map((optionId, index) => { const isSelected = currentIds.includes(optionId); 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 930f79da82..7e7c9ea095 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,17 +1,19 @@ +import { If } from "@mendix/widget-plugin-component-kit/If"; import classNames from "classnames"; import { ChangeEvent, MouseEvent, ReactElement } from "react"; import { SelectionBaseProps, SingleSelector } from "../../helpers/types"; import { getValidationErrorId } from "../../helpers/utils"; +import { useWrapperProps } from "../../hooks/useWrapperProps"; 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"; export function RadioSelection({ selector, tabIndex = 0, inputId, ariaRequired, + ariaLabel, readOnlyStyle, groupName, noOptionsText @@ -43,15 +45,14 @@ export function RadioSelection({ return (
{options.map((optionId, index) => { const isSelected = currentId === optionId; diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/types.ts b/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/types.ts index f2a9aa7a31..505ed7556a 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/types.ts +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/types.ts @@ -84,6 +84,7 @@ export interface SelectionBaseProps { selector: Selector; tabIndex: number; ariaRequired: DynamicValue; + ariaLabel: DynamicValue | undefined; groupName: DynamicValue | undefined; noOptionsText: string; } 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 c05390081e..646a20a472 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/utils.ts +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/helpers/utils.ts @@ -59,3 +59,7 @@ export function getCustomCaption(values: CheckboxRadioSelectionPreviewProps): st export function getValidationErrorId(inputId?: string): string | undefined { return inputId ? inputId + "-validation-message" : undefined; } + +export function getInputLabel(inputId: string): Element | null { + return document.querySelector(`label[for="${inputId}"]`); +} diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/hooks/useWrapperProps.ts b/packages/pluggableWidgets/checkbox-radio-selection-web/src/hooks/useWrapperProps.ts new file mode 100644 index 0000000000..2dcc1bf792 --- /dev/null +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/hooks/useWrapperProps.ts @@ -0,0 +1,34 @@ +import { useMemo } from "react"; +import { getInputLabel } from "../helpers/utils"; +import classNames from "classnames"; +import { ReadOnlyStyleEnum } from "../../typings/CheckboxRadioSelectionProps"; +import { DynamicValue } from "mendix"; + +interface WrapperProps { + inputId: string; + isReadOnly: boolean; + isCheckbox: boolean; + readOnlyStyle: ReadOnlyStyleEnum; + ariaRequired: DynamicValue; + ariaLabel: DynamicValue | undefined; +} + +export function useWrapperProps(props: WrapperProps): any { + const { inputId, isReadOnly, isCheckbox, readOnlyStyle, ariaRequired, ariaLabel } = props; + + const inputLabel = getInputLabel(inputId); + const hasLabel = useMemo(() => Boolean(inputLabel), [inputLabel]); + const hasAriaLabel = useMemo(() => Boolean(ariaLabel && ariaLabel.value), [ariaLabel]); + + return { + className: classNames("widget-checkbox-radio-selection-list", { + "widget-checkbox-radio-selection-readonly": isReadOnly, + [`widget-checkbox-radio-selection-readonly-${readOnlyStyle}`]: isReadOnly + }), + id: inputId, + role: isCheckbox ? "group" : "radiogroup", + "aria-labelledby": hasLabel ? `${inputId}-label` : undefined, + "aria-required": ariaRequired ? ariaRequired.value : undefined, + "aria-label": hasAriaLabel ? ariaLabel?.value : undefined + }; +} diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/typings/CheckboxRadioSelectionProps.d.ts b/packages/pluggableWidgets/checkbox-radio-selection-web/typings/CheckboxRadioSelectionProps.d.ts index 13b22642e8..d7cadccba5 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/typings/CheckboxRadioSelectionProps.d.ts +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/typings/CheckboxRadioSelectionProps.d.ts @@ -68,7 +68,7 @@ export interface CheckboxRadioSelectionContainerProps { readOnlyStyle: ReadOnlyStyleEnum; onChangeEvent?: ActionValue; ariaRequired: DynamicValue; - ariaLabel: string; + ariaLabel?: DynamicValue; } export interface CheckboxRadioSelectionPreviewProps {