diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md b/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md index 3622a1c456..21b5d83a19 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md @@ -9,6 +9,12 @@ 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. +- 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.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 24fab78561..9ec2a27136 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..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,6 +68,7 @@ describe("CheckboxRadioSelection", () => { customEditabilityExpression: { status: "available", value: false } as any, readOnlyStyle: "bordered" as const, ariaRequired: { status: "available", value: false } as any, + 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/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..d7cadccba5 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?: DynamicValue; } 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; }