Skip to content

Commit 9881ef7

Browse files
HedwigJDoetsgjulivan
authored andcommitted
fix: backport fix to combobox validation
1 parent f7a4e8f commit 9881ef7

File tree

7 files changed

+79
-56
lines changed

7 files changed

+79
-56
lines changed

packages/pluggableWidgets/combobox-web/CHANGELOG.md

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,174 +6,178 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Fixed
10+
11+
- We fixed an issue where validation was not connected to combobox, causing issues for screenreaders.
12+
913
## [1.7.2] - 2025-07-31
1014

1115
### Fixed
1216

13-
- We fixed an issue where combobox does not show initial caption correctly for database source.
17+
- We fixed an issue where combobox does not show initial caption correctly for database source.
1418

15-
- We fixed an issue where combobox caption expression does not load correctly.
19+
- We fixed an issue where combobox caption expression does not load correctly.
1620

1721
## [1.7.1] - 2025-01-28
1822

1923
### Fixed
2024

21-
- We fixed an issue where database onchange failed to trigger, if you have set your onChange event using database source on v1.7.0 previously, it will clear again upon upgrade, but it will be seamless upgrade from 1.6.x version.
25+
- We fixed an issue where database onchange failed to trigger, if you have set your onChange event using database source on v1.7.0 previously, it will clear again upon upgrade, but it will be seamless upgrade from 1.6.x version.
2226

2327
## [1.7.0] - 2024-12-17
2428

2529
### Fixed
2630

27-
- We fixed an issue where placeholder failed to shown on database source.
31+
- We fixed an issue where placeholder failed to shown on database source.
2832

29-
- We fixed an issue where onchange event on database source triggered directly onload.
33+
- We fixed an issue where onchange event on database source triggered directly onload.
3034

31-
- We fixed an issue where selected value sometimes shows unavailable text on lazy loaded content.
35+
- We fixed an issue where selected value sometimes shows unavailable text on lazy loaded content.
3236

33-
- We fixed an issue where database optional value attribute breaks on runtime when not being set.
37+
- We fixed an issue where database optional value attribute breaks on runtime when not being set.
3438

35-
- We fixed an issue where custom content not shown on design preview and runtime for database datasource.
39+
- We fixed an issue where custom content not shown on design preview and runtime for database datasource.
3640

37-
- We fixed an issue where the onChange event was not being triggered when selecting an item for the first time.
41+
- We fixed an issue where the onChange event was not being triggered when selecting an item for the first time.
3842

39-
- We fixed an issue where the combo box values would not refresh after toggling its read-only state.
43+
- We fixed an issue where the combo box values would not refresh after toggling its read-only state.
4044

41-
- We fixed an issue with the toolbar visibility when a combobox menu overlays it.
45+
- We fixed an issue with the toolbar visibility when a combobox menu overlays it.
4246

43-
- We fixed a11y issue where aria-required not applied in the widget.
47+
- We fixed a11y issue where aria-required not applied in the widget.
4448

4549
### Added
4650

47-
- We added support for attribute with **Long** type.
51+
- We added support for attribute with **Long** type.
4852

4953
### Changed
5054

51-
- We make **Value** no longer required if the **Target attribute** is not set for database datasource.
55+
- We make **Value** no longer required if the **Target attribute** is not set for database datasource.
5256

53-
- We restructure **Attribute** group configuration and rename it as **Store value**.
57+
- We restructure **Attribute** group configuration and rename it as **Store value**.
5458

5559
## [1.6.3] - 2024-08-07
5660

5761
### Added
5862

59-
- We added a new filter type, 'Contains (exact),' as an option to provide a more strict search ranking.
63+
- We added a new filter type, 'Contains (exact),' as an option to provide a more strict search ranking.
6064

6165
## [1.6.2] - 2024-07-16
6266

6367
### Changed
6468

65-
- Due to technical limitation, it is not possible to use lazy load on caption type = expression. Thus, we removed lazy loading for this configuration.
69+
- Due to technical limitation, it is not possible to use lazy load on caption type = expression. Thus, we removed lazy loading for this configuration.
6670

6771
## [1.6.1] - 2024-06-28
6872

6973
### Changed
7074

71-
- We changed the default value for lazy loading. From now on, lazy loading will be true by default.
75+
- We changed the default value for lazy loading. From now on, lazy loading will be true by default.
7276

73-
- We fixed an issue where in some cases the clear button is rendered outside of a combobox.
77+
- We fixed an issue where in some cases the clear button is rendered outside of a combobox.
7478

