@carbon/react 1.97.0 → 1.99.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 (83) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +1002 -967
  2. package/es/components/ButtonSet/ButtonSet.d.ts +5 -0
  3. package/es/components/ButtonSet/ButtonSet.js +68 -4
  4. package/es/components/CodeSnippet/CodeSnippet.d.ts +1 -1
  5. package/es/components/Copy/Copy.d.ts +1 -1
  6. package/es/components/CopyButton/CopyButton.d.ts +1 -1
  7. package/es/components/DataTable/DataTable.d.ts +2 -1
  8. package/es/components/DataTable/TableContainer.d.ts +10 -2
  9. package/es/components/DataTable/TableContainer.js +15 -3
  10. package/es/components/DataTable/state/sorting.d.ts +2 -4
  11. package/es/components/DatePicker/DatePicker.d.ts +3 -2
  12. package/es/components/DatePicker/DatePicker.js +18 -135
  13. package/es/components/DatePicker/DatePickerLocales.d.ts +12 -0
  14. package/es/components/DatePicker/DatePickerLocales.js +135 -0
  15. package/es/components/DatePickerInput/DatePickerInput.js +50 -28
  16. package/es/components/Dropdown/Dropdown.js +13 -1
  17. package/es/components/FileUploader/FileUploader.d.ts +23 -8
  18. package/es/components/FileUploader/FileUploader.js +53 -33
  19. package/es/components/FileUploader/FileUploaderButton.js +2 -2
  20. package/es/components/FileUploader/FileUploaderDropContainer.d.ts +13 -2
  21. package/es/components/FileUploader/FileUploaderDropContainer.js +15 -6
  22. package/es/components/FileUploader/FileUploaderItem.js +9 -6
  23. package/es/components/Link/Link.js +1 -1
  24. package/es/components/Menu/index.d.ts +4 -3
  25. package/es/components/MultiSelect/FilterableMultiSelect.js +1 -0
  26. package/es/components/MultiSelect/MultiSelect.js +1 -0
  27. package/es/components/NumberInput/NumberInput.d.ts +6 -2
  28. package/es/components/NumberInput/NumberInput.js +17 -5
  29. package/es/components/Pagination/Pagination.js +5 -5
  30. package/es/components/Popover/index.d.ts +1 -1
  31. package/es/components/Popover/index.js +27 -13
  32. package/es/components/Select/Select.js +27 -33
  33. package/es/components/Tabs/Tabs.js +1 -1
  34. package/es/components/TextArea/TextArea.js +14 -4
  35. package/es/components/TextInput/PasswordInput.js +3 -2
  36. package/es/components/Toggletip/index.d.ts +1 -0
  37. package/es/components/Toggletip/index.js +1 -1
  38. package/es/components/Tooltip/index.d.ts +3 -2
  39. package/es/internal/FloatingMenu.js +6 -5
  40. package/es/internal/OptimizedResize.js +4 -5
  41. package/es/tools/events.d.ts +1 -1
  42. package/lib/components/ButtonSet/ButtonSet.d.ts +5 -0
  43. package/lib/components/ButtonSet/ButtonSet.js +67 -3
  44. package/lib/components/CodeSnippet/CodeSnippet.d.ts +1 -1
  45. package/lib/components/Copy/Copy.d.ts +1 -1
  46. package/lib/components/CopyButton/CopyButton.d.ts +1 -1
  47. package/lib/components/DataTable/DataTable.d.ts +2 -1
  48. package/lib/components/DataTable/TableContainer.d.ts +10 -2
  49. package/lib/components/DataTable/TableContainer.js +15 -3
  50. package/lib/components/DataTable/state/sorting.d.ts +2 -4
  51. package/lib/components/DatePicker/DatePicker.d.ts +3 -2
  52. package/lib/components/DatePicker/DatePicker.js +18 -135
  53. package/lib/components/DatePicker/DatePickerLocales.d.ts +12 -0
  54. package/lib/components/DatePicker/DatePickerLocales.js +137 -0
  55. package/lib/components/DatePickerInput/DatePickerInput.js +49 -27
  56. package/lib/components/Dropdown/Dropdown.js +13 -1
  57. package/lib/components/FileUploader/FileUploader.d.ts +23 -8
  58. package/lib/components/FileUploader/FileUploader.js +53 -33
  59. package/lib/components/FileUploader/FileUploaderButton.js +2 -2
  60. package/lib/components/FileUploader/FileUploaderDropContainer.d.ts +13 -2
  61. package/lib/components/FileUploader/FileUploaderDropContainer.js +15 -6
  62. package/lib/components/FileUploader/FileUploaderItem.js +9 -6
  63. package/lib/components/Link/Link.js +1 -1
  64. package/lib/components/Menu/index.d.ts +4 -3
  65. package/lib/components/MultiSelect/FilterableMultiSelect.js +1 -0
  66. package/lib/components/MultiSelect/MultiSelect.js +1 -0
  67. package/lib/components/NumberInput/NumberInput.d.ts +6 -2
  68. package/lib/components/NumberInput/NumberInput.js +17 -5
  69. package/lib/components/Pagination/Pagination.js +5 -5
  70. package/lib/components/Popover/index.d.ts +1 -1
  71. package/lib/components/Popover/index.js +27 -13
  72. package/lib/components/Select/Select.js +27 -33
  73. package/lib/components/Tabs/Tabs.js +1 -1
  74. package/lib/components/TextArea/TextArea.js +14 -4
  75. package/lib/components/TextInput/PasswordInput.js +3 -2
  76. package/lib/components/Toggletip/index.d.ts +1 -0
  77. package/lib/components/Toggletip/index.js +3 -0
  78. package/lib/components/Tooltip/index.d.ts +3 -2
  79. package/lib/internal/FloatingMenu.js +6 -5
  80. package/lib/internal/OptimizedResize.js +4 -5
  81. package/lib/tools/events.d.ts +1 -1
  82. package/package.json +21 -29
  83. package/telemetry.yml +3 -0
