@carbon/react 1.69.0-rc.0 → 1.69.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.
@@ -143,6 +143,10 @@ export interface FilterableMultiSelectProps<ItemType> extends MultiSelectSorting
143
143
  * what this field is for
144
144
  */
145
145
  placeholder?: string;
146
+ /**
147
+ * Whether or not the filterable multiselect is readonly
148
+ */
149
+ readOnly?: boolean;
146
150
  /**
147
151
  * Specify feedback (mode) of the selection.
148
152
  * `top`: selected item jumps to top
@@ -102,6 +102,7 @@ const FilterableMultiSelect = /*#__PURE__*/React__default["default"].forwardRef(
102
102
  onChange,
103
103
  onMenuChange,
104
104
  placeholder,
105
+ readOnly,
105
106
  titleText,
106
107
  type,
107
108
  selectionFeedback = 'top-after-reopen',
@@ -244,9 +245,11 @@ const FilterableMultiSelect = /*#__PURE__*/React__default["default"].forwardRef(
244
245
  }
245
246
  };
246
247
  function handleMenuChange(forceIsOpen) {
247
- const nextIsOpen = forceIsOpen ?? !isOpen;
248
- setIsOpen(nextIsOpen);
249
- validateHighlightFocus();
248
+ if (!readOnly) {
249
+ const nextIsOpen = forceIsOpen ?? !isOpen;
250
+ setIsOpen(nextIsOpen);
251
+ validateHighlightFocus();
252
+ }
250
253
  }