7579
## [1.6.0] - 2024-06-19
7680

7781
### Changed
7882

79-
- We changed how input filtering works for lazy loading. if set to true, then the filter will works by retrieving directly from datasource, otherwise it works by scanning the current loaded data.
83+
- We changed how input filtering works for lazy loading. if set to true, then the filter will works by retrieving directly from datasource, otherwise it works by scanning the current loaded data.
8084

8185
### Added
8286

83-
- We added Spinner and Skeleton loaders in addition to lazy loading feature. The default loader is spinner, and skeleton can also be selected. This improves UX when loading more content.
87+
- We added Spinner and Skeleton loaders in addition to lazy loading feature. The default loader is spinner, and skeleton can also be selected. This improves UX when loading more content.
8488

85-
- We added lazy loading feature. By default it is turned off. When turned on, the items will be loaded in batches when scrolling.
89+
- We added lazy loading feature. By default it is turned off. When turned on, the items will be loaded in batches when scrolling.
8690

8791
## # Breaking
8892

89-
- The Combo box now uses Atlas variables for its styling. This may change how the widget looks depending on the custom variables.
93+
- The Combo box now uses Atlas variables for its styling. This may change how the widget looks depending on the custom variables.
9094

9195
## [1.5.0] - 2024-05-06
9296

9397
### Added
9498

95-
- We added readonly style to the combobox configuration.
99+
- We added readonly style to the combobox configuration.
96100

97101
## [1.4.0] - 2024-04-19
98102

99103
### Changed
100104

101-
- We made accessibility text as optional.
105+
- We made accessibility text as optional.
102106

103-
- We are no longer retrieving full dropdown options list if the combobox is readonly.
107+
- We are no longer retrieving full dropdown options list if the combobox is readonly.
104108

105109
### Added
106110

107-
- We improved accessibility on combobox by removing a duplicated aria-expanded from combobox widget.
111+
- We improved accessibility on combobox by removing a duplicated aria-expanded from combobox widget.
108112

109113
### Fixed
110114

111-
- We fixed an issue with the sorting of the displayed selected items in Multi Selection, where the sorting of selected items did not match the sorting in the menu.
115+
- We fixed an issue with the sorting of the displayed selected items in Multi Selection, where the sorting of selected items did not match the sorting in the menu.
112116

113117
## [1.3.1] - 2024-04-08
114118

115119
### Fixed
116120

117-
- We fixed scrollbar click issue causing combobox to close.
121+
- We fixed scrollbar click issue causing combobox to close.
118122

119123
## [1.3.0] - 2024-04-03
120124

121125
### Fixed
122126

123-
- We fixed sorting on combobox, now the sorting follows the default when the combobox opens, and follow a sorted ranking when any input is given.
127+
- We fixed sorting on combobox, now the sorting follows the default when the combobox opens, and follow a sorted ranking when any input is given.
124128

125-
- We fixed focusable element not able to have focus if being placed on custom footer.
129+
- We fixed focusable element not able to have focus if being placed on custom footer.
126130

127131
### Added
128132

129-
- We added static values feature support.
133+
- We added static values feature support.
130134

131135
## [1.2.0] - 2024-02-27
132136

133137
### Fixed
134138

135-
- We fixed no options text not shown on single selection.
139+
- We fixed no options text not shown on single selection.
136140

137141
### Changed
138142

139-
- We made a minor change on the configuration labels.
143+
- We made a minor change on the configuration labels.
140144

141145
### Added
142146

143-
- We added database list feature support.
147+
- We added database list feature support.
144148

145149
## [1.1.3] - 2024-01-22
146150

147151
### Fixed
148152

149-
- We fixed dropdown options directly closing when clicking on scrollbar if placed in popup dialog
153+
- We fixed dropdown options directly closing when clicking on scrollbar if placed in popup dialog
150154

151155
## [1.1.2] - 2024-01-19
152156

153157
### Fixed
154158

155-
- We fixed selected options not showing for custom content type "yes"
159+
- We fixed selected options not showing for custom content type "yes"
156160

157161
## [1.1.1] - 2024-01-15
158162

159163
### Changed
160164

161-
- We changed **Select/Unselect button** caption into **Show Select all** for easier understanding
165+
- We changed **Select/Unselect button** caption into **Show Select all** for easier understanding
162166

163167
## [1.1.0] - 2024-01-14
164168

165169
### Added
166170

