@carbon/react 1.36.0 → 1.37.0-rc.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.
Files changed (45) hide show
  1. package/es/components/ComboBox/ComboBox.js +2 -4
  2. package/es/components/DataTable/DataTable.d.ts +26 -0
  3. package/es/components/DataTable/DataTable.js +25 -0
  4. package/es/components/DataTable/TableExpandHeader.d.ts +27 -5
  5. package/es/components/DataTable/TableExpandHeader.js +17 -3
  6. package/es/components/DataTable/TableExpandRow.d.ts +24 -3
  7. package/es/components/DataTable/TableExpandRow.js +18 -3
  8. package/es/components/DataTable/TableRow.js +1 -1
  9. package/es/components/Dropdown/Dropdown.d.ts +1 -1
  10. package/es/components/Dropdown/Dropdown.js +40 -40
  11. package/es/components/ListBox/ListBoxMenu.d.ts +2 -2
  12. package/es/components/ListBox/ListBoxMenu.js +1 -1
  13. package/es/components/ListBox/ListBoxMenuItem.d.ts +6 -2
  14. package/es/components/ListBox/ListBoxMenuItem.js +6 -3
  15. package/es/components/MultiSelect/FilterableMultiSelect.js +24 -4
  16. package/es/components/MultiSelect/MultiSelect.js +77 -56
  17. package/es/components/NumberInput/NumberInput.d.ts +1 -1
  18. package/es/components/NumberInput/NumberInput.js +18 -44
  19. package/es/components/Slider/Slider.d.ts +6 -0
  20. package/es/components/Slider/Slider.js +17 -2
  21. package/es/components/TextArea/TextArea.js +1 -1
  22. package/es/components/Tile/Tile.js +5 -5
  23. package/lib/components/ComboBox/ComboBox.js +2 -4
  24. package/lib/components/DataTable/DataTable.d.ts +26 -0
  25. package/lib/components/DataTable/DataTable.js +25 -0
  26. package/lib/components/DataTable/TableExpandHeader.d.ts +27 -5
  27. package/lib/components/DataTable/TableExpandHeader.js +17 -3
  28. package/lib/components/DataTable/TableExpandRow.d.ts +24 -3
  29. package/lib/components/DataTable/TableExpandRow.js +18 -3
  30. package/lib/components/DataTable/TableRow.js +1 -1
  31. package/lib/components/Dropdown/Dropdown.d.ts +1 -1
  32. package/lib/components/Dropdown/Dropdown.js +39 -39
  33. package/lib/components/ListBox/ListBoxMenu.d.ts +2 -2
  34. package/lib/components/ListBox/ListBoxMenu.js +1 -1
  35. package/lib/components/ListBox/ListBoxMenuItem.d.ts +6 -2
  36. package/lib/components/ListBox/ListBoxMenuItem.js +6 -3
  37. package/lib/components/MultiSelect/FilterableMultiSelect.js +24 -4
  38. package/lib/components/MultiSelect/MultiSelect.js +75 -55
  39. package/lib/components/NumberInput/NumberInput.d.ts +1 -1
  40. package/lib/components/NumberInput/NumberInput.js +18 -44
  41. package/lib/components/Slider/Slider.d.ts +6 -0
  42. package/lib/components/Slider/Slider.js +17 -2
  43. package/lib/components/TextArea/TextArea.js +1 -1
  44. package/lib/components/Tile/Tile.js +5 -5
  45. package/package.json +3 -3
@@ -33,7 +33,6 @@ var keys = require('../../internal/keyboard/keys.js');
33
33
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
34
34
 
35
35
  var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx);
36
- var Downshift__default = /*#__PURE__*/_interopDefaultLegacy(Downshift);
37
36
  var isEqual__default = /*#__PURE__*/_interopDefaultLegacy(isEqual);
38
37
  var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
39
38
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
@@ -42,12 +41,16 @@ const noop = () => {};
42
41
  const getInstanceId = setupGetInstanceId["default"]();