251
254
  React.useEffect(() => {
252
255
  onMenuChange?.(isOpen);
@@ -435,7 +438,8 @@ const FilterableMultiSelect = /*#__PURE__*/React__default["default"].forwardRef(
435
438
  [`${prefix}--multi-select--open`]: isOpen,
436
439
  [`${prefix}--multi-select--inline`]: inline,
437
440
  [`${prefix}--multi-select--selected`]: controlledSelectedItems?.length > 0,
438
- [`${prefix}--multi-select--filterable--input-focused`]: inputFocused
441
+ [`${prefix}--multi-select--filterable--input-focused`]: inputFocused,
442
+ [`${prefix}--multi-select--readonly`]: readOnly
439
443
  });
440
444
  const labelProps = getLabelProps();
441
445
  const buttonProps = getToggleButtonProps({
@@ -522,6 +526,24 @@ const FilterableMultiSelect = /*#__PURE__*/React__default["default"].forwardRef(
522
526
  setIsFocused(evt?.type === 'focus' ? true : false);
523
527
  }
524
528
  };
529
+ const mergedRef = mergeRefs["default"](textInput, inputProps.ref);
530
+ const readOnlyEventHandlers = readOnly ? {
531
+ onClick: evt => {
532
+ // NOTE: does not prevent click
533
+ evt.preventDefault();
534
+ // focus on the element as per readonly input behavior
535
+ if (mergedRef.current !== undefined) {
536
+ mergedRef.current.focus();
537
+ }
538
+ },
539
+ onKeyDown: evt => {
540
+ const selectAccessKeys = ['ArrowDown', 'ArrowUp', ' ', 'Enter'];
541
+ // This prevents the select from opening for the above keys
542
+ if (selectAccessKeys.includes(evt.key)) {
543
+ evt.preventDefault();
544
+ }
545
+ }
546
+ } : {};
525
547
  const clearSelectionContent = controlledSelectedItems.length > 0 ? /*#__PURE__*/React__default["default"].createElement("span", {
526
548
  className: `${prefix}--visually-hidden`
527
549
  }, clearSelectionDescription, " ", controlledSelectedItems.length, ",", clearSelectionText) : /*#__PURE__*/React__default["default"].createElement("span", {
@@ -545,12 +567,13 @@ const FilterableMultiSelect = /*#__PURE__*/React__default["default"].forwardRef(
545
567
  invalidText: invalidText,
546
568
  warn: warn,
547
569
  warnText: warnText,
548
- isOpen: isOpen,
570
+ isOpen: !readOnly && isOpen,
549
571
  size: size
550
572
  }, /*#__PURE__*/React__default["default"].createElement("div", {
551
573
  className: `${prefix}--list-box__field`,
552
574
  ref: autoAlign ? refs.setReference : null
553
575
  }, controlledSelectedItems.length > 0 && /*#__PURE__*/React__default["default"].createElement(ListBoxSelection["default"], {
576
+ readOnly: readOnly,
554
577
  clearSelection: () => {
555
578
  clearSelection();
556
579
  if (textInput.current) {
@@ -563,7 +586,9 @@ const FilterableMultiSelect = /*#__PURE__*/React__default["default"].forwardRef(
563
586
  }), /*#__PURE__*/React__default["default"].createElement("input", _rollupPluginBabelHelpers["extends"]({
564
587
  className: inputClasses
565
588
  }, inputProps, {
566
- ref: mergeRefs["default"](textInput, inputProps.ref)
589
+ ref: mergedRef
590
+ }, readOnlyEventHandlers, {
591
+ readOnly: readOnly
567
592
  })), invalid && /*#__PURE__*/React__default["default"].createElement(iconsReact.WarningFilled, {
568
593
  className: `${prefix}--list-box__invalid-icon`
569
594
  }), showWarning && /*#__PURE__*/React__default["default"].createElement(iconsReact.WarningAltFilled, {
@@ -572,6 +597,7 @@ const FilterableMultiSelect = /*#__PURE__*/React__default["default"].forwardRef(
572
597
  clearSelection: clearInputValue,
573
598
  disabled: disabled,
574
599
  translateWithId: translateWithId,
600
+ readOnly: readOnly,
575
601
  onMouseUp: event => {
576
602
  // If we do not stop this event from propagating,
577
603
  // it seems like Downshift takes our event and
@@ -333,8 +333,8 @@ function TabList(_ref4) {
333
333
  function onKeyDown(event) {
334
334
  if (match.matches(event, [keys.ArrowRight, keys.ArrowLeft, keys.Home, keys.End])) {
335
335
  event.preventDefault();
336
- const filtredTabs = tabs.current.filter(tab => tab !== null);
337
- const activeTabs = filtredTabs.filter(tab => !tab.disabled);
336
+ const filteredTabs = tabs.current.filter(tab => tab !== null);
337
+ const activeTabs = filteredTabs.filter(tab => !tab.disabled);
338
338
  const currentIndex = activeTabs.indexOf(tabs.current[activation === 'automatic' ? selectedIndex : activeIndex]);
339
339
  const nextIndex = tabs.current.indexOf(activeTabs[getNextIndex(event, activeTabs.length, currentIndex)]);
340
340
  if (activation === 'automatic') {
@@ -345,6 +345,18 @@ function TabList(_ref4) {
345
345
  tabs.current[nextIndex]?.focus();
346
346
  }
347
347
  }
348
+ function handleBlur(_ref5) {
349
+ let {
350
+ relatedTarget: currentActiveNode
351
+ } = _ref5;
352
+ if (ref.current?.contains(currentActiveNode)) {
353
+ return;
354
+ }
355
+ // reset active index to selected tab index for manual activation
356
+ if (activation === 'manual') {
357
+ setActiveIndex(selectedIndex);
358
+ }
359
+ }
348
360
  useEffectOnce.useEffectOnce(() => {
349
361
  const tab = tabs.current[selectedIndex];
350
362
  if (scrollIntoView && tab) {
@@ -430,10 +442,10 @@ function TabList(_ref4) {
430
442
  }
431
443
  }, [activation, activeIndex, selectedIndex, isScrollable, children]);
432
444
  usePressable.usePressable(previousButton, {
433
- onPress(_ref5) {
445
+ onPress(_ref6) {
434
446
  let {
435
447
  longPress
436
- } = _ref5;
448
+ } = _ref6;
437
449
  if (!longPress && ref.current) {
438
450
  setScrollLeft(Math.max(scrollLeft - ref.current.scrollWidth / tabs.current.length * 1.5, 0));
439
451
  }
@@ -443,10 +455,10 @@ function TabList(_ref4) {
443
455
  }
444
456
  });
445
457
  usePressable.usePressable(nextButton, {
446
- onPress(_ref6) {
458
+ onPress(_ref7) {
447
459
  let {
448
460
  longPress
449
- } = _ref6;
461
+ } = _ref7;
450
462
  if (!longPress && ref.current) {
451
463
  setScrollLeft(Math.min(scrollLeft + ref.current.scrollWidth / tabs.current.length * 1.5, ref.current.scrollWidth - ref.current.clientWidth));
452
464
  }
@@ -470,7 +482,8 @@ function TabList(_ref4) {
470
482
  role: "tablist",
471
483
  className: `${prefix}--tab--list`,
472
484
  onScroll: debouncedOnScroll,
473
- onKeyDown: onKeyDown
485
+ onKeyDown: onKeyDown,
486
+ onBlur: handleBlur
474
487
  }), React__default["default"].Children.map(children, (child, index) => {
475
488
  return !reactIs.isElement(child) ? null : /*#__PURE__*/React__default["default"].createElement(TabContext.Provider, {
476
489
  value: {
@@ -555,7 +568,7 @@ TabList.propTypes = {
555
568
 
556
569
  // type TabElement = HTMLElement & { disabled?: boolean };
557
570
 
558
- function TabListVertical(_ref7) {
571
+ function TabListVertical(_ref8) {
559
572
  let {
560
573
  activation = 'automatic',
561
574
  'aria-label': label,
@@ -563,7 +576,7 @@ function TabListVertical(_ref7) {
563
576
  className: customClassName,
564
577
  scrollIntoView,
565
578
  ...rest
566
- } = _ref7;
579
+ } = _ref8;
567
580
  const {
568
581
  activeIndex,
569
582
  selectedIndex,
@@ -580,8 +593,8 @@ function TabListVertical(_ref7) {
580
593
  function onKeyDown(event) {
581
594
  if (match.matches(event, [keys.ArrowDown, keys.ArrowUp, keys.Home, keys.End])) {
582
595
  event.preventDefault();
583
- const filtredTabs = tabs.current.filter(tab => tab !== null);
584
- const activeTabs = filtredTabs.filter(tab => !tab.disabled);
596
+ const filteredTabs = tabs.current.filter(tab => tab !== null);
597
+ const activeTabs = filteredTabs.filter(tab => !tab.disabled);
585
598
  const currentIndex = activeTabs.indexOf(tabs.current[activation === 'automatic' ? selectedIndex : activeIndex]);
586
599
  const nextIndex = tabs.current.indexOf(activeTabs[getNextIndexVertical(event, activeTabs.length, currentIndex)]);
587
600
  if (activation === 'automatic') {
@@ -592,6 +605,18 @@ function TabListVertical(_ref7) {
592
605
  tabs.current[nextIndex]?.focus();
593
606
  }
594
607
  }
608
+ function handleBlur(_ref9) {
609
+ let {
610
+ relatedTarget: currentActiveNode
611
+ } = _ref9;
612
+ if (ref.current?.contains(currentActiveNode)) {
613
+ return;
614
+ }
615
+ // reset active index to selected tab index for manual activation
616
+ if (activation === 'manual') {
617
+ setActiveIndex(selectedIndex);
618
+ }
619
+ }
595
620
  useEffectOnce.useEffectOnce(() => {
596
621
  if (tabs.current[selectedIndex]?.disabled) {
597
622
  const activeTabs = tabs.current.filter(tab => {
@@ -658,7 +683,8 @@ function TabListVertical(_ref7) {
658
683
  ref: ref,
659
684
  role: "tablist",
660
685
  className: `${prefix}--tab--list`,
661
- onKeyDown: onKeyDown
686
+ onKeyDown: onKeyDown,
687
+ onBlur: handleBlur
662
688
  }), React__default["default"].Children.map(children, (child, index) => {
663
689
  return !reactIs.isElement(child) ? null : /*#__PURE__*/React__default["default"].createElement(TabContext.Provider, {
664
690
  value: {
@@ -741,7 +767,7 @@ function createLongPressBehavior(ref, direction, setScrollLeft) {
741
767
  * Tab
742
768
  */
743
769
 
744
- const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref8, forwardRef) {
770
+ const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref10, forwardRef) {
745
771
  let {
746
772
  as = 'button',
747
773
  children,
@@ -752,7 +778,7 @@ const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref8, forwardRef) {
752
778
  secondaryLabel,
753
779
  renderIcon: Icon,
754
780
  ...rest
755
- } = _ref8;
781
+ } = _ref10;
756
782
  const prefix = usePrefix.usePrefix();
757
783
  const {
758
784
  selectedIndex,
@@ -994,7 +1020,7 @@ Tab.propTypes = {
994
1020
  * IconTab
995
1021
  */
996
1022
 
997
- const IconTab = /*#__PURE__*/React__default["default"].forwardRef(function IconTab(_ref9, ref) {
1023
+ const IconTab = /*#__PURE__*/React__default["default"].forwardRef(function IconTab(_ref11, ref) {
998
1024
  let {
999
1025
  children,
1000
1026
  className: customClassName,
@@ -1003,7 +1029,7 @@ const IconTab = /*#__PURE__*/React__default["default"].forwardRef(function IconT
1003
1029
  leaveDelayMs,
1004
1030
  label,
1005
1031
  ...rest
1006
- } = _ref9;
1032
+ } = _ref11;
1007
1033
  const prefix = usePrefix.usePrefix();
1008
1034
  const classNames = cx__default["default"](`${prefix}--tabs__nav-item--icon-only`, customClassName);
1009
1035
  return /*#__PURE__*/React__default["default"].createElement(Tooltip.Tooltip, {
@@ -1052,12 +1078,12 @@ IconTab.propTypes = {
1052
1078
  * TabPanel
1053
1079
  */
1054
1080
 
1055
- const TabPanel = /*#__PURE__*/React__default["default"].forwardRef(function TabPanel(_ref10, forwardRef) {
1081
+ const TabPanel = /*#__PURE__*/React__default["default"].forwardRef(function TabPanel(_ref12, forwardRef) {
1056
1082
  let {
1057
1083
  children,
1058
1084
  className: customClassName,
1059
1085
  ...rest
1060
- } = _ref10;
1086
+ } = _ref12;
1061
1087
  const prefix = usePrefix.usePrefix();
1062
1088
  const panel = React.useRef(null);
1063
1089
  const ref = useMergedRefs.useMergedRefs([forwardRef, panel]);
@@ -1132,10 +1158,10 @@ TabPanel.propTypes = {
1132
1158
  * TabPanels
1133
1159
  */
1134
1160
 
1135
- function TabPanels(_ref11) {
1161
+ function TabPanels(_ref13) {
1136
1162
  let {
1137
1163
  children
1138
- } = _ref11;
1164
+ } = _ref13;
1139
1165
  const prefix = usePrefix.usePrefix();
1140
1166
  const refs = React.useRef([]);
1141
1167
  const hiddenStates = React.useRef([]);
@@ -26,6 +26,14 @@ export interface SelectableTagBaseProps {
26
26
  * Can be a React component class
27
27
  */
28
28
  renderIcon?: React.ElementType;
29
+ /**
30
+ * Provide an optional hook that is called when selected is changed
31
+ */
32
+ onChange?: (selected: boolean) => void;
33
+ /**
34
+ * Provide an optional function to be called when the tag is clicked.
35
+ */
36
+ onClick?: (e: Event) => void;
29
37
  /**
30
38
  * Specify the state of the selectable tag.
31
39
  */
@@ -42,7 +50,7 @@ export interface SelectableTagBaseProps {
42
50
  }
43
51
  export type SelectableTagProps<T extends React.ElementType> = PolymorphicProps<T, SelectableTagBaseProps>;
44
52
  declare const SelectableTag: {
45
- <T extends React.ElementType<any, keyof React.JSX.IntrinsicElements>>({ className, disabled, id, renderIcon, selected, size, text, ...other }: SelectableTagProps<T>): import("react/jsx-runtime").JSX.Element;
53
+ <T extends React.ElementType<any, keyof React.JSX.IntrinsicElements>>({ className, disabled, id, renderIcon, onChange, onClick, selected, size, text, ...other }: SelectableTagProps<T>): import("react/jsx-runtime").JSX.Element;
46
54
  propTypes: {
47
55
  /**
48
56
  * Provide a custom className that is applied to the containing <span>
@@ -61,6 +69,14 @@ declare const SelectableTag: {
61
69
  * Can be a React component class
62
70
  */
63
71
  renderIcon: PropTypes.Requireable<object>;
72
+ /**
73
+ * Provide an optional hook that is called when selected is changed
74
+ */
75
+ onChange: PropTypes.Requireable<(...args: any[]) => any>;
76
+ /**
77
+ * Provide an optional function to be called when the tag is clicked.
78
+ */
79
+ onClick: PropTypes.Requireable<(...args: any[]) => any>;
64
80
  /**
65
81
  * Specify the state of the selectable tag.
66
82
  */
@@ -34,6 +34,8 @@ const SelectableTag = _ref => {
34
34
  disabled,
35
35
  id,
36
36
  renderIcon,
37
+ onChange,
38
+ onClick,
37
39
  selected = false,
38
40
  size,
39
41
  text,
@@ -52,13 +54,11 @@ const SelectableTag = _ref => {
52
54
  setIsEllipsisApplied(isEllipsisActive.isEllipsisActive(newElement));
53
55
  }, [prefix, tagRef]);
54
56
  const tooltipClasses = cx__default["default"](`${prefix}--icon-tooltip`, `${prefix}--tag-label-tooltip`);
55
-
56
- // Removing onClick from the spread operator
57
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
58
- const {
59
- onClick,
60
- ...otherProps
61
- } = other;
57
+ const handleClick = e => {
58
+ setSelectedTag(!selectedTag);
59
+ onChange?.(!selectedTag);
60
+ onClick?.(e);
61
+ };
62
62
  if (isEllipsisApplied) {
63
63
  return /*#__PURE__*/React__default["default"].createElement(Tooltip.Tooltip, {
64
64
  label: text,
@@ -67,29 +67,29 @@ const SelectableTag = _ref => {
67
67
  leaveDelayMs: 0,
68
68
  onMouseEnter: () => false
69
69
  }, /*#__PURE__*/React__default["default"].createElement(Tag["default"], _rollupPluginBabelHelpers["extends"]({
70
- "aria-pressed": selectedTag,
70
+ "aria-pressed": selectedTag !== false,
71
71
  ref: tagRef,
72
72
  size: size,
73
73
  renderIcon: renderIcon,
74
74
  disabled: disabled,
75
75
  className: tagClasses,
76
76
  id: tagId,
77
- onClick: () => setSelectedTag(!selectedTag)
78
- }, otherProps), /*#__PURE__*/React__default["default"].createElement(Text.Text, {
77
+ onClick: handleClick
78
+ }, other), /*#__PURE__*/React__default["default"].createElement(Text.Text, {
79
79
  title: text,
80
80
  className: `${prefix}--tag__label`
81
81
  }, text)));
82
82
  }
83
83
  return /*#__PURE__*/React__default["default"].createElement(Tag["default"], _rollupPluginBabelHelpers["extends"]({
84
- "aria-pressed": selectedTag,
84
+ "aria-pressed": selectedTag !== false,
85
85
  ref: tagRef,
86
86
  size: size,
87
87
  renderIcon: renderIcon,
88
88
  disabled: disabled,
89
89
  className: tagClasses,
90
90
  id: tagId,
91
- onClick: () => setSelectedTag(!selectedTag)
92
- }, otherProps), /*#__PURE__*/React__default["default"].createElement(Text.Text, {
91
+ onClick: handleClick
92
+ }, other), /*#__PURE__*/React__default["default"].createElement(Text.Text, {
93
93
  title: text,
94
94
  className: `${prefix}--tag__label`
95
95
  }, text));
@@ -112,6 +112,14 @@ SelectableTag.propTypes = {
112
112
  * Can be a React component class
113
113
  */
114
114
  renderIcon: PropTypes__default["default"].oneOfType([PropTypes__default["default"].func, PropTypes__default["default"].object]),
115
+ /**
116
+ * Provide an optional hook that is called when selected is changed
117
+ */
118
+ onChange: PropTypes__default["default"].func,
119
+ /**
120
+ * Provide an optional function to be called when the tag is clicked.
121
+ */
122
+ onClick: PropTypes__default["default"].func,
115
123
  /**
116
124
  * Specify the state of the selectable tag.
117
125
  */
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.69.0-rc.0",
4
+ "version": "1.69.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "module": "es/index.js",
@@ -48,10 +48,10 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@babel/runtime": "^7.24.7",
51
- "@carbon/feature-flags": "^0.24.0-rc.0",
52
- "@carbon/icons-react": "^11.52.0-rc.0",
53
- "@carbon/layout": "^11.28.0-rc.0",
54
- "@carbon/styles": "^1.68.0-rc.0",
51
+ "@carbon/feature-flags": "^0.24.0",
52
+ "@carbon/icons-react": "^11.52.0",
53
+ "@carbon/layout": "^11.28.0",
54
+ "@carbon/styles": "^1.68.0",
55
55
  "@floating-ui/react": "^0.26.0",
56
56
  "@ibm/telemetry-js": "^1.5.0",
57
57
  "classnames": "2.5.1",
@@ -79,11 +79,11 @@
79
79
  "@babel/preset-env": "^7.24.7",
80
80
  "@babel/preset-react": "^7.24.7",
81
81
  "@babel/preset-typescript": "^7.24.7",
82
- "@carbon/test-utils": "^10.33.0",
83
- "@carbon/themes": "^11.43.0-rc.0",
82
+ "@carbon/test-utils": "^10.34.0",
83
+ "@carbon/themes": "^11.43.0",
84
84
  "@figma/code-connect": "^1.1.4",
85
85
  "@rollup/plugin-babel": "^6.0.0",
86
- "@rollup/plugin-commonjs": "^26.0.0",
86
+ "@rollup/plugin-commonjs": "^28.0.0",
87
87
  "@rollup/plugin-node-resolve": "^15.0.0",
88
88
  "@rollup/plugin-typescript": "^11.0.0",
89
89
  "@storybook/addon-a11y": "^8.2.8",
@@ -114,6 +114,7 @@
114
114
  "mini-css-extract-plugin": "^2.4.5",
115
115
  "postcss": "^8.4.5",
116
116
  "postcss-loader": "^8.0.0",
117
+ "prettier2": "npm:prettier@2",
117
118
  "process": "^0.11.10",
118
119
  "prop-types": "^15.7.2",
119
120
  "react": "^18.2.0",
@@ -144,5 +145,5 @@
144
145
  "**/*.scss",
145
146
  "**/*.css"
146
147
  ],
147
- "gitHead": "e48f5e6815bd93e518e8da73c7555590b85cda22"
148
+ "gitHead": "3141442e5ef367667c8f1d379a1b4a23f67992ad"
148
149
  }