167-
- We added a new property named **Show footer**, enabling the Combobox widget to display a footer within its menu. When **Show footer** is set to **Yes**, developers can include custom footer content such as text, buttons, links, etc.
171+
- We added a new property named **Show footer**, enabling the Combobox widget to display a footer within its menu. When **Show footer** is set to **Yes**, developers can include custom footer content such as text, buttons, links, etc.
168172

169-
- We added a new property named **Select/Unselect button**, enabling the Combobox widget to display a select/unselect all button on the top of the menu list.
173+
- We added a new property named **Select/Unselect button**, enabling the Combobox widget to display a select/unselect all button on the top of the menu list.
170174

171175
### Fixed
172176

173-
- Sorting of the search results now follow the sort order set for Selectable objects.
177+
- Sorting of the search results now follow the sort order set for Selectable objects.
174178

175-
- We removed the clear button on selected item labels in read-only state.
179+
- We removed the clear button on selected item labels in read-only state.
176180

177-
- We fixed the positioning of the menu when it is flipped and collides with top of the viewport.
181+
- We fixed the positioning of the menu when it is flipped and collides with top of the viewport.
178182

179-
- We fixed single selection combo box losing focus after selecting an item.
183+
- We fixed single selection combo box losing focus after selecting an item.

packages/pluggableWidgets/combobox-web/src/components/ComboboxWrapper.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ interface ComboboxWrapperProps extends PropsWithChildren {
1313
getToggleButtonProps: (options?: UseComboboxGetToggleButtonPropsOptions | undefined) => any;
1414
validation?: string;
1515
isLoading: boolean;
16+
errorId?: string;
1617
}
1718

1819
export const ComboboxWrapper = forwardRef(
1920
(props: ComboboxWrapperProps, ref: RefObject<HTMLDivElement>): ReactElement => {
20-
const { isOpen, readOnly, readOnlyStyle, getToggleButtonProps, validation, children, isLoading } = props;
21+
const { isOpen, readOnly, readOnlyStyle, getToggleButtonProps, validation, children, isLoading, errorId } =
22+
props;
2123
const { id, onClick } = getToggleButtonProps();
2224

2325
return (
@@ -45,7 +47,7 @@ export const ComboboxWrapper = forwardRef(
4547
</div>
4648
)}
4749
</div>
48-
{validation && <ValidationAlert>{validation}</ValidationAlert>}
50+
{validation && <ValidationAlert id={errorId}>{validation}</ValidationAlert>}
4951
</Fragment>
5052
);
5153
}

