carbon-react 144.9.3 → 144.9.5
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/navigation-bar/navigation-bar.style.js +1 -1
- package/esm/components/select/multi-select/multi-select.component.js +108 -46
- package/lib/components/navigation-bar/navigation-bar.style.js +1 -1
- package/lib/components/select/multi-select/multi-select.component.js +108 -46
- package/package.json +6 -7
|
@@ -63,16 +63,18 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
63
63
|
const accessibilityLabelId = useRef(guid());
|
|
64
64
|
const containerRef = useRef(null);
|
|
65
65
|
const listboxRef = useRef(null);
|
|
66
|
+
const isInputFocused = useRef(false);
|
|
66
67
|
const isClickTriggeredBySelect = useRef(false);
|
|
67
68
|
const isMouseDownReported = useRef(false);
|
|
68
69
|
const isMouseDownOnInput = useRef(false);
|
|
69
|
-
const
|
|
70
|
+
const isOpenedByFocus = useRef(false);
|
|
70
71
|
const isControlled = useRef(value !== undefined);
|
|
71
72
|
const [textboxRef, setTextboxRef] = useState();
|
|
72
|
-
const [
|
|
73
|
+
const [isOpen, setOpenState] = useState(false);
|
|
73
74
|
const [textValue, setTextValue] = useState("");
|
|
74
75
|
const [selectedValue, setSelectedValue] = useState(value || defaultValue || []);
|
|
75
76
|
const [highlightedValue, setHighlightedValue] = useState("");
|
|
77
|
+
const [filterText, setFilterText] = useState("");
|
|
76
78
|
const [placeholderOverride, setPlaceholderOverride] = useState();
|
|
77
79
|
const inputId = useRef(id || guid());
|
|
78
80
|
const {
|
|
@@ -81,12 +83,21 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
81
83
|
id: inputId.current,
|
|
82
84
|
label
|
|
83
85
|
});
|
|
86
|
+
const focusTimer = useRef(null);
|
|
84
87
|
const actualValue = isControlled.current ? value : selectedValue;
|
|
85
88
|
const componentIsUncontrolled = !isControlled || !onChange && defaultValue;
|
|
86
89
|
if (!deprecateUncontrolledWarnTriggered && componentIsUncontrolled) {
|
|
87
90
|
deprecateUncontrolledWarnTriggered = true;
|
|
88
91
|
Logger.deprecate("Uncontrolled behaviour in `Multi Select` is deprecated and support will soon be removed. Please make sure all your inputs are controlled.");
|
|
89
92
|
}
|
|
93
|
+
const setOpen = useCallback(() => {
|
|
94
|
+
setOpenState(isAlreadyOpen => {
|
|
95
|
+
if (!isAlreadyOpen && onOpen) {
|
|
96
|
+
onOpen();
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
});
|
|
100
|
+
}, [onOpen]);
|
|
90
101
|
const createCustomEvent = useCallback((newValue, selectionConfirmed) => {
|
|
91
102
|
const customEvent = {
|
|
92
103
|
target: {
|
|
@@ -133,12 +144,10 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
133
144
|
if (match) {
|
|
134
145
|
setHighlightedValue(match.props.value);
|
|
135
146
|
}
|
|
147
|
+
setFilterText(newValue);
|
|
136
148
|
setTextValue(newValue);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
setOpen(true);
|
|
141
|
-
}, [children, open, onOpen]);
|
|
149
|
+
setOpen();
|
|
150
|
+
}, [children, setOpen]);
|
|
142
151
|
const removeSelectedValue = useCallback(index => {
|
|
143
152
|
isClickTriggeredBySelect.current = true;
|
|
144
153
|
updateValue(previousValue => {
|
|
@@ -155,21 +164,21 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
155
164
|
key
|
|
156
165
|
} = event;
|
|
157
166
|
const isDeleteKey = key === "Backspace" || key === "Delete";
|
|
158
|
-
onKeyDown
|
|
167
|
+
if (onKeyDown) {
|
|
168
|
+
onKeyDown(event);
|
|
169
|
+
}
|
|
159
170
|
if (readOnly) {
|
|
160
171
|
return;
|
|
161
172
|
}
|
|
162
173
|
if (!event.defaultPrevented && isNavigationKey(key)) {
|
|
163
174
|
event.preventDefault();
|
|
164
|
-
|
|
165
|
-
onOpen?.();
|
|
166
|
-
}
|
|
167
|
-
setOpen(true);
|
|
175
|
+
setOpen();
|
|
168
176
|
}
|
|
169
|
-
if (isDeleteKey && textValue === "") {
|
|
177
|
+
if (isDeleteKey && (filterText === "" || textValue === "")) {
|
|
170
178
|
removeSelectedValue(-1);
|
|
171
179
|
}
|
|
172
|
-
|
|
180
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
181
|
+
}, [onKeyDown, readOnly, filterText, textValue, setOpen, removeSelectedValue]);
|
|
173
182
|
const accessibilityLabel = useMemo(() => {
|
|
174
183
|
return actualValue && actualValue.length ? React.Children.map(children, child => {
|
|
175
184
|
return /*#__PURE__*/React.isValidElement(child) && actualValue.includes(child.props.value) ? child.props.text : false;
|
|
@@ -179,18 +188,19 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
179
188
|
}, [children, actualValue]);
|
|
180
189
|
const handleGlobalClick = useCallback(event => {
|
|
181
190
|
isMouseDownReported.current = false;
|
|
182
|
-
if (!
|
|
191
|
+
if (!isOpen) {
|
|
183
192
|
return;
|
|
184
193
|
}
|
|
185
194
|
const notInContainer = containerRef.current && !containerRef.current.contains(event.target);
|
|
186
195
|
const notInList = listboxRef.current && !listboxRef.current.contains(event.target);
|
|
187
196
|
if (notInContainer && notInList && !isClickTriggeredBySelect.current) {
|
|
188
197
|
setTextValue("");
|
|
198
|
+
setFilterText("");
|
|
189
199
|
setHighlightedValue("");
|
|
190
|
-
|
|
200
|
+
setOpenState(false);
|
|
191
201
|
}
|
|
192
202
|
isClickTriggeredBySelect.current = false;
|
|
193
|
-
}, [
|
|
203
|
+
}, [isOpen]);
|
|
194
204
|
const mapValuesToPills = useMemo(() => {
|
|
195
205
|
const canDelete = !disabled && !readOnly;
|
|
196
206
|
let matchingOptionValue;
|
|
@@ -251,53 +261,103 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
251
261
|
const onFilterChange = useStableCallback(onFilterChangeProp);
|
|
252
262
|
const isFirstRender = useRef(true);
|
|
253
263
|
useEffect(() => {
|
|
254
|
-
if (!isFirstRender.current) {
|
|
255
|
-
onFilterChange
|
|
264
|
+
if (onFilterChange && !isFirstRender.current) {
|
|
265
|
+
onFilterChange(filterText);
|
|
256
266
|
}
|
|
257
|
-
}, [onFilterChange,
|
|
267
|
+
}, [onFilterChange, filterText]);
|
|
258
268
|
useEffect(() => {
|
|
259
269
|
isFirstRender.current = false;
|
|
260
270
|
}, []);
|
|
261
271
|
function handleTextboxClick(event) {
|
|
262
272
|
isMouseDownReported.current = false;
|
|
263
|
-
onClick
|
|
264
|
-
|
|
265
|
-
setIsOpenedByFocus(false);
|
|
266
|
-
return;
|
|
273
|
+
if (onClick) {
|
|
274
|
+
onClick(event);
|
|
267
275
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
276
|
+
|
|
277
|
+
/* istanbul ignore else */
|
|
278
|
+
if (!openOnFocus || openOnFocus && !isOpenedByFocus.current) {
|
|
279
|
+
if (isOpen) {
|
|
280
|
+
setFilterText("");
|
|
281
|
+
setOpenState(false);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
272
284
|
onOpen?.();
|
|
273
|
-
|
|
285
|
+
setOpenState(true);
|
|
286
|
+
} else {
|
|
287
|
+
// This is tested in Playwright.
|
|
288
|
+
// This line of code is triggered when a user initially clicks on the input when `openOnFocus` is true.
|
|
289
|
+
isOpenedByFocus.current = false;
|
|
274
290
|
}
|
|
275
291
|
}
|
|
292
|
+
function handleDropdownIconClick(event) {
|
|
293
|
+
isMouseDownReported.current = false;
|
|
294
|
+
if (onClick) {
|
|
295
|
+
onClick(event);
|
|
296
|
+
}
|
|
297
|
+
setOpenState(isAlreadyOpen => {
|
|
298
|
+
if (isAlreadyOpen) {
|
|
299
|
+
setFilterText("");
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
if (onOpen) {
|
|
303
|
+
onOpen();
|
|
304
|
+
}
|
|
305
|
+
return true;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
276
308
|
function handleTextboxBlur(event) {
|
|
277
309
|
isMouseDownOnInput.current = false;
|
|
278
310
|
if (isMouseDownReported.current) {
|
|
279
311
|
return;
|
|
280
312
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
313
|
+
isInputFocused.current = false;
|
|
314
|
+
if (onBlur) {
|
|
315
|
+
onBlur(event);
|
|
316
|
+
}
|
|
284
317
|
}
|
|
285
|
-
function handleTextboxMouseDown() {
|
|
318
|
+
function handleTextboxMouseDown(event) {
|
|
286
319
|
isMouseDownReported.current = true;
|
|
287
|
-
|
|
320
|
+
if (event.target.dataset.element === "input") {
|
|
321
|
+
isMouseDownOnInput.current = true;
|
|
322
|
+
}
|
|
288
323
|
}
|
|
289
324
|
function handleListMouseDown() {
|
|
290
325
|
isMouseDownReported.current = true;
|
|
291
326
|
}
|
|
292
327
|
function handleTextboxFocus(event) {
|
|
293
|
-
onFocus?.(event);
|
|
328
|
+
const triggerFocus = () => onFocus?.(event);
|
|
294
329
|
if (openOnFocus) {
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
if (isMouseDownOnInput.current) {
|
|
298
|
-
setIsOpenedByFocus(true);
|
|
330
|
+
if (focusTimer.current) {
|
|
331
|
+
clearTimeout(focusTimer.current);
|
|
299
332
|
}
|
|
300
|
-
|
|
333
|
+
|
|
334
|
+
// we need to use a timeout here as there is a race condition when rendered in a modal
|
|
335
|
+
// whereby the select list isn't visible when the select is auto focused straight away
|
|
336
|
+
focusTimer.current = setTimeout(() => {
|
|
337
|
+
setOpenState(isAlreadyOpen => {
|
|
338
|
+
if (isAlreadyOpen) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
if (onOpen) {
|
|
342
|
+
onOpen();
|
|
343
|
+
}
|
|
344
|
+
if (onFocus && !isInputFocused.current) {
|
|
345
|
+
triggerFocus();
|
|
346
|
+
isInputFocused.current = true;
|
|
347
|
+
}
|
|
348
|
+
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
349
|
+
isOpenedByFocus.current = false;
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
if (isMouseDownOnInput.current) {
|
|
353
|
+
isOpenedByFocus.current = true;
|
|
354
|
+
}
|
|
355
|
+
return true;
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
} else if (onFocus && !isInputFocused.current) {
|
|
359
|
+
triggerFocus();
|
|
360
|
+
isInputFocused.current = true;
|
|
301
361
|
}
|
|
302
362
|
}
|
|
303
363
|
const onSelectOption = useCallback(optionData => {
|
|
@@ -327,8 +387,8 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
327
387
|
}, selectionConfirmed);
|
|
328
388
|
}, [textboxRef, actualValue, updateValue]);
|
|
329
389
|
const onSelectListClose = useCallback(() => {
|
|
330
|
-
|
|
331
|
-
|
|
390
|
+
setOpenState(false);
|
|
391
|
+
setFilterText("");
|
|
332
392
|
}, []);
|
|
333
393
|
const assignInput = useCallback(element => {
|
|
334
394
|
if (!element) return;
|
|
@@ -355,6 +415,8 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
355
415
|
onMouseDown: handleTextboxMouseDown,
|
|
356
416
|
onFocus: handleTextboxFocus,
|
|
357
417
|
onBlur: handleTextboxBlur,
|
|
418
|
+
iconOnClick: handleDropdownIconClick,
|
|
419
|
+
iconOnMouseDown: handleTextboxMouseDown,
|
|
358
420
|
onKeyDown: handleTextboxKeydown,
|
|
359
421
|
onChange: handleTextboxChange,
|
|
360
422
|
tooltipPosition,
|
|
@@ -383,7 +445,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
383
445
|
onSelect: onSelectOption,
|
|
384
446
|
onSelectListClose: onSelectListClose,
|
|
385
447
|
onMouseDown: handleListMouseDown,
|
|
386
|
-
filterText:
|
|
448
|
+
filterText: filterText.trim(),
|
|
387
449
|
highlightedValue: highlightedValue,
|
|
388
450
|
noResultsMessage: noResultsMessage,
|
|
389
451
|
isLoading: isLoading,
|
|
@@ -393,7 +455,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
393
455
|
listMaxHeight: listMaxHeight,
|
|
394
456
|
flipEnabled: flipEnabled,
|
|
395
457
|
multiselectValues: actualValue,
|
|
396
|
-
isOpen:
|
|
458
|
+
isOpen: isOpen,
|
|
397
459
|
enableVirtualScroll: enableVirtualScroll,
|
|
398
460
|
virtualScrollOverscan: virtualScrollOverscan,
|
|
399
461
|
listWidth: listWidth
|
|
@@ -407,7 +469,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
407
469
|
"data-component": dataComponent,
|
|
408
470
|
"data-role": dataRole,
|
|
409
471
|
"data-element": dataElement,
|
|
410
|
-
isOpen:
|
|
472
|
+
isOpen: isOpen
|
|
411
473
|
}, marginProps), /*#__PURE__*/React.createElement("div", {
|
|
412
474
|
ref: containerRef
|
|
413
475
|
}, /*#__PURE__*/React.createElement(StyledAccessibilityLabelContainer, {
|
|
@@ -421,7 +483,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
421
483
|
ariaLabel: ariaLabel,
|
|
422
484
|
ariaLabelledby: ariaLabelledby,
|
|
423
485
|
hasTextCursor: true,
|
|
424
|
-
isOpen:
|
|
486
|
+
isOpen: isOpen,
|
|
425
487
|
labelId: labelId
|
|
426
488
|
}, getTextboxProps()))), selectList);
|
|
427
489
|
});
|
|
@@ -72,16 +72,18 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
72
72
|
const accessibilityLabelId = (0, _react.useRef)((0, _guid.default)());
|
|
73
73
|
const containerRef = (0, _react.useRef)(null);
|
|
74
74
|
const listboxRef = (0, _react.useRef)(null);
|
|
75
|
+
const isInputFocused = (0, _react.useRef)(false);
|
|
75
76
|
const isClickTriggeredBySelect = (0, _react.useRef)(false);
|
|
76
77
|
const isMouseDownReported = (0, _react.useRef)(false);
|
|
77
78
|
const isMouseDownOnInput = (0, _react.useRef)(false);
|
|
78
|
-
const
|
|
79
|
+
const isOpenedByFocus = (0, _react.useRef)(false);
|
|
79
80
|
const isControlled = (0, _react.useRef)(value !== undefined);
|
|
80
81
|
const [textboxRef, setTextboxRef] = (0, _react.useState)();
|
|
81
|
-
const [
|
|
82
|
+
const [isOpen, setOpenState] = (0, _react.useState)(false);
|
|
82
83
|
const [textValue, setTextValue] = (0, _react.useState)("");
|
|
83
84
|
const [selectedValue, setSelectedValue] = (0, _react.useState)(value || defaultValue || []);
|
|
84
85
|
const [highlightedValue, setHighlightedValue] = (0, _react.useState)("");
|
|
86
|
+
const [filterText, setFilterText] = (0, _react.useState)("");
|
|
85
87
|
const [placeholderOverride, setPlaceholderOverride] = (0, _react.useState)();
|
|
86
88
|
const inputId = (0, _react.useRef)(id || (0, _guid.default)());
|
|
87
89
|
const {
|
|
@@ -90,12 +92,21 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
90
92
|
id: inputId.current,
|
|
91
93
|
label
|
|
92
94
|
});
|
|
95
|
+
const focusTimer = (0, _react.useRef)(null);
|
|
93
96
|
const actualValue = isControlled.current ? value : selectedValue;
|
|
94
97
|
const componentIsUncontrolled = !isControlled || !onChange && defaultValue;
|
|
95
98
|
if (!deprecateUncontrolledWarnTriggered && componentIsUncontrolled) {
|
|
96
99
|
deprecateUncontrolledWarnTriggered = true;
|
|
97
100
|
_logger.default.deprecate("Uncontrolled behaviour in `Multi Select` is deprecated and support will soon be removed. Please make sure all your inputs are controlled.");
|
|
98
101
|
}
|
|
102
|
+
const setOpen = (0, _react.useCallback)(() => {
|
|
103
|
+
setOpenState(isAlreadyOpen => {
|
|
104
|
+
if (!isAlreadyOpen && onOpen) {
|
|
105
|
+
onOpen();
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
});
|
|
109
|
+
}, [onOpen]);
|
|
99
110
|
const createCustomEvent = (0, _react.useCallback)((newValue, selectionConfirmed) => {
|
|
100
111
|
const customEvent = {
|
|
101
112
|
target: {
|
|
@@ -142,12 +153,10 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
142
153
|
if (match) {
|
|
143
154
|
setHighlightedValue(match.props.value);
|
|
144
155
|
}
|
|
156
|
+
setFilterText(newValue);
|
|
145
157
|
setTextValue(newValue);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
setOpen(true);
|
|
150
|
-
}, [children, open, onOpen]);
|
|
158
|
+
setOpen();
|
|
159
|
+
}, [children, setOpen]);
|
|
151
160
|
const removeSelectedValue = (0, _react.useCallback)(index => {
|
|
152
161
|
isClickTriggeredBySelect.current = true;
|
|
153
162
|
updateValue(previousValue => {
|
|
@@ -164,21 +173,21 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
164
173
|
key
|
|
165
174
|
} = event;
|
|
166
175
|
const isDeleteKey = key === "Backspace" || key === "Delete";
|
|
167
|
-
onKeyDown
|
|
176
|
+
if (onKeyDown) {
|
|
177
|
+
onKeyDown(event);
|
|
178
|
+
}
|
|
168
179
|
if (readOnly) {
|
|
169
180
|
return;
|
|
170
181
|
}
|
|
171
182
|
if (!event.defaultPrevented && (0, _isNavigationKey.default)(key)) {
|
|
172
183
|
event.preventDefault();
|
|
173
|
-
|
|
174
|
-
onOpen?.();
|
|
175
|
-
}
|
|
176
|
-
setOpen(true);
|
|
184
|
+
setOpen();
|
|
177
185
|
}
|
|
178
|
-
if (isDeleteKey && textValue === "") {
|
|
186
|
+
if (isDeleteKey && (filterText === "" || textValue === "")) {
|
|
179
187
|
removeSelectedValue(-1);
|
|
180
188
|
}
|
|
181
|
-
|
|
189
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
190
|
+
}, [onKeyDown, readOnly, filterText, textValue, setOpen, removeSelectedValue]);
|
|
182
191
|
const accessibilityLabel = (0, _react.useMemo)(() => {
|
|
183
192
|
return actualValue && actualValue.length ? _react.default.Children.map(children, child => {
|
|
184
193
|
return /*#__PURE__*/_react.default.isValidElement(child) && actualValue.includes(child.props.value) ? child.props.text : false;
|
|
@@ -188,18 +197,19 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
188
197
|
}, [children, actualValue]);
|
|
189
198
|
const handleGlobalClick = (0, _react.useCallback)(event => {
|
|
190
199
|
isMouseDownReported.current = false;
|
|
191
|
-
if (!
|
|
200
|
+
if (!isOpen) {
|
|
192
201
|
return;
|
|
193
202
|
}
|
|
194
203
|
const notInContainer = containerRef.current && !containerRef.current.contains(event.target);
|
|
195
204
|
const notInList = listboxRef.current && !listboxRef.current.contains(event.target);
|
|
196
205
|
if (notInContainer && notInList && !isClickTriggeredBySelect.current) {
|
|
197
206
|
setTextValue("");
|
|
207
|
+
setFilterText("");
|
|
198
208
|
setHighlightedValue("");
|
|
199
|
-
|
|
209
|
+
setOpenState(false);
|
|
200
210
|
}
|
|
201
211
|
isClickTriggeredBySelect.current = false;
|
|
202
|
-
}, [
|
|
212
|
+
}, [isOpen]);
|
|
203
213
|
const mapValuesToPills = (0, _react.useMemo)(() => {
|
|
204
214
|
const canDelete = !disabled && !readOnly;
|
|
205
215
|
let matchingOptionValue;
|
|
@@ -260,53 +270,103 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
260
270
|
const onFilterChange = (0, _useStableCallback.default)(onFilterChangeProp);
|
|
261
271
|
const isFirstRender = (0, _react.useRef)(true);
|
|
262
272
|
(0, _react.useEffect)(() => {
|
|
263
|
-
if (!isFirstRender.current) {
|
|
264
|
-
onFilterChange
|
|
273
|
+
if (onFilterChange && !isFirstRender.current) {
|
|
274
|
+
onFilterChange(filterText);
|
|
265
275
|
}
|
|
266
|
-
}, [onFilterChange,
|
|
276
|
+
}, [onFilterChange, filterText]);
|
|
267
277
|
(0, _react.useEffect)(() => {
|
|
268
278
|
isFirstRender.current = false;
|
|
269
279
|
}, []);
|
|
270
280
|
function handleTextboxClick(event) {
|
|
271
281
|
isMouseDownReported.current = false;
|
|
272
|
-
onClick
|
|
273
|
-
|
|
274
|
-
setIsOpenedByFocus(false);
|
|
275
|
-
return;
|
|
282
|
+
if (onClick) {
|
|
283
|
+
onClick(event);
|
|
276
284
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
285
|
+
|
|
286
|
+
/* istanbul ignore else */
|
|
287
|
+
if (!openOnFocus || openOnFocus && !isOpenedByFocus.current) {
|
|
288
|
+
if (isOpen) {
|
|
289
|
+
setFilterText("");
|
|
290
|
+
setOpenState(false);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
281
293
|
onOpen?.();
|
|
282
|
-
|
|
294
|
+
setOpenState(true);
|
|
295
|
+
} else {
|
|
296
|
+
// This is tested in Playwright.
|
|
297
|
+
// This line of code is triggered when a user initially clicks on the input when `openOnFocus` is true.
|
|
298
|
+
isOpenedByFocus.current = false;
|
|
283
299
|
}
|
|
284
300
|
}
|
|
301
|
+
function handleDropdownIconClick(event) {
|
|
302
|
+
isMouseDownReported.current = false;
|
|
303
|
+
if (onClick) {
|
|
304
|
+
onClick(event);
|
|
305
|
+
}
|
|
306
|
+
setOpenState(isAlreadyOpen => {
|
|
307
|
+
if (isAlreadyOpen) {
|
|
308
|
+
setFilterText("");
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
if (onOpen) {
|
|
312
|
+
onOpen();
|
|
313
|
+
}
|
|
314
|
+
return true;
|
|
315
|
+
});
|
|
316
|
+
}
|
|
285
317
|
function handleTextboxBlur(event) {
|
|
286
318
|
isMouseDownOnInput.current = false;
|
|
287
319
|
if (isMouseDownReported.current) {
|
|
288
320
|
return;
|
|
289
321
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
322
|
+
isInputFocused.current = false;
|
|
323
|
+
if (onBlur) {
|
|
324
|
+
onBlur(event);
|
|
325
|
+
}
|
|
293
326
|
}
|
|
294
|
-
function handleTextboxMouseDown() {
|
|
327
|
+
function handleTextboxMouseDown(event) {
|
|
295
328
|
isMouseDownReported.current = true;
|
|
296
|
-
|
|
329
|
+
if (event.target.dataset.element === "input") {
|
|
330
|
+
isMouseDownOnInput.current = true;
|
|
331
|
+
}
|
|
297
332
|
}
|
|
298
333
|
function handleListMouseDown() {
|
|
299
334
|
isMouseDownReported.current = true;
|
|
300
335
|
}
|
|
301
336
|
function handleTextboxFocus(event) {
|
|
302
|
-
onFocus?.(event);
|
|
337
|
+
const triggerFocus = () => onFocus?.(event);
|
|
303
338
|
if (openOnFocus) {
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
if (isMouseDownOnInput.current) {
|
|
307
|
-
setIsOpenedByFocus(true);
|
|
339
|
+
if (focusTimer.current) {
|
|
340
|
+
clearTimeout(focusTimer.current);
|
|
308
341
|
}
|
|
309
|
-
|
|
342
|
+
|
|
343
|
+
// we need to use a timeout here as there is a race condition when rendered in a modal
|
|
344
|
+
// whereby the select list isn't visible when the select is auto focused straight away
|
|
345
|
+
focusTimer.current = setTimeout(() => {
|
|
346
|
+
setOpenState(isAlreadyOpen => {
|
|
347
|
+
if (isAlreadyOpen) {
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
if (onOpen) {
|
|
351
|
+
onOpen();
|
|
352
|
+
}
|
|
353
|
+
if (onFocus && !isInputFocused.current) {
|
|
354
|
+
triggerFocus();
|
|
355
|
+
isInputFocused.current = true;
|
|
356
|
+
}
|
|
357
|
+
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
358
|
+
isOpenedByFocus.current = false;
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
if (isMouseDownOnInput.current) {
|
|
362
|
+
isOpenedByFocus.current = true;
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
} else if (onFocus && !isInputFocused.current) {
|
|
368
|
+
triggerFocus();
|
|
369
|
+
isInputFocused.current = true;
|
|
310
370
|
}
|
|
311
371
|
}
|
|
312
372
|
const onSelectOption = (0, _react.useCallback)(optionData => {
|
|
@@ -336,8 +396,8 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
336
396
|
}, selectionConfirmed);
|
|
337
397
|
}, [textboxRef, actualValue, updateValue]);
|
|
338
398
|
const onSelectListClose = (0, _react.useCallback)(() => {
|
|
339
|
-
|
|
340
|
-
|
|
399
|
+
setOpenState(false);
|
|
400
|
+
setFilterText("");
|
|
341
401
|
}, []);
|
|
342
402
|
const assignInput = (0, _react.useCallback)(element => {
|
|
343
403
|
if (!element) return;
|
|
@@ -364,6 +424,8 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
364
424
|
onMouseDown: handleTextboxMouseDown,
|
|
365
425
|
onFocus: handleTextboxFocus,
|
|
366
426
|
onBlur: handleTextboxBlur,
|
|
427
|
+
iconOnClick: handleDropdownIconClick,
|
|
428
|
+
iconOnMouseDown: handleTextboxMouseDown,
|
|
367
429
|
onKeyDown: handleTextboxKeydown,
|
|
368
430
|
onChange: handleTextboxChange,
|
|
369
431
|
tooltipPosition,
|
|
@@ -392,7 +454,7 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
392
454
|
onSelect: onSelectOption,
|
|
393
455
|
onSelectListClose: onSelectListClose,
|
|
394
456
|
onMouseDown: handleListMouseDown,
|
|
395
|
-
filterText:
|
|
457
|
+
filterText: filterText.trim(),
|
|
396
458
|
highlightedValue: highlightedValue,
|
|
397
459
|
noResultsMessage: noResultsMessage,
|
|
398
460
|
isLoading: isLoading,
|
|
@@ -402,7 +464,7 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
402
464
|
listMaxHeight: listMaxHeight,
|
|
403
465
|
flipEnabled: flipEnabled,
|
|
404
466
|
multiselectValues: actualValue,
|
|
405
|
-
isOpen:
|
|
467
|
+
isOpen: isOpen,
|
|
406
468
|
enableVirtualScroll: enableVirtualScroll,
|
|
407
469
|
virtualScrollOverscan: virtualScrollOverscan,
|
|
408
470
|
listWidth: listWidth
|
|
@@ -416,7 +478,7 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
416
478
|
"data-component": dataComponent,
|
|
417
479
|
"data-role": dataRole,
|
|
418
480
|
"data-element": dataElement,
|
|
419
|
-
isOpen:
|
|
481
|
+
isOpen: isOpen
|
|
420
482
|
}, marginProps), /*#__PURE__*/_react.default.createElement("div", {
|
|
421
483
|
ref: containerRef
|
|
422
484
|
}, /*#__PURE__*/_react.default.createElement(_multiSelect.StyledAccessibilityLabelContainer, {
|
|
@@ -430,7 +492,7 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
|
|
|
430
492
|
ariaLabel: ariaLabel,
|
|
431
493
|
ariaLabelledby: ariaLabelledby,
|
|
432
494
|
hasTextCursor: true,
|
|
433
|
-
isOpen:
|
|
495
|
+
isOpen: isOpen,
|
|
434
496
|
labelId: labelId
|
|
435
497
|
}, getTextboxProps()))), selectList);
|
|
436
498
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carbon-react",
|
|
3
|
-
"version": "144.9.
|
|
3
|
+
"version": "144.9.5",
|
|
4
4
|
"description": "A library of reusable React components for easily building user interfaces.",
|
|
5
5
|
"files": [
|
|
6
6
|
"lib",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"scripts/check_rfcs/index.js"
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
|
-
"start": "node ./scripts/check_node_version.mjs &&
|
|
13
|
-
"start:debug-theme": "
|
|
12
|
+
"start": "node ./scripts/check_node_version.mjs && storybook dev -p 9001 -c .storybook",
|
|
13
|
+
"start:debug-theme": "node ./scripts/debug-storybook.js",
|
|
14
14
|
"test": "jest --config=./jest.config.ts",
|
|
15
15
|
"test-update": "jest --config=./jest.config.ts --updateSnapshot",
|
|
16
16
|
"format": "prettier --write './{src,playwright}/**/*.{js,jsx,ts,tsx}'",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"prepublishOnly": "npm run precompile",
|
|
22
22
|
"postinstall": "node ./scripts/check_rfcs/index.js",
|
|
23
23
|
"watch": "npm run clean-lib && npm run copy-files -- --watch & npm run babel -- --watch",
|
|
24
|
-
"build-storybook": "
|
|
25
|
-
"start-storybook": "
|
|
24
|
+
"build-storybook": "node ./scripts/build-storybook.js",
|
|
25
|
+
"start-storybook": "node ./scripts/start-storybook.js",
|
|
26
26
|
"start:static": "npx http-server -p 9001 ./storybook-static",
|
|
27
27
|
"clean-lib": "rimraf ./lib && rimraf ./esm",
|
|
28
28
|
"commit": "git-cz",
|
|
@@ -122,11 +122,11 @@
|
|
|
122
122
|
"concurrently": "^8.2.2",
|
|
123
123
|
"conventional-changelog-conventionalcommits": "^4.6.3",
|
|
124
124
|
"core-js": "^3.33.3",
|
|
125
|
-
"cross-env": "^5.2.1",
|
|
126
125
|
"css-loader": "^6.8.1",
|
|
127
126
|
"cz-conventional-changelog": "^3.3.0",
|
|
128
127
|
"date-fns-tz": "^1.3.8",
|
|
129
128
|
"dayjs": "^1.11.10",
|
|
129
|
+
"dotenv": "^16.4.5",
|
|
130
130
|
"draft-js": "^0.11.7",
|
|
131
131
|
"eslint": "^8.55.0",
|
|
132
132
|
"eslint-config-airbnb": "^19.0.0",
|
|
@@ -189,7 +189,6 @@
|
|
|
189
189
|
"classnames": "~2.3.2",
|
|
190
190
|
"crypto-js": "^4.2.0",
|
|
191
191
|
"date-fns": "^2.30.0",
|
|
192
|
-
"dotenv": "^10.0.0",
|
|
193
192
|
"immutable": "~3.8.2",
|
|
194
193
|
"invariant": "^2.2.4",
|
|
195
194
|
"lodash": "^4.17.21",
|