@@ -8,7 +8,9 @@ import React, { type HTMLAttributes } from 'react';
8
8
  interface FileItem {
9
9
  name: string;
10
10
  uuid: string;
11
- file: File;
11
+ file: File & {
12
+ invalidFileType?: boolean;
13
+ };
12
14
  }
13
15
  export interface FileChangeData {
14
16
  addedFiles: FileItem[];
@@ -58,6 +60,10 @@ export interface FileUploaderProps extends HTMLAttributes<HTMLSpanElement> {
58
60
  * Specify the title text of this `<FileUploader>`
59
61
  */
60
62
  labelTitle?: string;
63
+ /**
64
+ * Maximum file size allowed in bytes. Files larger than this will be marked invalid
65
+ */
66
+ maxFileSize?: number;
61
67
  /**
62
68
  * Specify if the component should accept multiple files to upload
63
69
  */
@@ -66,24 +72,32 @@ export interface FileUploaderProps extends HTMLAttributes<HTMLSpanElement> {
66
72
  * Provide a name for the underlying `<input>` node
67
73
  */
68
74
  name?: string;
75
+ /**
76
+ * Event handler that is called after files are added to the uploader
77
+ */
78
+ onAddFiles?: (event: React.SyntheticEvent<HTMLElement>, content: {
79
+ addedFiles: Array<File & {
80
+ invalidFileType?: boolean;
81
+ }>;
82
+ }) => void;
69
83
  /**
70
84
  * Provide an optional `onChange` hook that is called each time the input is changed.
71
85
  * When 'enable-enhanced-file-uploader' feature flag is enabled:
72
86
  * - Also fires for file deletions and clearFiles operations
73
87
  * - Event includes enhanced file information in event.target
74
88
  */
75
- onChange?: (event: any, data?: FileChangeData) => void;
89
+ onChange?: (event: React.SyntheticEvent<HTMLElement>, data?: FileChangeData) => void;
76
90
  /**
77
91
  * Provide an optional `onClick` hook that is called each time the
78
92
  * FileUploader is clicked
79
93
  */
80
- onClick?: (event: any) => void;
94
+ onClick?: (event: React.SyntheticEvent<HTMLElement>) => void;
81
95
  /**
82
96
  * Provide an optional `onDelete` hook that is called when an uploaded item is removed.
83
97
  * When 'enable-enhanced-file-uploader' feature flag is enabled:
84
98
  * - Event includes deleted file information in event.target
85
99
  */
86
- onDelete?: (event: any, data?: FileDeleteData) => void;
100
+ onDelete?: (event: React.SyntheticEvent<HTMLElement>, data?: FileDeleteData) => void;
87
101
  /**
88
102
  * Specify the size of the FileUploaderButton, from a list of available
89
103
  * sizes.
@@ -101,9 +115,10 @@ export interface FileUploaderHandle {
101
115
  getCurrentFiles?: () => FileItem[];
102
116
  }
103
117
  declare const FileUploader: {
104
- <ItemType>(props: FileUploaderProps): React.ReactElement<any>;
105
- propTypes?: any;
106
- contextTypes?: any;
107
- defaultProps?: any;
118
+ (props: FileUploaderProps): React.ReactElement;
119
+ displayName?: string;
120
+ propTypes?: unknown;
121
+ contextTypes?: unknown;
122
+ defaultProps?: unknown;
108
123
  };
109
124
  export default FileUploader;
@@ -24,7 +24,6 @@ require('../Text/TextDirection.js');
24
24
  var useId = require('../../internal/useId.js');
25
25
  var index = require('../FeatureFlags/index.js');
26
26
 
27
- // eslint-disable-next-line react/display-name -- https://github.com/carbon-design-system/carbon/issues/20452
28
27
  const FileUploader = /*#__PURE__*/React.forwardRef(({
29
28
  accept,
30
29
  buttonKind,
@@ -35,8 +34,10 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
35
34
  iconDescription,
36
35
  labelDescription,
37
36
  labelTitle,
37
+ maxFileSize,
38
38
  multiple,
39
39
  name,
40
+ onAddFiles,
40
41
  onChange,
41
42
  onClick,
42
43
  onDelete,
@@ -48,20 +49,51 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
48
49
  const enhancedFileUploaderEnabled = index.useFeatureFlag('enable-enhanced-file-uploader');
49
50
  const [fileItems, setFileItems] = React.useState([]);
50
51
  const [legacyFileNames, setLegacyFileNames] = React.useState([]);
51
-
52
- // eslint-disable-next-line @typescript-eslint/no-unused-vars -- https://github.com/carbon-design-system/carbon/issues/20452
53
- const [fileObjects, setFileObjects] = React.useState(new Map());
52
+ const uploaderButton = /*#__PURE__*/React.createRef();
54
53
  const nodes = [];
55
- const createFileItem = file => ({
54
+ const createFileItem = React.useCallback(file => ({
56
55
  name: file.name,
57
56
  uuid: `${fileUploaderInstanceId}-${Date.now()}-${Array.from(crypto.getRandomValues(new Uint8Array(8))).map(b => b.toString(36)).join('')}`,
58
57
  file
59
- });
58
+ }), [fileUploaderInstanceId]);
59
+
60
+ /**
61
+ * Validates files based on file size restrictions.
62
+ * Marks invalid files with `invalidFileType: true` but includes them in the result.
63
+ *
64
+ * Note: The `accept` prop is passed to the native HTML input element (`FileUploaderButton`),
65
+ * which provides UI-level filtering in the file picker dialog, but there is no JavaScript validation
66
+ * for file types - users can bypass this by changing the file type filter in the dialog.
67
+ * https://github.com/carbon-design-system/carbon/issues/21166
68
+ */
69
+ const validateFiles = React.useCallback(files => {
70
+ return files.map(file => {
71
+ if (maxFileSize && file.size > maxFileSize) {
72
+ file.invalidFileType = true;
73
+ }
74
+ return file;
75
+ });
76
+ }, [maxFileSize]);
60
77
  const handleChange = React.useCallback(evt => {
61
78
  evt.stopPropagation();
62
- const newFiles = Array.from(evt.target.files);
79
+ const incomingFiles = Array.from(evt.target.files);
80
+ const filesToValidate = multiple ? incomingFiles : [incomingFiles[0]];
81
+ const validatedFiles = validateFiles(filesToValidate);
82
+ if (onAddFiles) {
83
+ onAddFiles(evt, {
84
+ addedFiles: validatedFiles
85
+ });
86
+ }
87
+
88
+ // Filter out invalid files since FileUploader cannot display them
89
+ // (FileUploaderDropContainer returns all files because parent uses FileUploaderItem to display errors)
90
+ // https://github.com/carbon-design-system/carbon/issues/21166
91
+ const validFiles = validatedFiles.filter(file => !file.invalidFileType);
92
+ if (validFiles.length === 0) {
93
+ return;
94
+ }
63
95
  if (enhancedFileUploaderEnabled) {
64
- const newFileItems = newFiles.map(createFileItem);
96
+ const newFileItems = validFiles.map(createFileItem);
65
97
  let updatedFileItems;
66
98
  if (multiple) {
67
99
  const existingNames = new Set(fileItems.map(item => item.name));
@@ -88,23 +120,14 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
88
120
  onChange(enhancedEvent);
89
121
  }
90
122
  } else {
91
- const filenames = newFiles.map(file => file.name);
123
+ const filenames = validFiles.map(file => file.name);
92
124
  const updatedFileNames = multiple ? [...new Set([...legacyFileNames, ...filenames])] : filenames;
93
125
  setLegacyFileNames(updatedFileNames);
94
- setFileObjects(prevMap => {
95
- const newMap = multiple ? new Map(prevMap) : new Map();
96
- newFiles.forEach(file => {
97
- newMap.set(file.name, file);
98
- });
99
- return newMap;
100
- });
101
126
  if (onChange) {
102
127
  onChange(evt);
103
128
  }
104
129
  }
105
- },
106
- // eslint-disable-next-line react-hooks/exhaustive-deps -- https://github.com/carbon-design-system/carbon/issues/20452
107
- [enhancedFileUploaderEnabled, fileItems, legacyFileNames, multiple, onChange]);
130
+ }, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, multiple, onAddFiles, onChange, createFileItem, validateFiles]);
108
131
  const handleClick = React.useCallback((evt, {
109
132
  index,
110
133
  filenameStatus
@@ -141,15 +164,6 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
141
164
  const deletedFileName = legacyFileNames[index];
142
165
  const filteredArray = legacyFileNames.filter(filename => filename !== deletedFileName);
143
166
  setLegacyFileNames(filteredArray);
144
-
145
- // Update File objects
146
- setFileObjects(prevMap => {
147
- const newMap = new Map(prevMap);
148
- if (deletedFileName) {
149
- newMap.delete(deletedFileName);
150
- }
151
- return newMap;
152
- });
153
167
  if (onDelete) {
154
168
  onDelete(evt);
155
169
  }
@@ -159,9 +173,7 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
159
173
  }
160
174
  uploaderButton.current?.focus?.();
161
175
  }
162
- },
163
- // eslint-disable-next-line react-hooks/exhaustive-deps -- https://github.com/carbon-design-system/carbon/issues/20452
164
- [enhancedFileUploaderEnabled, fileItems, legacyFileNames, onDelete, onChange, onClick]);
176
+ }, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, onDelete, onChange, onClick, uploaderButton]);
165
177
  React.useImperativeHandle(ref, () => ({
166
178
  clearFiles() {
167
179
  if (enhancedFileUploaderEnabled) {
@@ -184,7 +196,6 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
184
196
  }
185
197
  } else {
186
198
  setLegacyFileNames([]);
187
- setFileObjects(new Map());
188
199
  }
189
200
  },
190
201
  ...(enhancedFileUploaderEnabled && {
@@ -193,7 +204,6 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
193
204
  }
194
205
  })
195
206
  }), [enhancedFileUploaderEnabled, fileItems, onChange]);