packages/pluggableWidgets/combobox-web/src/components/MultiSelection/MultiSelection.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import classNames from "classnames";
22
import { Fragment, KeyboardEvent, ReactElement, createElement, useMemo, useRef } from "react";
33
import { ClearButton } from "../../assets/icons";
44
import { MultiSelector, SelectionBaseProps } from "../../helpers/types";
5-
import { getSelectedCaptionsPlaceholder } from "../../helpers/utils";
5+
import { getSelectedCaptionsPlaceholder, getValidationErrorId } from "../../helpers/utils";
66
import { useDownshiftMultiSelectProps } from "../../hooks/useDownshiftMultiSelectProps";
77
import { useLazyLoading } from "../../hooks/useLazyLoading";
88
import { ComboboxWrapper } from "../ComboboxWrapper";
@@ -37,6 +37,7 @@ export function MultiSelection({
3737
const inputRef = useRef<HTMLInputElement>(null);
3838
const isSelectedItemsBoxStyle = selector.selectedItemsStyle === "boxes";
3939
const isOptionsSelected = selector.isOptionsSelected();
40+
const errorId = getValidationErrorId(options.inputId);
4041

4142
const memoizedselectedCaptions = useMemo(
4243
() => getSelectedCaptionsPlaceholder(selector, selectedItems),
@@ -65,6 +66,7 @@ export function MultiSelection({
6566
getToggleButtonProps={getToggleButtonProps}
6667
validation={selector.validation}
6768
isLoading={lazyLoading && selector.options.isLoading}
69+
errorId={errorId}
6870
>
6971
<div
7072
className={classNames(
@@ -135,6 +137,8 @@ export function MultiSelection({
135137
readOnly: selector.options.filterType === "none",
136138
"aria-required": ariaRequired
137139
})}
140+
aria-describedby={selector.validation ? errorId : undefined}
141+
aria-invalid={selector.validation ? true : undefined}
138142
/>
139143
<InputPlaceholder isEmpty={selectedItems.length <= 0}>{memoizedselectedCaptions}</InputPlaceholder>
140144
</div>

packages/pluggableWidgets/combobox-web/src/components/SingleSelection/SingleSelection.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import classNames from "classnames";
22
import { Fragment, ReactElement, createElement, useMemo, useRef } from "react";
33
import { ClearButton } from "../../assets/icons";
44
import { SelectionBaseProps, SingleSelector } from "../../helpers/types";
5+
import { getValidationErrorId } from "../../helpers/utils";
56
import { useDownshiftSingleSelectProps } from "../../hooks/useDownshiftSingleSelectProps";
67
import { useLazyLoading } from "../../hooks/useLazyLoading";
78
import { ComboboxWrapper } from "../ComboboxWrapper";
@@ -54,6 +55,8 @@ export function SingleSelection({
5455
]
5556
);
5657

58+
const errorId = getValidationErrorId(options.inputId);
59+
5760
return (
5861
<Fragment>
5962
<ComboboxWrapper
@@ -63,6 +66,7 @@ export function SingleSelection({
6366
getToggleButtonProps={getToggleButtonProps}
6467
validation={selector.validation}
6568
isLoading={lazyLoading && selector.options.isLoading}
69+
errorId={errorId}
6670
>
6771
<div
6872
className={classNames("widget-combobox-selected-items", {
@@ -84,6 +88,8 @@ export function SingleSelection({
8488
{ suppressRefError: true }
8589
)}
8690
placeholder=" "
91+
aria-describedby={selector.validation ? errorId : undefined}
92+
aria-invalid={selector.validation ? true : undefined}
8793
/>
8894
<InputPlaceholder
8995
isEmpty={!selector.currentId || !selector.caption.render(selectedItem, "label")}

packages/pluggableWidgets/combobox-web/src/helpers/utils.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ export function CaptionContent(props: CaptionContentProps): ReactElement {
4040
onClick: onClick
4141
? onClick
4242
: htmlFor
43-
? (e: MouseEvent) => {
44-
e.preventDefault();
45-
}
46-
: undefined
43+
? (e: MouseEvent) => {
44+
e.preventDefault();
45+
}
46+
: undefined
4747
});
4848
}
4949

@@ -114,3 +114,7 @@ export function _valuesIsEqual(valueA: ValueType, valueB: ValueType): boolean {
114114
}
115115
return valueA === valueB;
116116
}
117+
118+
export function getValidationErrorId(inputId?: string): string | undefined {
119+
return inputId ? inputId + "-validation-message" : undefined;
120+
}

packages/pluggableWidgets/combobox-web/typings/ComboboxProps.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ export interface ComboboxContainerProps {
9696

9797
export interface ComboboxPreviewProps {
9898
readOnly: boolean;
99-
renderMode?: "design" | "xray" | "structure";
99+
renderMode: "design" | "xray" | "structure";
100+
translate: (text: string) => string;
100101
source: SourceEnum;
101102
optionsSourceType: OptionsSourceTypeEnum;
102103
attributeEnumeration: string;

packages/shared/widget-plugin-component-kit/src/Alert.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,26 @@ export interface AlertProps {
55
children?: ReactNode;
66
className?: string;
77
bootstrapStyle: "default" | "primary" | "success" | "info" | "warning" | "danger";
8+
id?: string;
89
role?: string;
910
}
1011

1112
export interface ValidationAlertProps {
1213
children?: ReactNode;
1314
className?: string;
15+
id?: string;
1416
}
1517

1618
// cloning from https://gitlab.rnd.mendix.com/appdev/appdev/-/blob/master/client/src/widgets/web/helpers/Alert.tsx
17-
export const ValidationAlert = ({ className, children }: ValidationAlertProps): JSX.Element | null => (
18-
<Alert className={classNames("mx-validation-message", className)} bootstrapStyle="danger" role="alert">
19+
export const ValidationAlert = ({ className, children, id }: ValidationAlertProps): JSX.Element | null => (
20+
<Alert className={classNames("mx-validation-message", className)} bootstrapStyle="danger" role="alert" id={id}>
1921
{children}
2022
</Alert>
2123
);
2224

23-
export const Alert = ({ className, bootstrapStyle, children, role }: AlertProps): JSX.Element | null =>
25+
export const Alert = ({ className, bootstrapStyle, children, role, id }: AlertProps): JSX.Element | null =>
2426
Children.count(children) > 0 ? (
25-
<div className={classNames(`alert alert-${bootstrapStyle}`, className)} role={role}>
27+
<div className={classNames(`alert alert-${bootstrapStyle}`, className)} role={role} id={id}>
2628
{children}
2729
</div>
2830
) : null;

0 commit comments

Comments
 (0)