carbon-react 109.3.4 → 109.3.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.
@@ -64,6 +64,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
64
64
  const [highlightedValue, setHighlightedValue] = useState("");
65
65
  const [filterText, setFilterText] = useState("");
66
66
  const [placeholderOverride, setPlaceholderOverride] = useState();
67
+ const actualValue = isControlled.current ? value : selectedValue;
67
68
  const setOpen = useCallback(() => {
68
69
  setOpenState(isAlreadyOpen => {
69
70
  if (!isAlreadyOpen && onOpen) {
@@ -86,6 +87,23 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
86
87
  };
87
88
  return customEvent;
88
89
  }, [name, id]);
90
+ /* generic value update function which can be used for both controlled and uncontrolled
91
+ * components, both with and without onChange.
92
+ * It accepts a function to update the value, which is assumed to be have no side effects and therefore
93
+ * be safe to run more than once if needed. */
94
+
95
+ const updateValue = useCallback(updateFunction => {
96
+ const newValue = updateFunction(actualValue); // only call onChange if an option has been selected or deselected
97
+
98
+ if (onChange && newValue.length !== actualValue.length) {
99
+ onChange(createCustomEvent(newValue));
100
+ } // no need to update selectedValue if the component is controlled: onChange should take care of updating the value
101
+
102
+
103
+ if (!isControlled.current) {
104
+ setSelectedValue(updateFunction);
105
+ }
106
+ }, [createCustomEvent, onChange, actualValue]);
89
107
  const handleTextboxChange = useCallback(event => {
90
108
  const newValue = event.target.value;
91
109
  const match = findElementWithMatchingText(newValue, children);
@@ -99,24 +117,17 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
99
117
  setOpen();
100
118
  }, [children, setOpen]);
101
119
  const removeSelectedValue = useCallback(index => {
102
- setSelectedValue(previousValue => {
103
- isClickTriggeredBySelect.current = true;
104
-
120
+ isClickTriggeredBySelect.current = true;
121
+ updateValue(previousValue => {
105
122
  if (!previousValue.length) {
106
123
  return previousValue;
107
124
  }
108
125
 
109
126
  const newValue = [...previousValue];
110
127
  newValue.splice(index, 1);
111
-
112
- if (onChange) {
113
- onChange(createCustomEvent(newValue));
114
- return newValue;
115
- }
116
-
117
128
  return newValue;
118
129
  });
119
- }, [createCustomEvent, onChange]);
130
+ }, [updateValue]);
120
131
  const handleTextboxKeydown = useCallback(event => {
121
132
  const {
122
133
  key
@@ -142,12 +153,12 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
142
153
 
143
154
  }, [onKeyDown, readOnly, filterText, textValue, setOpen, removeSelectedValue]);
144
155
  const accessibilityLabel = useMemo(() => {
145
- return selectedValue && selectedValue.length ? React.Children.map(children, child => {
146
- return selectedValue.includes(child.props.value) ? child.props.text : false;
156
+ return actualValue && actualValue.length ? React.Children.map(children, child => {
157
+ return actualValue.includes(child.props.value) ? child.props.text : false;
147
158
  }).filter(child => child).reduce((acc, item) => {
148
159
  return acc ? `${acc}, ${item}` : item;
149
160
  }, "") : null;
150
- }, [children, selectedValue]);
161
+ }, [children, actualValue]);
151
162
  const handleGlobalClick = useCallback(event => {
152
163
  isMouseDownReported.current = false;
153
164
 
@@ -170,11 +181,11 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
170
181
  const mapValuesToPills = useMemo(() => {
171
182
  const canDelete = !disabled && !readOnly;
172
183
 
173
- if (!selectedValue.length) {
184
+ if (!actualValue.length) {
174
185
  return "";
175
186
  }
176
187
 
177
- return selectedValue.map((singleValue, index) => {
188
+ return actualValue.map((singleValue, index) => {
178
189
  const matchingOption = React.Children.toArray(children).find(child => isExpectedOption(child, singleValue));
179
190
  let pillProps = {};
180
191
 
@@ -203,22 +214,18 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
203
214
  const onChangeMissingMessage = "onChange prop required when using a controlled input element";
204
215
  !(isControlled.current === (value !== undefined)) ? process.env.NODE_ENV !== "production" ? invariant(false, modeSwitchedMessage) : invariant(false) : void 0;
205
216
  !(!isControlled.current || isControlled.current && onChange) ? process.env.NODE_ENV !== "production" ? invariant(false, onChangeMissingMessage) : invariant(false) : void 0;
206
-
207
- if (isControlled.current) {
208
- setSelectedValue(value);
209
- }
210
217
  }, [value, onChange]); // removes placeholder when a value is present
211
218
 
212
219
  useEffect(() => {
213
220
  const hasValue = value === null || value === void 0 ? void 0 : value.length;
214
- const hasSelectedValue = selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue.length;
221
+ const hasSelectedValue = actualValue === null || actualValue === void 0 ? void 0 : actualValue.length;
215
222
 
216
223
  if (hasValue || hasSelectedValue) {
217
224
  setPlaceholderOverride(" ");
218
225
  } else {
219
226
  setPlaceholderOverride(placeholder);
220
227
  }
221
- }, [value, selectedValue, placeholder]);
228
+ }, [value, actualValue, placeholder]);
222
229
  useEffect(() => {
223
230
  const clickEvent = "click";
224
231
  window.addEventListener(clickEvent, handleGlobalClick);
@@ -335,27 +342,22 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
335
342
  }
336
343
 
337
344
  setTextValue("");
338
- const isAlreadySelected = selectedValue.findIndex(val => isExpectedValue(val, newValue)) !== -1;
345
+ const isAlreadySelected = actualValue.findIndex(val => isExpectedValue(val, newValue)) !== -1;
339
346
 
340
347
  if (!isAlreadySelected && isControlled.current && onChange) {
341
- onChange(createCustomEvent([...selectedValue, newValue]));
348
+ onChange(createCustomEvent([...actualValue, newValue]));
342
349
  }
343
350
 
344
- setSelectedValue(previousValue => {
345
- textboxRef.focus();
346
- isMouseDownReported.current = false;
347
-
351
+ textboxRef.focus();
352
+ isMouseDownReported.current = false;
353
+ updateValue(previousValue => {
348
354
  if (isAlreadySelected) {
349
355
  return previousValue;
350
356
  }
351
357
 
352
- if (onChange) {
353
- onChange(createCustomEvent([...previousValue, newValue]));
354
- }
355
-
356
358
  return [...previousValue, newValue];
357
359
  });
358
- }, [createCustomEvent, onChange, textboxRef, selectedValue]);
360
+ }, [createCustomEvent, onChange, textboxRef, actualValue, updateValue]);
359
361
 
360
362
  function onSelectListClose() {
361
363
  setOpenState(false);
@@ -389,7 +391,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
389
391
  leftChildren: mapValuesToPills,
390
392
  inputRef: assignInput,
391
393
  formattedValue: textValue,
392
- selectedValue,
394
+ selectedValue: actualValue,
393
395
  onClick: handleTextboxClick,
394
396
  onMouseDown: handleTextboxMouseDown,
395
397
  onFocus: handleTextboxFocus,
@@ -423,7 +425,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
423
425
  listPlacement: listPlacement,
424
426
  flipEnabled: flipEnabled,
425
427
  loaderDataRole: "multi-select-list-loader",
426
- multiselectValues: selectedValue
428
+ multiselectValues: actualValue
427
429
  }, children);
428
430
  return /*#__PURE__*/React.createElement(StyledSelectMultiSelect, _extends({
429
431
  disabled: disabled,
@@ -91,6 +91,7 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
91
91
  const [highlightedValue, setHighlightedValue] = (0, _react.useState)("");
92
92
  const [filterText, setFilterText] = (0, _react.useState)("");
93
93
  const [placeholderOverride, setPlaceholderOverride] = (0, _react.useState)();
94
+ const actualValue = isControlled.current ? value : selectedValue;
94
95
  const setOpen = (0, _react.useCallback)(() => {
95
96
  setOpenState(isAlreadyOpen => {
96
97
  if (!isAlreadyOpen && onOpen) {
@@ -113,6 +114,23 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
113
114
  };
114
115
  return customEvent;
115
116
  }, [name, id]);
117
+ /* generic value update function which can be used for both controlled and uncontrolled
118
+ * components, both with and without onChange.
119
+ * It accepts a function to update the value, which is assumed to be have no side effects and therefore
120
+ * be safe to run more than once if needed. */
121
+
122
+ const updateValue = (0, _react.useCallback)(updateFunction => {
123
+ const newValue = updateFunction(actualValue); // only call onChange if an option has been selected or deselected
124
+
125
+ if (onChange && newValue.length !== actualValue.length) {
126
+ onChange(createCustomEvent(newValue));
127
+ } // no need to update selectedValue if the component is controlled: onChange should take care of updating the value
128
+
129
+
130
+ if (!isControlled.current) {
131
+ setSelectedValue(updateFunction);
132
+ }
133
+ }, [createCustomEvent, onChange, actualValue]);
116
134
  const handleTextboxChange = (0, _react.useCallback)(event => {
117
135
  const newValue = event.target.value;
118
136
  const match = findElementWithMatchingText(newValue, children);
@@ -126,24 +144,17 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
126
144
  setOpen();
127
145
  }, [children, setOpen]);
128
146
  const removeSelectedValue = (0, _react.useCallback)(index => {
129
- setSelectedValue(previousValue => {
130
- isClickTriggeredBySelect.current = true;
131
-
147
+ isClickTriggeredBySelect.current = true;
148
+ updateValue(previousValue => {
132
149
  if (!previousValue.length) {
133
150
  return previousValue;
134
151
  }
135
152
 
136
153
  const newValue = [...previousValue];
137
154
  newValue.splice(index, 1);
138
-
139
- if (onChange) {
140
- onChange(createCustomEvent(newValue));
141
- return newValue;
142
- }
143
-
144
155
  return newValue;
145
156
  });
146
- }, [createCustomEvent, onChange]);
157
+ }, [updateValue]);
147
158
  const handleTextboxKeydown = (0, _react.useCallback)(event => {
148
159
  const {
149
160
  key
@@ -169,12 +180,12 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
169
180
 
170
181
  }, [onKeyDown, readOnly, filterText, textValue, setOpen, removeSelectedValue]);
171
182
  const accessibilityLabel = (0, _react.useMemo)(() => {
172
- return selectedValue && selectedValue.length ? _react.default.Children.map(children, child => {
173
- return selectedValue.includes(child.props.value) ? child.props.text : false;
183
+ return actualValue && actualValue.length ? _react.default.Children.map(children, child => {
184
+ return actualValue.includes(child.props.value) ? child.props.text : false;
174
185
  }).filter(child => child).reduce((acc, item) => {
175
186
  return acc ? `${acc}, ${item}` : item;
176
187
  }, "") : null;
177
- }, [children, selectedValue]);
188
+ }, [children, actualValue]);
178
189
  const handleGlobalClick = (0, _react.useCallback)(event => {
179
190
  isMouseDownReported.current = false;
180
191
 
@@ -197,11 +208,11 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
197
208
  const mapValuesToPills = (0, _react.useMemo)(() => {
198
209
  const canDelete = !disabled && !readOnly;
199
210
 
200
- if (!selectedValue.length) {
211
+ if (!actualValue.length) {
201
212
  return "";
202
213
  }
203
214
 
204
- return selectedValue.map((singleValue, index) => {
215
+ return actualValue.map((singleValue, index) => {
205
216
  const matchingOption = _react.default.Children.toArray(children).find(child => (0, _isExpectedOption.default)(child, singleValue));
206
217
 
207
218
  let pillProps = {};
@@ -231,22 +242,18 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
231
242
  const onChangeMissingMessage = "onChange prop required when using a controlled input element";
232
243
  !(isControlled.current === (value !== undefined)) ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, modeSwitchedMessage) : (0, _invariant.default)(false) : void 0;
233
244
  !(!isControlled.current || isControlled.current && onChange) ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, onChangeMissingMessage) : (0, _invariant.default)(false) : void 0;
234
-
235
- if (isControlled.current) {
236
- setSelectedValue(value);
237
- }
238
245
  }, [value, onChange]); // removes placeholder when a value is present
239
246
 
240
247
  (0, _react.useEffect)(() => {
241
248
  const hasValue = value === null || value === void 0 ? void 0 : value.length;
242
- const hasSelectedValue = selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue.length;
249
+ const hasSelectedValue = actualValue === null || actualValue === void 0 ? void 0 : actualValue.length;
243
250
 
244
251
  if (hasValue || hasSelectedValue) {
245
252
  setPlaceholderOverride(" ");
246
253
  } else {
247
254
  setPlaceholderOverride(placeholder);
248
255
  }
249
- }, [value, selectedValue, placeholder]);
256
+ }, [value, actualValue, placeholder]);
250
257
  (0, _react.useEffect)(() => {
251
258
  const clickEvent = "click";
252
259
  window.addEventListener(clickEvent, handleGlobalClick);
@@ -363,27 +370,22 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
363
370
  }
364
371
 
365
372
  setTextValue("");
366
- const isAlreadySelected = selectedValue.findIndex(val => (0, _isExpectedValue.default)(val, newValue)) !== -1;
373
+ const isAlreadySelected = actualValue.findIndex(val => (0, _isExpectedValue.default)(val, newValue)) !== -1;
367
374
 
368
375
  if (!isAlreadySelected && isControlled.current && onChange) {
369
- onChange(createCustomEvent([...selectedValue, newValue]));
376
+ onChange(createCustomEvent([...actualValue, newValue]));
370
377
  }
371
378
 
372
- setSelectedValue(previousValue => {
373
- textboxRef.focus();
374
- isMouseDownReported.current = false;
375
-
379
+ textboxRef.focus();
380
+ isMouseDownReported.current = false;
381
+ updateValue(previousValue => {
376
382
  if (isAlreadySelected) {
377
383
  return previousValue;
378
384
  }
379
385
 
380
- if (onChange) {
381
- onChange(createCustomEvent([...previousValue, newValue]));
382
- }
383
-
384
386
  return [...previousValue, newValue];
385
387
  });
386
- }, [createCustomEvent, onChange, textboxRef, selectedValue]);
388
+ }, [createCustomEvent, onChange, textboxRef, actualValue, updateValue]);
387
389
 
388
390
  function onSelectListClose() {
389
391
  setOpenState(false);
@@ -417,7 +419,7 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
417
419
  leftChildren: mapValuesToPills,
418
420
  inputRef: assignInput,
419
421
  formattedValue: textValue,
420
- selectedValue,
422
+ selectedValue: actualValue,
421
423
  onClick: handleTextboxClick,
422
424
  onMouseDown: handleTextboxMouseDown,
423
425
  onFocus: handleTextboxFocus,
@@ -451,7 +453,7 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
451
453
  listPlacement: listPlacement,
452
454
  flipEnabled: flipEnabled,
453
455
  loaderDataRole: "multi-select-list-loader",
454
- multiselectValues: selectedValue
456
+ multiselectValues: actualValue
455
457
  }, children);
456
458
 
457
459
  return /*#__PURE__*/_react.default.createElement(_multiSelect.StyledSelectMultiSelect, _extends({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "109.3.4",
3
+ "version": "109.3.5",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "engineStrict": true,
6
6
  "engines": {