carbon-react 123.6.0 → 123.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/components/select/filterable-select/filterable-select.component.js +17 -15
- package/esm/components/select/index.d.ts +1 -1
- package/esm/components/select/multi-select/multi-select.component.js +16 -9
- package/esm/components/select/option/option.component.js +3 -1
- package/esm/components/select/select-list/select-list.component.d.ts +1 -0
- package/esm/components/select/select-list/select-list.component.js +8 -4
- package/esm/components/select/select-textbox/select-textbox.component.d.ts +3 -2
- package/esm/components/select/simple-select/index.d.ts +1 -1
- package/esm/components/select/simple-select/simple-select.component.d.ts +10 -0
- package/esm/components/select/simple-select/simple-select.component.js +8 -6
- package/lib/components/select/filterable-select/filterable-select.component.js +17 -15
- package/lib/components/select/index.d.ts +1 -1
- package/lib/components/select/multi-select/multi-select.component.js +16 -9
- package/lib/components/select/option/option.component.js +3 -1
- package/lib/components/select/select-list/select-list.component.d.ts +1 -0
- package/lib/components/select/select-list/select-list.component.js +8 -4
- package/lib/components/select/select-textbox/select-textbox.component.d.ts +3 -2
- package/lib/components/select/simple-select/index.d.ts +1 -1
- package/lib/components/select/simple-select/simple-select.component.d.ts +10 -0
- package/lib/components/select/simple-select/simple-select.component.js +8 -6
- package/package.json +1 -1
|
@@ -86,7 +86,7 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
86
86
|
deprecateUncontrolledWarnTriggered = true;
|
|
87
87
|
Logger.deprecate("Uncontrolled behaviour in `Filterable Select` is deprecated and support will soon be removed. Please make sure all your inputs are controlled.");
|
|
88
88
|
}
|
|
89
|
-
const createCustomEvent = useCallback(newValue => {
|
|
89
|
+
const createCustomEvent = useCallback((newValue, selectionConfirmed) => {
|
|
90
90
|
const customEvent = {
|
|
91
91
|
target: {
|
|
92
92
|
...(name && {
|
|
@@ -96,13 +96,14 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
96
96
|
id
|
|
97
97
|
}),
|
|
98
98
|
value: newValue
|
|
99
|
-
}
|
|
99
|
+
},
|
|
100
|
+
selectionConfirmed
|
|
100
101
|
};
|
|
101
102
|
return customEvent;
|
|
102
103
|
}, [name, id]);
|
|
103
|
-
const triggerChange = useCallback(newValue => {
|
|
104
|
+
const triggerChange = useCallback((newValue, selectionConfirmed) => {
|
|
104
105
|
if (onChange) {
|
|
105
|
-
onChange(createCustomEvent(newValue));
|
|
106
|
+
onChange(createCustomEvent(newValue, selectionConfirmed));
|
|
106
107
|
}
|
|
107
108
|
}, [onChange, createCustomEvent]);
|
|
108
109
|
function findElementWithMatchingText(textToMatch, list) {
|
|
@@ -110,7 +111,7 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
110
111
|
const {
|
|
111
112
|
text
|
|
112
113
|
} = child.props;
|
|
113
|
-
return text
|
|
114
|
+
return text?.toLowerCase().indexOf(textToMatch?.toLowerCase()) !== -1;
|
|
114
115
|
});
|
|
115
116
|
}
|
|
116
117
|
const updateValues = useCallback((newFilterText, isDeleteEvent) => {
|
|
@@ -119,15 +120,15 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
119
120
|
const isFilterCleared = isDeleteEvent && newFilterText === "";
|
|
120
121
|
if (!match || isFilterCleared) {
|
|
121
122
|
setTextValue(newFilterText);
|
|
122
|
-
triggerChange("");
|
|
123
|
+
triggerChange("", false);
|
|
123
124
|
return "";
|
|
124
125
|
}
|
|
125
126
|
if (isDeleteEvent) {
|
|
126
127
|
setTextValue(newFilterText);
|
|
127
128
|
return match.props.value;
|
|
128
129
|
}
|
|
129
|
-
triggerChange(match.props.value);
|
|
130
|
-
if (match.props.text
|
|
130
|
+
triggerChange(match.props.value, false);
|
|
131
|
+
if (match.props.text?.toLowerCase().startsWith(newFilterText.toLowerCase())) {
|
|
131
132
|
setTextValue(match.props.text);
|
|
132
133
|
} else {
|
|
133
134
|
setTextValue(newFilterText);
|
|
@@ -143,7 +144,7 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
143
144
|
const matchingOption = React.Children.toArray(children).find(child => /*#__PURE__*/React.isValidElement(child) && isExpectedOption(child, newValue));
|
|
144
145
|
if (!matchingOption || matchingOption.props.text === undefined) {
|
|
145
146
|
setTextValue(filterText || "");
|
|
146
|
-
} else if (isClosing || matchingOption.props.text
|
|
147
|
+
} else if (isClosing || matchingOption.props.text?.toLowerCase().startsWith(filterText?.toLowerCase())) {
|
|
147
148
|
setTextValue(matchingOption.props.text);
|
|
148
149
|
}
|
|
149
150
|
}, [children, filterText]);
|
|
@@ -156,7 +157,7 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
156
157
|
}, [updateValues]);
|
|
157
158
|
const fillLastFilterCharacter = useCallback(key => {
|
|
158
159
|
setFilterText(previousFilterText => {
|
|
159
|
-
if (previousFilterText
|
|
160
|
+
if (previousFilterText?.length === textValue?.length - 1 && key === textValue.slice(-1)) {
|
|
160
161
|
return textValue;
|
|
161
162
|
}
|
|
162
163
|
return previousFilterText;
|
|
@@ -251,9 +252,9 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
251
252
|
};
|
|
252
253
|
}, [handleGlobalClick]);
|
|
253
254
|
useEffect(() => {
|
|
254
|
-
const textStartsWithFilter = textValue
|
|
255
|
+
const textStartsWithFilter = textValue?.toLowerCase().startsWith(filterText?.toLowerCase());
|
|
255
256
|
const isTextboxActive = !disabled && !readOnly;
|
|
256
|
-
if (isTextboxActive && textboxRef && filterText
|
|
257
|
+
if (isTextboxActive && textboxRef && filterText?.length && textValue?.length > filterText?.length && textStartsWithFilter) {
|
|
257
258
|
textboxRef.selectionStart = filterText.length;
|
|
258
259
|
}
|
|
259
260
|
}, [textValue, filterText, textboxRef, disabled, readOnly]);
|
|
@@ -262,7 +263,8 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
262
263
|
id: selectedOptionId,
|
|
263
264
|
text,
|
|
264
265
|
value: newValue,
|
|
265
|
-
selectionType
|
|
266
|
+
selectionType,
|
|
267
|
+
selectionConfirmed
|
|
266
268
|
} = optionData;
|
|
267
269
|
if (selectionType === "tab") {
|
|
268
270
|
setOpen(false);
|
|
@@ -273,8 +275,8 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
273
275
|
setSelectedValue(newValue);
|
|
274
276
|
setHighlightedValue(newValue);
|
|
275
277
|
}
|
|
276
|
-
setTextValue(text);
|
|
277
|
-
triggerChange(newValue);
|
|
278
|
+
setTextValue(text || /* istanbul ignore next */"");
|
|
279
|
+
triggerChange(newValue, selectionConfirmed);
|
|
278
280
|
setActiveDescendantId(selectedOptionId);
|
|
279
281
|
if (selectionType !== "navigationKey") {
|
|
280
282
|
setOpen(false);
|
|
@@ -5,7 +5,7 @@ export type { OptionRowProps } from "./option-row";
|
|
|
5
5
|
export { default as OptionGroupHeader } from "./option-group-header";
|
|
6
6
|
export type { OptionGroupHeaderProps } from "./option-group-header";
|
|
7
7
|
export { default as Select } from "./simple-select";
|
|
8
|
-
export type { SimpleSelectProps } from "./simple-select";
|
|
8
|
+
export type { SimpleSelectProps, CustomSelectChangeEvent, } from "./simple-select";
|
|
9
9
|
export { default as FilterableSelect } from "./filterable-select";
|
|
10
10
|
export type { FilterableSelectProps } from "./filterable-select";
|
|
11
11
|
export { default as MultiSelect } from "./multi-select";
|
|
@@ -100,7 +100,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
100
100
|
return true;
|
|
101
101
|
});
|
|
102
102
|
}, [onOpen]);
|
|
103
|
-
const createCustomEvent = useCallback(newValue => {
|
|
103
|
+
const createCustomEvent = useCallback((newValue, selectionConfirmed) => {
|
|
104
104
|
const customEvent = {
|
|
105
105
|
target: {
|
|
106
106
|
...(name && {
|
|
@@ -110,7 +110,8 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
110
110
|
id
|
|
111
111
|
}),
|
|
112
112
|
value: newValue
|
|
113
|
-
}
|
|
113
|
+
},
|
|
114
|
+
selectionConfirmed
|
|
114
115
|
};
|
|
115
116
|
return customEvent;
|
|
116
117
|
}, [name, id]);
|
|
@@ -119,11 +120,11 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
119
120
|
* components, both with and without onChange.
|
|
120
121
|
* It accepts a function to update the value, which is assumed to be have no side effects and therefore
|
|
121
122
|
* be safe to run more than once if needed. */
|
|
122
|
-
const updateValue = useCallback(updateFunction => {
|
|
123
|
+
const updateValue = useCallback((updateFunction, selectionConfirmed) => {
|
|
123
124
|
const newValue = updateFunction(actualValue);
|
|
124
125
|
// only call onChange if an option has been selected or deselected
|
|
125
126
|
if (onChange && newValue.length !== actualValue?.length) {
|
|
126
|
-
onChange(createCustomEvent(newValue));
|
|
127
|
+
onChange(createCustomEvent(newValue, selectionConfirmed));
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
// no need to update selectedValue if the component is controlled: onChange should take care of updating the value
|
|
@@ -158,7 +159,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
158
159
|
const newValue = previousValue.slice(); // spreading does not work but slice does - see https://github.com/microsoft/TypeScript/issues/53670
|
|
159
160
|
newValue.splice(index, 1);
|
|
160
161
|
return newValue;
|
|
161
|
-
});
|
|
162
|
+
}, true);
|
|
162
163
|
}, [updateValue]);
|
|
163
164
|
const handleTextboxKeydown = useCallback(event => {
|
|
164
165
|
const {
|
|
@@ -210,6 +211,11 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
210
211
|
return actualValue.map((singleValue, index) => {
|
|
211
212
|
const matchingOption = React.Children.toArray(children).find(child => /*#__PURE__*/React.isValidElement(child) && isExpectedOption(child, singleValue));
|
|
212
213
|
let pillProps = {};
|
|
214
|
+
if (!matchingOption) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* istanbul ignore else */
|
|
213
219
|
if ( /*#__PURE__*/React.isValidElement(matchingOption)) {
|
|
214
220
|
pillProps = {
|
|
215
221
|
title: matchingOption.props.text,
|
|
@@ -217,8 +223,8 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
217
223
|
borderColor: matchingOption.props.borderColor
|
|
218
224
|
};
|
|
219
225
|
}
|
|
220
|
-
const title = pillProps.title || "";
|
|
221
|
-
const key = title + ( /*#__PURE__*/React.isValidElement(matchingOption) && matchingOption.props.value || index);
|
|
226
|
+
const title = pillProps.title || /* istanbul ignore next */"";
|
|
227
|
+
const key = title + ( /*#__PURE__*/React.isValidElement(matchingOption) && matchingOption.props.value || /* istanbul ignore next */index);
|
|
222
228
|
return /*#__PURE__*/React.createElement(StyledSelectPillContainer, {
|
|
223
229
|
key: key
|
|
224
230
|
}, /*#__PURE__*/React.createElement(Pill, _extends({
|
|
@@ -331,7 +337,8 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
331
337
|
const {
|
|
332
338
|
value: newValue,
|
|
333
339
|
selectionType,
|
|
334
|
-
id: selectedOptionId
|
|
340
|
+
id: selectedOptionId,
|
|
341
|
+
selectionConfirmed
|
|
335
342
|
} = optionData;
|
|
336
343
|
if (selectionType === "navigationKey") {
|
|
337
344
|
setHighlightedValue(newValue);
|
|
@@ -350,7 +357,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
350
357
|
return previousValue;
|
|
351
358
|
}
|
|
352
359
|
return [...previousValue, newValue];
|
|
353
|
-
});
|
|
360
|
+
}, selectionConfirmed);
|
|
354
361
|
}, [textboxRef, actualValue, updateValue]);
|
|
355
362
|
const onSelectListClose = useCallback(() => {
|
|
356
363
|
setOpenState(false);
|
|
@@ -93,7 +93,8 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
93
93
|
const handleSelect = useCallback(optionData => {
|
|
94
94
|
onSelect({
|
|
95
95
|
...optionData,
|
|
96
|
-
selectionType: "click"
|
|
96
|
+
selectionType: "click",
|
|
97
|
+
selectionConfirmed: true
|
|
97
98
|
});
|
|
98
99
|
}, [onSelect]);
|
|
99
100
|
const childIdsRef = useRef(null);
|
|
@@ -177,13 +178,15 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
177
178
|
text,
|
|
178
179
|
value,
|
|
179
180
|
selectionType: "navigationKey",
|
|
180
|
-
id: childIds ? childIds[nextIndex] : /* istanbul ignore next */undefined
|
|
181
|
+
id: childIds ? childIds[nextIndex] : /* istanbul ignore next */undefined,
|
|
182
|
+
selectionConfirmed: false
|
|
181
183
|
});
|
|
182
184
|
}, [childrenList, currentOptionsListIndex, getIndexOfMatch, getNextHighlightableItemIndex, highlightedValue, onSelect, childIds]);
|
|
183
185
|
const handleActionButtonTab = useCallback((event, isActionButtonFocused) => {
|
|
184
186
|
if (isActionButtonFocused) {
|
|
185
187
|
onSelect({
|
|
186
|
-
selectionType: "tab"
|
|
188
|
+
selectionType: "tab",
|
|
189
|
+
selectionConfirmed: false
|
|
187
190
|
});
|
|
188
191
|
} else {
|
|
189
192
|
event.preventDefault();
|
|
@@ -222,7 +225,8 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
222
225
|
id: childIds ? childIds[currentOptionsListIndex] : /* istanbul ignore next */undefined,
|
|
223
226
|
text,
|
|
224
227
|
value,
|
|
225
|
-
selectionType: "enterKey"
|
|
228
|
+
selectionType: "enterKey",
|
|
229
|
+
selectionConfirmed: true
|
|
226
230
|
});
|
|
227
231
|
} else if (isNavigationKey(key)) {
|
|
228
232
|
focusOnAnchor();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CommonTextboxProps } from "../../textbox";
|
|
3
3
|
import { ValidationProps } from "../../../__internal__/validations";
|
|
4
|
-
|
|
4
|
+
import { CustomSelectChangeEvent } from "../simple-select/simple-select.component";
|
|
5
|
+
export interface FormInputPropTypes extends ValidationProps, Omit<CommonTextboxProps, "onClick" | "onChange"> {
|
|
5
6
|
/** Breakpoint for adaptive label (inline labels change to top aligned). Enables the adaptive behaviour when set */
|
|
6
7
|
adaptiveLabelBreakpoint?: number;
|
|
7
8
|
/** Prop to specify the aria-label attribute of the component input */
|
|
@@ -29,7 +30,7 @@ export interface FormInputPropTypes extends ValidationProps, Omit<CommonTextboxP
|
|
|
29
30
|
/** Specify a callback triggered on blur */
|
|
30
31
|
onBlur?: (ev: React.FocusEvent<HTMLInputElement>) => void;
|
|
31
32
|
/** Specify a callback triggered on change */
|
|
32
|
-
onChange?: (ev: React.ChangeEvent<HTMLInputElement>) => void;
|
|
33
|
+
onChange?: (ev: CustomSelectChangeEvent | React.ChangeEvent<HTMLInputElement>) => void;
|
|
33
34
|
/** Specify a callback triggered on click */
|
|
34
35
|
onClick?: (ev: React.MouseEvent<HTMLInputElement>) => void;
|
|
35
36
|
/** Specify a callback triggered on focus */
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from "./simple-select.component";
|
|
2
|
-
export type { SimpleSelectProps } from "./simple-select.component";
|
|
2
|
+
export type { SimpleSelectProps, CustomSelectChangeEvent, } from "./simple-select.component";
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Side } from "@floating-ui/dom";
|
|
3
3
|
import { FormInputPropTypes } from "../select-textbox";
|
|
4
|
+
export interface OptionData {
|
|
5
|
+
text?: string;
|
|
6
|
+
value?: string | Record<string, unknown>;
|
|
7
|
+
id?: string;
|
|
8
|
+
selectionType: string;
|
|
9
|
+
selectionConfirmed?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface CustomSelectChangeEvent extends React.ChangeEvent<HTMLInputElement> {
|
|
12
|
+
selectionConfirmed?: boolean;
|
|
13
|
+
}
|
|
4
14
|
export interface SimpleSelectProps extends Omit<FormInputPropTypes, "defaultValue" | "value"> {
|
|
5
15
|
/** Prop to specify the aria-label attribute of the component input */
|
|
6
16
|
"aria-label"?: string;
|
|
@@ -82,7 +82,7 @@ const SimpleSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
82
82
|
Logger.deprecate("Uncontrolled behaviour in `Simple Select` is deprecated and support will soon be removed. Please make sure all your inputs are controlled.");
|
|
83
83
|
}
|
|
84
84
|
const childOptions = useMemo(() => React.Children.toArray(children), [children]);
|
|
85
|
-
const createCustomEvent = useCallback(newValue => {
|
|
85
|
+
const createCustomEvent = useCallback((newValue, selectionConfirmed = false) => {
|
|
86
86
|
const customEvent = {
|
|
87
87
|
target: {
|
|
88
88
|
...(name && {
|
|
@@ -92,7 +92,8 @@ const SimpleSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
92
92
|
id
|
|
93
93
|
}),
|
|
94
94
|
value: newValue
|
|
95
|
-
}
|
|
95
|
+
},
|
|
96
|
+
selectionConfirmed
|
|
96
97
|
};
|
|
97
98
|
return customEvent;
|
|
98
99
|
}, [name, id]);
|
|
@@ -245,13 +246,13 @@ const SimpleSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
245
246
|
});
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
|
-
function updateValue(newValue, text) {
|
|
249
|
+
function updateValue(newValue, text, selectionConfirmed) {
|
|
249
250
|
if (!isControlled.current) {
|
|
250
251
|
setSelectedValue(newValue);
|
|
251
252
|
setTextValue(text);
|
|
252
253
|
}
|
|
253
254
|
if (onChange) {
|
|
254
|
-
onChange(createCustomEvent(newValue));
|
|
255
|
+
onChange(createCustomEvent(newValue, selectionConfirmed));
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
258
|
function onSelectOption(optionData) {
|
|
@@ -259,10 +260,11 @@ const SimpleSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
259
260
|
text,
|
|
260
261
|
value: newValue,
|
|
261
262
|
selectionType,
|
|
262
|
-
id: selectedOptionId
|
|
263
|
+
id: selectedOptionId,
|
|
264
|
+
selectionConfirmed
|
|
263
265
|
} = optionData;
|
|
264
266
|
const isClickTriggered = selectionType === "click";
|
|
265
|
-
updateValue(newValue, text);
|
|
267
|
+
updateValue(newValue, text, selectionConfirmed);
|
|
266
268
|
setActiveDescendantId(selectedOptionId);
|
|
267
269
|
if (selectionType !== "navigationKey") {
|
|
268
270
|
setOpenState(false);
|
|
@@ -95,7 +95,7 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
95
95
|
deprecateUncontrolledWarnTriggered = true;
|
|
96
96
|
_logger.default.deprecate("Uncontrolled behaviour in `Filterable Select` is deprecated and support will soon be removed. Please make sure all your inputs are controlled.");
|
|
97
97
|
}
|
|
98
|
-
const createCustomEvent = (0, _react.useCallback)(newValue => {
|
|
98
|
+
const createCustomEvent = (0, _react.useCallback)((newValue, selectionConfirmed) => {
|
|
99
99
|
const customEvent = {
|
|
100
100
|
target: {
|
|
101
101
|
...(name && {
|
|
@@ -105,13 +105,14 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
105
105
|
id
|
|
106
106
|
}),
|
|
107
107
|
value: newValue
|
|
108
|
-
}
|
|
108
|
+
},
|
|
109
|
+
selectionConfirmed
|
|
109
110
|
};
|
|
110
111
|
return customEvent;
|
|
111
112
|
}, [name, id]);
|
|
112
|
-
const triggerChange = (0, _react.useCallback)(newValue => {
|
|
113
|
+
const triggerChange = (0, _react.useCallback)((newValue, selectionConfirmed) => {
|
|
113
114
|
if (onChange) {
|
|
114
|
-
onChange(createCustomEvent(newValue));
|
|
115
|
+
onChange(createCustomEvent(newValue, selectionConfirmed));
|
|
115
116
|
}
|
|
116
117
|
}, [onChange, createCustomEvent]);
|
|
117
118
|
function findElementWithMatchingText(textToMatch, list) {
|
|
@@ -119,7 +120,7 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
119
120
|
const {
|
|
120
121
|
text
|
|
121
122
|
} = child.props;
|
|
122
|
-
return text
|
|
123
|
+
return text?.toLowerCase().indexOf(textToMatch?.toLowerCase()) !== -1;
|
|
123
124
|
});
|
|
124
125
|
}
|
|
125
126
|
const updateValues = (0, _react.useCallback)((newFilterText, isDeleteEvent) => {
|
|
@@ -128,15 +129,15 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
128
129
|
const isFilterCleared = isDeleteEvent && newFilterText === "";
|
|
129
130
|
if (!match || isFilterCleared) {
|
|
130
131
|
setTextValue(newFilterText);
|
|
131
|
-
triggerChange("");
|
|
132
|
+
triggerChange("", false);
|
|
132
133
|
return "";
|
|
133
134
|
}
|
|
134
135
|
if (isDeleteEvent) {
|
|
135
136
|
setTextValue(newFilterText);
|
|
136
137
|
return match.props.value;
|
|
137
138
|
}
|
|
138
|
-
triggerChange(match.props.value);
|
|
139
|
-
if (match.props.text
|
|
139
|
+
triggerChange(match.props.value, false);
|
|
140
|
+
if (match.props.text?.toLowerCase().startsWith(newFilterText.toLowerCase())) {
|
|
140
141
|
setTextValue(match.props.text);
|
|
141
142
|
} else {
|
|
142
143
|
setTextValue(newFilterText);
|
|
@@ -152,7 +153,7 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
152
153
|
const matchingOption = _react.default.Children.toArray(children).find(child => /*#__PURE__*/_react.default.isValidElement(child) && (0, _isExpectedOption.default)(child, newValue));
|
|
153
154
|
if (!matchingOption || matchingOption.props.text === undefined) {
|
|
154
155
|
setTextValue(filterText || "");
|
|
155
|
-
} else if (isClosing || matchingOption.props.text
|
|
156
|
+
} else if (isClosing || matchingOption.props.text?.toLowerCase().startsWith(filterText?.toLowerCase())) {
|
|
156
157
|
setTextValue(matchingOption.props.text);
|
|
157
158
|
}
|
|
158
159
|
}, [children, filterText]);
|
|
@@ -165,7 +166,7 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
165
166
|
}, [updateValues]);
|
|
166
167
|
const fillLastFilterCharacter = (0, _react.useCallback)(key => {
|
|
167
168
|
setFilterText(previousFilterText => {
|
|
168
|
-
if (previousFilterText
|
|
169
|
+
if (previousFilterText?.length === textValue?.length - 1 && key === textValue.slice(-1)) {
|
|
169
170
|
return textValue;
|
|
170
171
|
}
|
|
171
172
|
return previousFilterText;
|
|
@@ -260,9 +261,9 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
260
261
|
};
|
|
261
262
|
}, [handleGlobalClick]);
|
|
262
263
|
(0, _react.useEffect)(() => {
|
|
263
|
-
const textStartsWithFilter = textValue
|
|
264
|
+
const textStartsWithFilter = textValue?.toLowerCase().startsWith(filterText?.toLowerCase());
|
|
264
265
|
const isTextboxActive = !disabled && !readOnly;
|
|
265
|
-
if (isTextboxActive && textboxRef && filterText
|
|
266
|
+
if (isTextboxActive && textboxRef && filterText?.length && textValue?.length > filterText?.length && textStartsWithFilter) {
|
|
266
267
|
textboxRef.selectionStart = filterText.length;
|
|
267
268
|
}
|
|
268
269
|
}, [textValue, filterText, textboxRef, disabled, readOnly]);
|
|
@@ -271,7 +272,8 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
271
272
|
id: selectedOptionId,
|
|
272
273
|
text,
|
|
273
274
|
value: newValue,
|
|
274
|
-
selectionType
|
|
275
|
+
selectionType,
|
|
276
|
+
selectionConfirmed
|
|
275
277
|
} = optionData;
|
|
276
278
|
if (selectionType === "tab") {
|
|
277
279
|
setOpen(false);
|
|
@@ -282,8 +284,8 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
282
284
|
setSelectedValue(newValue);
|
|
283
285
|
setHighlightedValue(newValue);
|
|
284
286
|
}
|
|
285
|
-
setTextValue(text);
|
|
286
|
-
triggerChange(newValue);
|
|
287
|
+
setTextValue(text || /* istanbul ignore next */"");
|
|
288
|
+
triggerChange(newValue, selectionConfirmed);
|
|
287
289
|
setActiveDescendantId(selectedOptionId);
|
|
288
290
|
if (selectionType !== "navigationKey") {
|
|
289
291
|
setOpen(false);
|
|
@@ -5,7 +5,7 @@ export type { OptionRowProps } from "./option-row";
|
|
|
5
5
|
export { default as OptionGroupHeader } from "./option-group-header";
|
|
6
6
|
export type { OptionGroupHeaderProps } from "./option-group-header";
|
|
7
7
|
export { default as Select } from "./simple-select";
|
|
8
|
-
export type { SimpleSelectProps } from "./simple-select";
|
|
8
|
+
export type { SimpleSelectProps, CustomSelectChangeEvent, } from "./simple-select";
|
|
9
9
|
export { default as FilterableSelect } from "./filterable-select";
|
|
10
10
|
export type { FilterableSelectProps } from "./filterable-select";
|
|
11
11
|
export { default as MultiSelect } from "./multi-select";
|
|
@@ -109,7 +109,7 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
109
109
|
return true;
|
|
110
110
|
});
|
|
111
111
|
}, [onOpen]);
|
|
112
|
-
const createCustomEvent = (0, _react.useCallback)(newValue => {
|
|
112
|
+
const createCustomEvent = (0, _react.useCallback)((newValue, selectionConfirmed) => {
|
|
113
113
|
const customEvent = {
|
|
114
114
|
target: {
|
|
115
115
|
...(name && {
|
|
@@ -119,7 +119,8 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
119
119
|
id
|
|
120
120
|
}),
|
|
121
121
|
value: newValue
|
|
122
|
-
}
|
|
122
|
+
},
|
|
123
|
+
selectionConfirmed
|
|
123
124
|
};
|
|
124
125
|
return customEvent;
|
|
125
126
|
}, [name, id]);
|
|
@@ -128,11 +129,11 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
128
129
|
* components, both with and without onChange.
|
|
129
130
|
* It accepts a function to update the value, which is assumed to be have no side effects and therefore
|
|
130
131
|
* be safe to run more than once if needed. */
|
|
131
|
-
const updateValue = (0, _react.useCallback)(updateFunction => {
|
|
132
|
+
const updateValue = (0, _react.useCallback)((updateFunction, selectionConfirmed) => {
|
|
132
133
|
const newValue = updateFunction(actualValue);
|
|
133
134
|
// only call onChange if an option has been selected or deselected
|
|
134
135
|
if (onChange && newValue.length !== actualValue?.length) {
|
|
135
|
-
onChange(createCustomEvent(newValue));
|
|
136
|
+
onChange(createCustomEvent(newValue, selectionConfirmed));
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
// no need to update selectedValue if the component is controlled: onChange should take care of updating the value
|
|
@@ -167,7 +168,7 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
167
168
|
const newValue = previousValue.slice(); // spreading does not work but slice does - see https://github.com/microsoft/TypeScript/issues/53670
|
|
168
169
|
newValue.splice(index, 1);
|
|
169
170
|
return newValue;
|
|
170
|
-
});
|
|
171
|
+
}, true);
|
|
171
172
|
}, [updateValue]);
|
|
172
173
|
const handleTextboxKeydown = (0, _react.useCallback)(event => {
|
|
173
174
|
const {
|
|
@@ -219,6 +220,11 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
219
220
|
return actualValue.map((singleValue, index) => {
|
|
220
221
|
const matchingOption = _react.default.Children.toArray(children).find(child => /*#__PURE__*/_react.default.isValidElement(child) && (0, _isExpectedOption.default)(child, singleValue));
|
|
221
222
|
let pillProps = {};
|
|
223
|
+
if (!matchingOption) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* istanbul ignore else */
|
|
222
228
|
if ( /*#__PURE__*/_react.default.isValidElement(matchingOption)) {
|
|
223
229
|
pillProps = {
|
|
224
230
|
title: matchingOption.props.text,
|
|
@@ -226,8 +232,8 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
226
232
|
borderColor: matchingOption.props.borderColor
|
|
227
233
|
};
|
|
228
234
|
}
|
|
229
|
-
const title = pillProps.title || "";
|
|
230
|
-
const key = title + ( /*#__PURE__*/_react.default.isValidElement(matchingOption) && matchingOption.props.value || index);
|
|
235
|
+
const title = pillProps.title || /* istanbul ignore next */"";
|
|
236
|
+
const key = title + ( /*#__PURE__*/_react.default.isValidElement(matchingOption) && matchingOption.props.value || /* istanbul ignore next */index);
|
|
231
237
|
return /*#__PURE__*/_react.default.createElement(_multiSelect.StyledSelectPillContainer, {
|
|
232
238
|
key: key
|
|
233
239
|
}, /*#__PURE__*/_react.default.createElement(_pill.default, _extends({
|
|
@@ -340,7 +346,8 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
340
346
|
const {
|
|
341
347
|
value: newValue,
|
|
342
348
|
selectionType,
|
|
343
|
-
id: selectedOptionId
|
|
349
|
+
id: selectedOptionId,
|
|
350
|
+
selectionConfirmed
|
|
344
351
|
} = optionData;
|
|
345
352
|
if (selectionType === "navigationKey") {
|
|
346
353
|
setHighlightedValue(newValue);
|
|
@@ -359,7 +366,7 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
359
366
|
return previousValue;
|
|
360
367
|
}
|
|
361
368
|
return [...previousValue, newValue];
|
|
362
|
-
});
|
|
369
|
+
}, selectionConfirmed);
|
|
363
370
|
}, [textboxRef, actualValue, updateValue]);
|
|
364
371
|
const onSelectListClose = (0, _react.useCallback)(() => {
|
|
365
372
|
setOpenState(false);
|
|
@@ -51,7 +51,9 @@ const Option = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
51
51
|
role: "option",
|
|
52
52
|
hidden: hidden,
|
|
53
53
|
style: style
|
|
54
|
-
}, rest
|
|
54
|
+
}, rest, {
|
|
55
|
+
fill: undefined
|
|
56
|
+
}), children || text);
|
|
55
57
|
});
|
|
56
58
|
Option.propTypes = {
|
|
57
59
|
"about": _propTypes.default.string,
|
|
@@ -102,7 +102,8 @@ const SelectList = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
102
102
|
const handleSelect = (0, _react.useCallback)(optionData => {
|
|
103
103
|
onSelect({
|
|
104
104
|
...optionData,
|
|
105
|
-
selectionType: "click"
|
|
105
|
+
selectionType: "click",
|
|
106
|
+
selectionConfirmed: true
|
|
106
107
|
});
|
|
107
108
|
}, [onSelect]);
|
|
108
109
|
const childIdsRef = (0, _react.useRef)(null);
|
|
@@ -186,13 +187,15 @@ const SelectList = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
186
187
|
text,
|
|
187
188
|
value,
|
|
188
189
|
selectionType: "navigationKey",
|
|
189
|
-
id: childIds ? childIds[nextIndex] : /* istanbul ignore next */undefined
|
|
190
|
+
id: childIds ? childIds[nextIndex] : /* istanbul ignore next */undefined,
|
|
191
|
+
selectionConfirmed: false
|
|
190
192
|
});
|
|
191
193
|
}, [childrenList, currentOptionsListIndex, getIndexOfMatch, getNextHighlightableItemIndex, highlightedValue, onSelect, childIds]);
|
|
192
194
|
const handleActionButtonTab = (0, _react.useCallback)((event, isActionButtonFocused) => {
|
|
193
195
|
if (isActionButtonFocused) {
|
|
194
196
|
onSelect({
|
|
195
|
-
selectionType: "tab"
|
|
197
|
+
selectionType: "tab",
|
|
198
|
+
selectionConfirmed: false
|
|
196
199
|
});
|
|
197
200
|
} else {
|
|
198
201
|
event.preventDefault();
|
|
@@ -231,7 +234,8 @@ const SelectList = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
231
234
|
id: childIds ? childIds[currentOptionsListIndex] : /* istanbul ignore next */undefined,
|
|
232
235
|
text,
|
|
233
236
|
value,
|
|
234
|
-
selectionType: "enterKey"
|
|
237
|
+
selectionType: "enterKey",
|
|
238
|
+
selectionConfirmed: true
|
|
235
239
|
});
|
|
236
240
|
} else if ((0, _isNavigationKey.default)(key)) {
|
|
237
241
|
focusOnAnchor();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CommonTextboxProps } from "../../textbox";
|
|
3
3
|
import { ValidationProps } from "../../../__internal__/validations";
|
|
4
|
-
|
|
4
|
+
import { CustomSelectChangeEvent } from "../simple-select/simple-select.component";
|
|
5
|
+
export interface FormInputPropTypes extends ValidationProps, Omit<CommonTextboxProps, "onClick" | "onChange"> {
|
|
5
6
|
/** Breakpoint for adaptive label (inline labels change to top aligned). Enables the adaptive behaviour when set */
|
|
6
7
|
adaptiveLabelBreakpoint?: number;
|
|
7
8
|
/** Prop to specify the aria-label attribute of the component input */
|
|
@@ -29,7 +30,7 @@ export interface FormInputPropTypes extends ValidationProps, Omit<CommonTextboxP
|
|
|
29
30
|
/** Specify a callback triggered on blur */
|
|
30
31
|
onBlur?: (ev: React.FocusEvent<HTMLInputElement>) => void;
|
|
31
32
|
/** Specify a callback triggered on change */
|
|
32
|
-
onChange?: (ev: React.ChangeEvent<HTMLInputElement>) => void;
|
|
33
|
+
onChange?: (ev: CustomSelectChangeEvent | React.ChangeEvent<HTMLInputElement>) => void;
|
|
33
34
|
/** Specify a callback triggered on click */
|
|
34
35
|
onClick?: (ev: React.MouseEvent<HTMLInputElement>) => void;
|
|
35
36
|
/** Specify a callback triggered on focus */
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from "./simple-select.component";
|
|
2
|
-
export type { SimpleSelectProps } from "./simple-select.component";
|
|
2
|
+
export type { SimpleSelectProps, CustomSelectChangeEvent, } from "./simple-select.component";
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Side } from "@floating-ui/dom";
|
|
3
3
|
import { FormInputPropTypes } from "../select-textbox";
|
|
4
|
+
export interface OptionData {
|
|
5
|
+
text?: string;
|
|
6
|
+
value?: string | Record<string, unknown>;
|
|
7
|
+
id?: string;
|
|
8
|
+
selectionType: string;
|
|
9
|
+
selectionConfirmed?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface CustomSelectChangeEvent extends React.ChangeEvent<HTMLInputElement> {
|
|
12
|
+
selectionConfirmed?: boolean;
|
|
13
|
+
}
|
|
4
14
|
export interface SimpleSelectProps extends Omit<FormInputPropTypes, "defaultValue" | "value"> {
|
|
5
15
|
/** Prop to specify the aria-label attribute of the component input */
|
|
6
16
|
"aria-label"?: string;
|
|
@@ -91,7 +91,7 @@ const SimpleSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
91
91
|
_logger.default.deprecate("Uncontrolled behaviour in `Simple Select` is deprecated and support will soon be removed. Please make sure all your inputs are controlled.");
|
|
92
92
|
}
|
|
93
93
|
const childOptions = (0, _react.useMemo)(() => _react.default.Children.toArray(children), [children]);
|
|
94
|
-
const createCustomEvent = (0, _react.useCallback)(newValue => {
|
|
94
|
+
const createCustomEvent = (0, _react.useCallback)((newValue, selectionConfirmed = false) => {
|
|
95
95
|
const customEvent = {
|
|
96
96
|
target: {
|
|
97
97
|
...(name && {
|
|
@@ -101,7 +101,8 @@ const SimpleSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
101
101
|
id
|
|
102
102
|
}),
|
|
103
103
|
value: newValue
|
|
104
|
-
}
|
|
104
|
+
},
|
|
105
|
+
selectionConfirmed
|
|
105
106
|
};
|
|
106
107
|
return customEvent;
|
|
107
108
|
}, [name, id]);
|
|
@@ -254,13 +255,13 @@ const SimpleSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
254
255
|
});
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
|
-
function updateValue(newValue, text) {
|
|
258
|
+
function updateValue(newValue, text, selectionConfirmed) {
|
|
258
259
|
if (!isControlled.current) {
|
|
259
260
|
setSelectedValue(newValue);
|
|
260
261
|
setTextValue(text);
|
|
261
262
|
}
|
|
262
263
|
if (onChange) {
|
|
263
|
-
onChange(createCustomEvent(newValue));
|
|
264
|
+
onChange(createCustomEvent(newValue, selectionConfirmed));
|
|
264
265
|
}
|
|
265
266
|
}
|
|
266
267
|
function onSelectOption(optionData) {
|
|
@@ -268,10 +269,11 @@ const SimpleSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
268
269
|
text,
|
|
269
270
|
value: newValue,
|
|
270
271
|
selectionType,
|
|
271
|
-
id: selectedOptionId
|
|
272
|
+
id: selectedOptionId,
|
|
273
|
+
selectionConfirmed
|
|
272
274
|
} = optionData;
|
|
273
275
|
const isClickTriggered = selectionType === "click";
|
|
274
|
-
updateValue(newValue, text);
|
|
276
|
+
updateValue(newValue, text, selectionConfirmed);
|
|
275
277
|
setActiveDescendantId(selectedOptionId);
|
|
276
278
|
if (selectionType !== "navigationKey") {
|
|
277
279
|
setOpenState(false);
|