43
42
  const {
44
43
  ItemClick,
45
- MenuBlur,
46
- MenuKeyDownArrowDown,
47
- MenuKeyDownArrowUp,
48
- MenuKeyDownEscape,
49
- MenuKeyDownSpaceButton,
50
- ToggleButtonClick
44
+ ToggleButtonBlur,
45
+ ToggleButtonKeyDownArrowDown,
46
+ ToggleButtonKeyDownArrowUp,
47
+ ToggleButtonKeyDownEnter,
48
+ ToggleButtonKeyDownEscape,
49
+ ToggleButtonKeyDownSpaceButton,
50
+ ItemMouseMove,
51
+ ToggleButtonClick,
52
+ ToggleButtonKeyDownHome,
53
+ ToggleButtonKeyDownEnd
51
54
  } = Downshift.useSelect.stateChangeTypes;
52
55
  const defaultItemToString = item => {
53
56
  if (typeof item === 'string') {
@@ -61,7 +64,7 @@ const defaultItemToString = item => {
61
64
  }
62
65
  return '';
63
66
  };
64
- const MultiSelect = /*#__PURE__*/React__default["default"].forwardRef(function MultiSelect(_ref, ref) {
67
+ const MultiSelect = /*#__PURE__*/React__default["default"].forwardRef((_ref, ref) => {
65
68
  let {
66
69
  className: containerClassName,
67
70
  id,
@@ -104,7 +107,6 @@ const MultiSelect = /*#__PURE__*/React__default["default"].forwardRef(function M
104
107
  const {
105
108
  current: multiSelectInstanceId
106
109
  } = React.useRef(getInstanceId());
107
- const [highlightedIndex, setHighlightedIndex] = React.useState();
108
110
  const [isFocused, setIsFocused] = React.useState(false);
109
111
  const [inputFocused, setInputFocused] = React.useState(false);
110
112
  const [isOpen, setIsOpen] = React.useState(open || false);
@@ -120,31 +122,46 @@ const MultiSelect = /*#__PURE__*/React__default["default"].forwardRef(function M
120
122
  onChange,
121
123
  selectedItems: selected
122
124
  });
123
- const {
124
- getToggleButtonProps,
125
- getLabelProps,
126
- getMenuProps,
127
- getItemProps,
128
- selectedItem
129
- } = Downshift.useSelect({
125
+ const selectProps = {
130
126
  ...downshiftProps,
131
- highlightedIndex,
127
+ stateReducer,
132
128
  isOpen,
133
129
  itemToString: items => {
134
130
  return Array.isArray(items) && items.map(function (item) {
135
131
  return itemToString(item);
136
132
  }).join(', ') || '';
137
133
  },
138
- onStateChange,
139
134
  selectedItem: controlledSelectedItems,
140
- items
141
- });
135
+ items,
136
+ isItemDisabled(item, _index) {
137
+ return item.disabled;
138
+ }
139
+ };
140
+ const {
141
+ getToggleButtonProps,
142
+ getLabelProps,
143
+ getMenuProps,
144
+ getItemProps,
145
+ selectedItem,
146
+ highlightedIndex
147
+ } = Downshift.useSelect(selectProps);
142
148
  const toggleButtonProps = getToggleButtonProps({
143
149
  onFocus: () => {
144
150
  setInputFocused(true);
145
151
  },
146
152
  onBlur: () => {
147
153
  setInputFocused(false);
154
+ },
155
+ onKeyDown: e => {
156
+ if (!disabled) {
157
+ if ((match.match(e, keys.Delete) || match.match(e, keys.Escape)) && !isOpen) {
158
+ clearSelection();
159
+ e.stopPropagation();
160
+ }
161
+ if ((match.match(e, keys.Space) || match.match(e, keys.ArrowDown) || match.match(e, keys.Enter)) && !isOpen) {
162
+ setIsOpenWrapper(true);
163
+ }
164
+ }
148
165
  }
149
166
  });
150
167
  const mergedRef = mergeRefs["default"](toggleButtonProps.ref, ref);
@@ -210,47 +227,54 @@ const MultiSelect = /*#__PURE__*/React__default["default"].forwardRef(function M
210
227
  } else if (selectionFeedback === 'top-after-reopen') {
211
228
  sortOptions.selectedItems = topItems;
212
229
  }
213
- function onStateChange(changes) {
214
- if (changes.isOpen && !isOpen) {
215
- setTopItems(controlledSelectedItems);
216
- }
230
+ function stateReducer(state, actionAndChanges) {
217
231
  const {
232
+ changes,
233
+ props,
218
234
  type
235
+ } = actionAndChanges;
236
+ const {
237
+ highlightedIndex
219
238
  } = changes;
239
+ if (changes.isOpen && !isOpen) {
240
+ setTopItems(controlledSelectedItems);
241
+ }
220
242
  switch (type) {
221
243
  case ItemClick:
222
- case MenuKeyDownSpaceButton:
244
+ case ToggleButtonKeyDownSpaceButton:
245
+ case ToggleButtonKeyDownEnter:
223
246
  if (changes.selectedItem === undefined) {
224
247
  break;
225
248
  }
226
249
  onItemChange(changes.selectedItem);
227
- break;
228
- case MenuKeyDownArrowDown:
229
- case MenuKeyDownArrowUp:
230
- setHighlightedIndex(changes.highlightedIndex);
231
- break;
232
- case MenuBlur:
233
- case MenuKeyDownEscape:
250
+ return {
251
+ ...changes,
252
+ highlightedIndex: state.highlightedIndex
253
+ };
254
+ case ToggleButtonBlur:
255
+ case ToggleButtonKeyDownEscape:
234
256
  setIsOpenWrapper(false);
235
- setHighlightedIndex(changes.highlightedIndex);
236
257
  break;
237
258
  case ToggleButtonClick:
238
259
  setIsOpenWrapper(changes.isOpen || false);
239
- setHighlightedIndex(changes.highlightedIndex);
240
260
  break;
261
+ case ToggleButtonKeyDownArrowDown:
262
+ case ToggleButtonKeyDownArrowUp:
263
+ case ToggleButtonKeyDownHome:
264
+ case ToggleButtonKeyDownEnd:
265
+ if (highlightedIndex > -1) {
266
+ const itemArray = document.querySelectorAll(`li.${prefix}--list-box__menu-item[role="option"]`);
267
+ props.scrollIntoView(itemArray[highlightedIndex]);
268
+ }
269
+ return changes;
270
+ case ItemMouseMove:
271
+ return {
272
+ ...changes,
273
+ highlightedIndex: state.highlightedIndex
274
+ };
241
275
  }
276
+ return changes;
242
277
  }
243
- const onKeyDown = e => {
244
- if (!disabled) {
245
- if (match.match(e, keys.Delete) || match.match(e, keys.Escape)) {
246
- clearSelection();
247
- e.stopPropagation();
248
- }
249
- if (match.match(e, keys.Space) || match.match(e, keys.ArrowDown)) {
250
- setIsOpenWrapper(true);
251
- }
252
- }
253
- };
254
278
  const multiSelectFieldWrapperClasses = cx__default["default"](`${prefix}--list-box__field--wrapper`, {
255
279
  [`${prefix}--list-box__field--wrapper--input-focused`]: inputFocused
256
280
  });
@@ -315,17 +339,14 @@ const MultiSelect = /*#__PURE__*/React__default["default"].forwardRef(function M
315
339
  "aria-disabled": disabled || readOnly,
316
340
  "aria-describedby": !inline && !invalid && !warn && helperText ? helperId : undefined
317
341
  }, toggleButtonProps, {
318
- ref: mergedRef,
319
- onKeyDown: onKeyDown
342
+ ref: mergedRef
320
343
  }, readOnlyEventHandlers), /*#__PURE__*/React__default["default"].createElement("span", {
321
344
  id: fieldLabelId,
322
345
  className: `${prefix}--list-box__label`
323
346
  }, label), /*#__PURE__*/React__default["default"].createElement(index["default"].MenuIcon, {
324
347
  isOpen: isOpen,
325
348
  translateWithId: translateWithId
326
- }))), /*#__PURE__*/React__default["default"].createElement(index["default"].Menu, _rollupPluginBabelHelpers["extends"]({
327
- "aria-multiselectable": "true"
328
- }, getMenuProps()), isOpen &&
349
+ }))), /*#__PURE__*/React__default["default"].createElement(index["default"].Menu, getMenuProps(), isOpen &&
329
350
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
330
351
  sortItems(items, sortOptions).map((item, index$1) => {
331
352
  const isChecked = selectedItems.filter(selected => isEqual__default["default"](selected, item)).length > 0;
@@ -333,8 +354,7 @@ const MultiSelect = /*#__PURE__*/React__default["default"].forwardRef(function M
333
354
  item,
334
355
  // we don't want Downshift to set aria-selected for us
335
356
  // we also don't want to set 'false' for reader verbosity's sake
336
- ['aria-selected']: isChecked ? true : undefined,
337
- disabled: item.disabled
357
+ ['aria-selected']: isChecked
338
358
  });
339
359
  const itemText = itemToString(item);
340
360
  return /*#__PURE__*/React__default["default"].createElement(index["default"].MenuItem, _rollupPluginBabelHelpers["extends"]({
@@ -342,7 +362,8 @@ const MultiSelect = /*#__PURE__*/React__default["default"].forwardRef(function M
342
362
  isActive: isChecked,
343
363
  "aria-label": itemText,
344
364
  isHighlighted: highlightedIndex === index$1,
345
- title: itemText
365
+ title: itemText,
366
+ disabled: itemProps['aria-disabled']
346
367
  }, itemProps), /*#__PURE__*/React__default["default"].createElement("div", {
347
368
  className: `${prefix}--checkbox-wrapper`
348
369
  }, /*#__PURE__*/React__default["default"].createElement("span", {
@@ -385,8 +406,7 @@ MultiSelect.propTypes = {
385
406
  /**
386
407
  * Additional props passed to Downshift
387
408
  */
388
- // @ts-ignore
389
- downshiftProps: PropTypes__default["default"].shape(Downshift__default["default"].propTypes),
409
+ downshiftProps: PropTypes__default["default"].object,
390
410
  /**
391
411
  * Provide helper text that is used alongside the control label for
392
412
  * additional help
@@ -128,5 +128,5 @@ export interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInp
128
128
  */
129
129
  warnText?: ReactNode;
130
130
  }
131
- declare const NumberInput: React.ForwardRefExoticComponent<NumberInputProps & React.RefAttributes<unknown>>;
131
+ declare const NumberInput: React.ForwardRefExoticComponent<NumberInputProps & React.RefAttributes<HTMLInputElement>>;
132
132
  export { NumberInput };
@@ -159,6 +159,22 @@ const NumberInput = /*#__PURE__*/React__default["default"].forwardRef(function N
159
159
  [`${prefix}--number-input--fluid--disabled`]: isFluid && disabled
160
160
  });
161
161
  const Icon = normalizedProps.icon;
162
+ function handleStepperClick(event, direction) {
163
+ if (inputRef.current) {
164
+ direction === 'up' ? inputRef.current.stepUp() : inputRef.current.stepDown();
165
+ const state = {
166
+ value: Number(inputRef.current.value),
167
+ direction: direction
168
+ };
169
+ setValue(state.value);
170
+ if (onChange) {
171
+ onChange(event, state);
172
+ }
173
+ if (onClick) {
174
+ onClick(event, state);
175
+ }
176
+ }
177
+ }
162
178
  return /*#__PURE__*/React__default["default"].createElement("div", {
163
179
  className: outerElementClasses,
164
180
  onFocus: isFluid ? handleFocus : undefined,
@@ -214,19 +230,7 @@ const NumberInput = /*#__PURE__*/React__default["default"].forwardRef(function N
214
230
  "aria-label": decrementNumLabel || iconDescription,
215
231
  className: `${prefix}--number__control-btn down-icon`,
216
232
  disabled: disabled || readOnly,
217
- onClick: event => {
218
- const state = {
219
- value: clamp(max, min, parseInt(value) - step),
220
- direction: 'down'
221
- };
222
- setValue(state.value);
223
- if (onChange) {
224
- onChange(event, state);
225
- }
226
- if (onClick) {
227
- onClick(event, state);
228
- }
229
- },
233
+ onClick: event => handleStepperClick(event, 'down'),
230
234
  tabIndex: -1,
231
235
  title: decrementNumLabel || iconDescription,
232
236
  type: "button"
@@ -238,19 +242,7 @@ const NumberInput = /*#__PURE__*/React__default["default"].forwardRef(function N
238
242
  "aria-label": incrementNumLabel || iconDescription,
239
243
  className: `${prefix}--number__control-btn up-icon`,
240
244
  disabled: disabled || readOnly,
241
- onClick: event => {
242
- const state = {
243
- value: clamp(max, min, parseInt(value) + step),
244
- direction: 'up'
245
- };
246
- setValue(state.value);
247
- if (onChange) {
248
- onChange(event, state);
249
- }
250
- if (onClick) {
251
- onClick(event, state);
252
- }
253
- },
245
+ onClick: event => handleStepperClick(event, 'up'),
254
246
  tabIndex: -1,
255
247
  title: incrementNumLabel || iconDescription,
256
248
  type: "button"
@@ -477,23 +469,5 @@ function disableWheel(e) {
477
469
  e.preventDefault();
478
470
  }
479
471
 
480
- /**
481
- * Clamp the given value between the upper bound `max` and the lower bound `min`
482
- *
483
- * 16 digit min/max more precise than Infinity. Somewhere in 9 quadrillion,
484
- * there will be integer display issues at runtime. 9quad is a safe cutoff.
485
- * @param {number} max
486
- * @param {number} min
487
- * @param {number} value
488
- */
489
- const boundLimit = 9000000000000000; // 16 digit, 9 quadrillion
490
-
491
- function clamp() {
492
- let max = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : boundLimit;
493
- let min = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -boundLimit;
494
- let value = arguments.length > 2 ? arguments[2] : undefined;
495
- return Math.min(max, Math.max(min, value));
496
- }
497
-
498
472
  exports.NumberInput = NumberInput;
499
473
  exports.translationIds = translationIds;
@@ -300,6 +300,12 @@ export default class Slider extends PureComponent<SliderProps> {
300
300
  * @returns `val` if `max>=val>=min`; `min` if `val<min`; `max` if `val>max`.
301
301
  */
302
302
  clamp(val: any, min: any, max: any): number;
303
+ /**
304
+ * Takes a value and ensures it fits to the steps of the range
305
+ * @param value
306
+ * @returns value of the nearest step
307
+ */
308
+ nearestStepValue(value: any): number;
303
309
  /**
304
310
  * Sets up "drag" event handlers and calls `this.onDrag` in case dragging
305
311
  * started on somewhere other than the thumb without a corresponding "move"
@@ -140,7 +140,7 @@ class Slider extends React.PureComponent {
140
140
  clientX
141
141
  });
142
142
  this.setState({
143
- value,
143
+ value: this.nearestStepValue(value),
144
144
  left,
145
145
  isValid: true
146
146
  });
@@ -189,7 +189,7 @@ class Slider extends React.PureComponent {
189
189
  value: (delta > 0 ? Math.floor(this.state.value / (this.props.step ?? Slider.defaultProps.step)) * (this.props.step ?? Slider.defaultProps.step) : this.state.value) + delta
190
190
  });
191
191
  this.setState({
192
- value,
192
+ value: this.nearestStepValue(value),
193
193
  left,
194
194
  isValid: true
195
195
  });
@@ -399,6 +399,21 @@ class Slider extends React.PureComponent {
399
399
  clamp(val, min, max) {
400
400
  return Math.max(min, Math.min(val, max));
401
401
  }
402
+
403
+ /**
404
+ * Takes a value and ensures it fits to the steps of the range
405
+ * @param value
406
+ * @returns value of the nearest step
407
+ */
408
+ nearestStepValue(value) {
409
+ const tempInput = document.createElement('input');
410
+ tempInput.type = 'range';
411
+ tempInput.min = `${this.props.min}`;
412
+ tempInput.max = `${this.props.max}`;
413
+ tempInput.step = `${this.props.step}`;
414
+ tempInput.value = `${value}`;
415
+ return parseFloat(tempInput.value);
416
+ }
402
417
  // syncs invalid state and prop
403
418
  static getDerivedStateFromProps(props, state) {
404
419
  const {
@@ -69,7 +69,7 @@ const TextArea = /*#__PURE__*/React__default["default"].forwardRef((props, forwa
69
69
  id,
70
70
  onChange: evt => {
71
71
  if (!other.disabled && onChange) {
72
- evt.persist();
72
+ evt?.persist?.();
73
73
  // delay textCount assignation to give the textarea element value time to catch up if is a controlled input
74
74
  setTimeout(() => {
75
75
  setTextCount(evt.target?.value?.length);
@@ -82,12 +82,12 @@ const ClickableTile = /*#__PURE__*/React__default["default"].forwardRef(function
82
82
  const classes = cx__default["default"](`${prefix}--tile`, `${prefix}--tile--clickable`, clicked && `${prefix}--tile--is-clicked`, light && `${prefix}--tile--light`, className);
83
83
  const [isSelected, setIsSelected] = React.useState(clicked);
84
84
  function handleOnClick(evt) {
85
- evt.persist();
85
+ evt?.persist?.();
86
86
  setIsSelected(!isSelected);
87
87
  onClick(evt);
88
88
  }
89
89
  function handleOnKeyDown(evt) {
90
- evt.persist();
90
+ evt?.persist?.();
91
91
  if (match.matches(evt, [keys.Enter, keys.Space])) {
92
92
  evt.preventDefault();
93
93
  setIsSelected(!isSelected);
@@ -191,7 +191,7 @@ const SelectableTile = /*#__PURE__*/React__default["default"].forwardRef(functio
191
191
  // TODO: rename to handleClick when handleClick prop is deprecated
192
192
  function handleOnClick(evt) {
193
193
  evt.preventDefault();
194
- evt.persist();
194
+ evt?.persist?.();
195
195
  setIsSelected(!isSelected);
196
196
  clickHandler(evt);
197
197
  onChange(evt);
@@ -199,7 +199,7 @@ const SelectableTile = /*#__PURE__*/React__default["default"].forwardRef(functio
199
199
 
200
200
  // TODO: rename to handleKeyDown when handleKeyDown prop is deprecated
201
201
  function handleOnKeyDown(evt) {
202
- evt.persist();
202
+ evt?.persist?.();
203
203
  if (match.matches(evt, [keys.Enter, keys.Space])) {
204
204
  evt.preventDefault();
205
205
  setIsSelected(!isSelected);
@@ -345,7 +345,7 @@ const ExpandableTile = /*#__PURE__*/React__default["default"].forwardRef(functio
345
345
  }
346
346
  }
347
347
  function handleClick(evt) {
348
- evt.persist();
348
+ evt?.persist?.();
349
349
  setIsExpanded(!isExpanded);
350
350
  setMaxHeight();
351
351
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/react",
3
3
  "description": "React components for the Carbon Design System",
4
- "version": "1.36.0",
4
+ "version": "1.37.0-rc.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "module": "es/index.js",
@@ -52,7 +52,7 @@
52
52
  "@carbon/telemetry": "0.1.0",
53
53
  "classnames": "2.3.2",
54
54
  "copy-to-clipboard": "^3.3.1",
55
- "downshift": "5.2.1",
55
+ "downshift": "8.1.0",
56
56
  "flatpickr": "4.6.9",
57
57
  "invariant": "^2.2.3",
58
58
  "lodash.debounce": "^4.0.8",
@@ -138,5 +138,5 @@
138
138
  "**/*.scss",
139
139
  "**/*.css"
140
140
  ],
141
- "gitHead": "34d9b328b856918ec5a3bfc720f4b3ba3d45daf4"
141
+ "gitHead": "41aa48117e9c5ce799621dd4d3477a848ca655c9"
142
142
  }