196
- const uploaderButton = /*#__PURE__*/React.createRef();
197
207
  const classes = cx({
198
208
  [`${prefix}--form-item`]: true,
199
209
  [className]: className
@@ -267,6 +277,7 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
267
277
  })
268
278
  }))))));
269
279
  });
280
+ FileUploader.displayName = 'FileUploader';
270
281
  FileUploader.propTypes = {
271
282
  /**
272
283
  * Specify the types of files that this input should be able to receive
@@ -305,6 +316,10 @@ FileUploader.propTypes = {
305
316
  * Specify the title text of this `<FileUploader>`
306
317
  */
307
318
  labelTitle: PropTypes.string,
319
+ /**
320
+ * Maximum file size allowed in bytes. Files larger than this will be marked invalid
321
+ */
322
+ maxFileSize: PropTypes.number,
308
323
  /**
309
324
  * Specify if the component should accept multiple files to upload
310
325
  */
@@ -313,6 +328,11 @@ FileUploader.propTypes = {
313
328
  * Provide a name for the underlying `<input>` node
314
329
  */
315
330
  name: PropTypes.string,
331
+ /**
332
+ * Event handler that is called after files are added to the uploader
333
+ * The event handler signature looks like `onAddFiles(evt, { addedFiles })`
334
+ */
335
+ onAddFiles: PropTypes.func,
316
336
  /**
317
337
  * Provide an optional `onChange` hook that is called each time the input is
318
338
  * changed
@@ -40,10 +40,10 @@ function FileUploaderButton({
40
40
  const prefix = usePrefix.usePrefix();
41
41
  const [labelText, setLabelText] = React.useState(ownerLabelText);
42
42
  const [prevOwnerLabelText, setPrevOwnerLabelText] = React.useState(ownerLabelText);
43
- // eslint-disable-next-line react-hooks/rules-of-hooks -- https://github.com/carbon-design-system/carbon/issues/20452
43
+ const generatedId = useId.useId();
44
44
  const {
45
45
  current: inputId
46
- } = React.useRef(id || useId.useId());
46
+ } = React.useRef(id || generatedId);
47
47
  const inputNode = React.useRef(null);
48
48
  const classes = cx(`${prefix}--btn`, className, {
49
49
  [`${prefix}--btn--${buttonKind}`]: buttonKind,
@@ -28,6 +28,10 @@ export interface FileUploaderDropContainerProps extends Omit<HTMLAttributes<HTML
28
28
  * this control
29
29
  */
30
30
  labelText?: string;
31
+ /**
32
+ * Maximum file size allowed in bytes. Files larger than this will be marked invalid
33
+ */
34
+ maxFileSize?: number;
31
35
  /**
32
36
  * Specify if the component should accept multiple files to upload
33
37
  */
@@ -40,7 +44,7 @@ export interface FileUploaderDropContainerProps extends Omit<HTMLAttributes<HTML
40
44
  * Event handler that is called after files are added to the uploader
41
45
  */
42
46
  onAddFiles?: (event: React.SyntheticEvent<HTMLElement>, content: {
43
- addedFiles: File[];
47
+ addedFiles: AddedFile[];
44
48
  }) => void;
45
49
  /**
46
50
  * Provide an optional function to be called when the button element
@@ -64,7 +68,10 @@ export interface FileUploaderDropContainerProps extends Omit<HTMLAttributes<HTML
64
68
  */
65
69
  tabIndex?: number | string;
66
70
  }
67
- declare function FileUploaderDropContainer({ accept, className, id, disabled, labelText, multiple, name, onAddFiles, onClick, pattern, innerRef, ...rest }: FileUploaderDropContainerProps): import("react/jsx-runtime").JSX.Element;
71
+ interface AddedFile extends File {
72
+ invalidFileType?: boolean;
73
+ }
74
+ declare function FileUploaderDropContainer({ accept, className, id, disabled, labelText, maxFileSize, multiple, name, onAddFiles, onClick, pattern, innerRef, ...rest }: FileUploaderDropContainerProps): import("react/jsx-runtime").JSX.Element;
68
75
  declare namespace FileUploaderDropContainer {
69
76
  var propTypes: {
70
77
  /**
@@ -88,6 +95,10 @@ declare namespace FileUploaderDropContainer {
88
95
  * this control
89
96
  */
90
97
  labelText: PropTypes.Validator<string>;
98
+ /**
99
+ * Maximum file size allowed in bytes. Files larger than this will be marked invalid
100
+ */
101
+ maxFileSize: PropTypes.Requireable<number>;
91
102
  /**
92
103
  * Specify if the component should accept multiple files to upload
93
104
  */
@@ -27,6 +27,7 @@ function FileUploaderDropContainer({
27
27
  id,
28
28
  disabled,
29
29
  labelText = 'Add file',
30
+ maxFileSize,
30
31
  multiple = false,
31
32
  name,
32
33
  onAddFiles = noopFn.noopFn,
@@ -37,10 +38,10 @@ function FileUploaderDropContainer({
37
38
  }) {
38
39
  const prefix = usePrefix.usePrefix();
39
40
  const inputRef = React.useRef(null);
40
- // eslint-disable-next-line react-hooks/rules-of-hooks -- https://github.com/carbon-design-system/carbon/issues/20452
41
+ const generatedId = useId.useId();
41
42
  const {
42
43
  current: uid
43
- } = React.useRef(id || useId.useId());
44
+ } = React.useRef(id || generatedId);
44
45
  const [isActive, setActive] = React.useState(false);
45
46
  const dropareaClasses = cx(`${prefix}--file__drop-container`, `${prefix}--file-browse-btn`, {
46
47
  [`${prefix}--file__drop-container--drag-over`]: isActive,
@@ -48,12 +49,9 @@ function FileUploaderDropContainer({
48
49
  }, className);
49
50
 
50
51
  /**
51
- * Filters the array of added files based on file type restrictions
52
+ * Filters the array of added files based on file type and size restrictions
52
53
  */
53
54
  function validateFiles(transferredFiles) {
54
- if (!accept.length) {
55
- return transferredFiles;
56
- }
57
55
  const acceptedTypes = new Set(accept);
58
56
  return transferredFiles.reduce((acc, curr) => {
59
57
  const {
@@ -62,6 +60,13 @@ function FileUploaderDropContainer({
62
60
  } = curr;
63
61
  const fileExtensionRegExp = new RegExp(pattern, 'i');
64
62
  const [fileExtension] = name.match(fileExtensionRegExp) ?? [];
63
+ if (maxFileSize && curr.size > maxFileSize) {
64
+ curr.invalidFileType = true;
65
+ return acc.concat([curr]);
66
+ }
67
+ if (!accept.length) {
68
+ return acc.concat([curr]);
69
+ }
65
70
  if (fileExtension === undefined) {
66
71
  return acc;
67
72
  }
@@ -174,6 +179,10 @@ FileUploaderDropContainer.propTypes = {
174
179
  * this control
175
180
  */
176
181
  labelText: PropTypes.string.isRequired,
182
+ /**
183
+ * Maximum file size allowed in bytes. Files larger than this will be marked invalid
184
+ */
185
+ maxFileSize: PropTypes.number,
177
186
  /**
178
187
  * Specify if the component should accept multiple files to upload
179
188
  */
@@ -40,10 +40,10 @@ function FileUploaderItem({
40
40
  const textRef = React.useRef(null);
41
41
  const [isEllipsisApplied, setIsEllipsisApplied] = React.useState(false);
42
42
  const prefix = usePrefix.usePrefix();
43
- // eslint-disable-next-line react-hooks/rules-of-hooks -- https://github.com/carbon-design-system/carbon/issues/20452
43
+ const generatedId = useId.useId();
44
44
  const {
45
45
  current: id
46
- } = React.useRef(uuid || useId.useId());
46
+ } = React.useRef(uuid || generatedId);
47
47
  const classes = cx(`${prefix}--file__selected-file`, className, {
48
48
  [`${prefix}--file__selected-file--invalid`]: invalid,
49
49
  [`${prefix}--file__selected-file--md`]: size === 'md',
@@ -53,11 +53,14 @@ function FileUploaderItem({
53
53
  const filterSpaceName = name => {
54
54
  return name?.replace(/\s+/g, '');
55
55
  };
56
-
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
58
56
  const isEllipsisActive = element => {
59
- setIsEllipsisApplied(element.offsetWidth < element.scrollWidth);
60
- return element.offsetWidth < element.scrollWidth;
57
+ if (!element) {
58
+ setIsEllipsisApplied(false);
59
+ return false;
60
+ }
61
+ const isActive = element.offsetWidth < element.scrollWidth;
62
+ setIsEllipsisApplied(isActive);
63
+ return isActive;
61
64
  };
62
65
  React.useLayoutEffect(() => {
63
66
  isEllipsisActive(textRef.current);
@@ -71,7 +71,7 @@ const LinkBase = /*#__PURE__*/React.forwardRef(({
71
71
  ref: ref
72
72
  }, linkProps, rest, {
73
73
  onClick: handleOnClick
74
- }), children, !inline && Icon && /*#__PURE__*/React.createElement("div", {
74
+ }), children, !inline && Icon && /*#__PURE__*/React.createElement("span", {
75
75
  className: `${prefix}--link__icon`
76
76
  }, /*#__PURE__*/React.createElement(Icon, null)));
77
77
  });
@@ -4,6 +4,7 @@
4
4
  * This source code is licensed under the Apache-2.0 license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import { Menu } from './Menu';
8
- import { MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable } from './MenuItem';
9
- export { Menu, MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable, };
7
+ import { Menu, type MenuProps } from './Menu';
8
+ import { MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable, type MenuItemProps, type MenuItemDividerProps, type MenuItemGroupProps, type MenuItemRadioGroupProps, type MenuItemSelectableProps } from './MenuItem';
9
+ export { Menu, MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable, type MenuProps, type MenuItemProps, type MenuItemDividerProps, type MenuItemGroupProps, type MenuItemRadioGroupProps, type MenuItemSelectableProps, };
10
+ export default Menu;
@@ -726,6 +726,7 @@ const FilterableMultiSelect = /*#__PURE__*/React.forwardRef(function FilterableM
726
726
  return /*#__PURE__*/React.createElement(index$1.default.MenuItem, _rollupPluginBabelHelpers.extends({
727
727
  key: itemProps.id,
728
728
  "aria-label": itemText,
729
+ "aria-checked": isIndeterminate ? 'mixed' : isChecked,
729
730
  isActive: isChecked && !item['isSelectAll'],
730
731
  isHighlighted: highlightedIndex === index,
731
732
  title: itemText,
@@ -511,6 +511,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
511
511
  key: itemProps.id,
512
512
  isActive: isChecked && !item['isSelectAll'],
513
513
  "aria-label": itemText,
514
+ "aria-checked": isIndeterminate ? 'mixed' : isChecked,
514
515
  isHighlighted: highlightedIndex === index,
515
516
  title: itemText,
516
517
  disabled: itemProps['aria-disabled']
@@ -135,10 +135,14 @@ export interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInp
135
135
  */
136
136
  min?: number;
137
137
  /**
138
- * Provide an optional handler that is called when the input or stepper
138
+ * Provide an optional handler that is called when the input is blurred.
139
+ */
140
+ onBlur?: (event: React.FocusEvent<HTMLInputElement>, value?: string | number) => void;
141
+ /**
142
+ * Provide an optional handler that is called when the stepper
139
143
  * buttons are blurred.
140
144
  */
141
- onBlur?: (event: React.FocusEvent<HTMLInputElement> | React.FocusEvent<HTMLButtonElement>) => void;
145
+ onStepperBlur?: (event: React.FocusEvent<HTMLButtonElement>) => void;
142
146
  /**
143
147
  * Provide an optional handler that is called when the internal state of
144
148
  * NumberInput changes. This handler is called with event and state info.
@@ -128,6 +128,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
128
128
  max,
129
129
  min,
130
130
  onBlur,
131
+ onStepperBlur,
131
132
  onChange,
132
133
  onClick,
133
134
  onKeyUp,
@@ -428,6 +429,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
428
429
  if (disableWheelProp) {
429
430
  e.target.removeEventListener('wheel', disableWheel);
430
431
  }
432
+ let parsedValueForBlur;
431
433
  if (type === 'text') {
432
434
  // When isControlled, the current inputValue needs re-parsed
433
435
  // because the consumer's onChange hasn't been called yet and
@@ -443,6 +445,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
443
445
  // causing the _numberValue to mismatch the formatted value in
444
446
  // the input. To avoid this, formattedValue is re-parsed.
445
447
  const parsedFormattedNewValue = numberParser.parse(formattedValue);
448
+ parsedValueForBlur = parsedFormattedNewValue;
446
449
  if (onChange && isValid) {
447
450
  const state = {
448
451
  value: parsedFormattedNewValue,
@@ -468,7 +471,12 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
468
471
  }
469
472
  }
470
473
  if (onBlur) {
471
- onBlur(e);
474
+ if (type === 'number') {
475
+ onBlur(e, value);
476
+ return;
477
+ }
478
+ const parsedTextValue = parsedValueForBlur ?? (isControlled ? numberParser.parse(inputValue) : numberValue);
479
+ onBlur(e, parsedTextValue);
472
480
  }
473
481
  },
474
482
  pattern: pattern,
@@ -488,7 +496,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
488
496
  className: `${prefix}--number__control-btn down-icon`,
489
497
  disabled: disabled || readOnly,
490
498
  onClick: event => handleStepperClick(event, 'down'),
491
- onBlur: onBlur,
499
+ onBlur: onStepperBlur,
492
500
  tabIndex: -1,
493
501
  title: decrementNumLabel || iconDescription,
494
502
  type: "button"
@@ -501,7 +509,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
501
509
  className: `${prefix}--number__control-btn up-icon`,
502
510
  disabled: disabled || readOnly,
503
511
  onClick: event => handleStepperClick(event, 'up'),
504
- onBlur: onBlur,
512
+ onBlur: onStepperBlur,
505
513
  tabIndex: -1,
506
514
  title: incrementNumLabel || iconDescription,
507
515
  type: "button"
@@ -612,10 +620,14 @@ NumberInput.propTypes = {
612
620
  */
613
621
  stepStartValue: PropTypes.number,
614
622
  /**
615
- * Provide an optional handler that is called when the input or stepper
616
- * buttons are blurred.
623
+ * Provide an optional handler that is called when the input is blurred.
617
624
  */
618
625
  onBlur: PropTypes.func,
626
+ /**
627
+ * Provide an optional handler that is called when the stepper
628
+ * buttons are blurred.
629
+ */
630
+ onStepperBlur: PropTypes.func,
619
631
  /**
620
632
  * Provide an optional handler that is called when the internal state of
621
633
  * NumberInput changes. This handler is called with event and state info.
@@ -11,16 +11,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
11
11
 
12
12
  var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
13
13
  var iconsReact = require('@carbon/icons-react');
14
- var cx = require('classnames');
15
- var PropTypes = require('prop-types');
16
14
  var React = require('react');
15
+ var index = require('../IconButton/index.js');
16
+ var PropTypes = require('prop-types');
17
17
  var Select = require('../Select/Select.js');
18
18
  require('../Select/Select.Skeleton.js');
19
19
  var SelectItem = require('../SelectItem/SelectItem.js');
20
+ var cx = require('classnames');
21
+ var isEqual = require('react-fast-compare');
20
22
  var useId = require('../../internal/useId.js');
21
23
  var usePrefix = require('../../internal/usePrefix.js');
22
- var index = require('../IconButton/index.js');
23
- var isEqual = require('react-fast-compare');
24
24
 
25
25
  var _CaretLeft, _CaretRight;
26
26
  function mapPageSizesToObject(sizes) {
@@ -270,7 +270,7 @@ const Pagination = /*#__PURE__*/React.forwardRef(({
270
270
  onClick: decrementPage,
271
271
  ref: backBtnRef
272
272
  }, _CaretLeft || (_CaretLeft = /*#__PURE__*/React.createElement(iconsReact.CaretLeft, null))), /*#__PURE__*/React.createElement(index.IconButton, {
273
- align: "top-end",
273
+ align: "top",
274
274
  disabled: forwardButtonDisabled || isLastPage,
275
275
  kind: "ghost",
276
276
  className: forwardButtonClasses,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2016, 2025
2
+ * Copyright IBM Corp. 2016, 2026
3
3
  *
4
4
  * This source code is licensed under the Apache-2.0 license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -62,27 +62,41 @@ forwardRef) {
62
62
  const caretRef = React.useRef(null);
63
63
  const popover = React.useRef(null);
64
64
  const enableFloatingStyles = index.useFeatureFlag('enable-v12-dynamic-floating-styles') || autoAlign;
65
+ const lastClickWasInsidePopoverContent = React.useRef(false);
65
66
  let align = mapPopoverAlign.mapPopoverAlign(initialAlign);
66
67
 
68
+ // Tracks clicks inside PopoverContent to prevent it from closing when clicked, this handles an edge
69
+ // case where the popover will close when clicking non-focusable elements (e.g. text)
70
+ useEvent.useEvent(popover, 'mousedown', event => {
71
+ const target = event.target;
72
+ lastClickWasInsidePopoverContent.current = refs.floating.current?.contains(target) || false;
73
+
74
+ // reset flag
75
+ if (lastClickWasInsidePopoverContent.current) {
76
+ setTimeout(() => {
77
+ lastClickWasInsidePopoverContent.current = false;
78
+ }, 0);
79
+ }
80
+ });
81
+
67
82
  // The `Popover` should close whenever it and its children loses focus
68
83
  useEvent.useEvent(popover, 'focusout', event => {
69
84
  const relatedTarget = event.relatedTarget;
70
- if (isTabTip) {
71
- if (relatedTarget && !popover.current?.contains(relatedTarget)) {
72
- onRequestClose?.();
73
- }
74
- return;
75
- }
76
85
  if (!relatedTarget) {
86
+ // do not close if PopoverContent was clicked
87
+ if (lastClickWasInsidePopoverContent.current) {
88
+ lastClickWasInsidePopoverContent.current = false;
89
+ return;
90
+ }
77
91
  onRequestClose?.();
78
- return;
79
- }
80
- const isOutsideMainContainer = !popover.current?.contains(relatedTarget);
81
- const isOutsideFloating = enableFloatingStyles && refs.floating.current ? !refs.floating.current.contains(relatedTarget) : true;
92
+ } else if (relatedTarget && !popover.current?.contains(relatedTarget)) {
93
+ const isOutsideFloating = enableFloatingStyles && refs.floating.current ? !refs.floating.current.contains(relatedTarget) : true;
94
+ const isFocusableWrapper = relatedTarget && popover.current && relatedTarget.contains(popover.current);
82
95
 
83
- // Only close if focus moved outside both containers
84
- if (isOutsideMainContainer && isOutsideFloating) {
85
- onRequestClose?.();
96
+ // Only close if focus moved outside both containers and not to an interactive parent wrapper
97
+ if (isOutsideFloating && !isFocusableWrapper) {
98
+ onRequestClose?.();
99
+ }
86
100
  }
87
101
  });
88
102
  useEvent.useWindowEvent('click', ({