@carbon/react 1.107.0-rc.0 → 1.107.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.
@@ -4,7 +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 React from 'react';
7
+ import React, { type ReactNode } from 'react';
8
8
  import { IconButtonKind } from '../IconButton';
9
9
  import { PolymorphicComponentPropWithRef } from '../../internal/PolymorphicProps';
10
10
  export declare const ButtonKinds: readonly ["primary", "secondary", "danger", "ghost", "danger--primary", "danger--ghost", "danger--tertiary", "tertiary"];
@@ -82,6 +82,6 @@ export interface ButtonBaseProps extends React.ButtonHTMLAttributes<HTMLButtonEl
82
82
  tooltipPosition?: ButtonTooltipPosition;
83
83
  }
84
84
  export type ButtonProps<T extends React.ElementType> = PolymorphicComponentPropWithRef<T, ButtonBaseProps>;
85
- export type ButtonComponent = <T extends React.ElementType = 'button'>(props: ButtonProps<T>, context?: any) => React.ReactElement | any;
85
+ export type ButtonComponent = <T extends React.ElementType = 'button'>(props: ButtonProps<T>) => ReactNode;
86
86
  declare const _default: ButtonComponent;
87
87
  export default _default;
@@ -38,8 +38,8 @@ const ComboButton = React.forwardRef(function ComboButton({ children, className,
38
38
  const id = useId("combobutton");
39
39
  const prefix = usePrefix();
40
40
  const containerRef = useRef(null);
41
- let middlewares = [];
42
- if (!enableOnlyFloatingStyles) middlewares = [flip({ crossAxis: false }), hide()];
41
+ const middlewares = [];
42
+ if (!enableOnlyFloatingStyles) middlewares.push(flip({ crossAxis: false }), hide());
43
43
  if (menuAlignment === "bottom" || menuAlignment === "top") middlewares.push(size({ apply({ rects, elements }) {
44
44
  Object.assign(elements.floating.style, { width: `${rects.reference.width}px` });
45
45
  } }));
@@ -57,7 +57,7 @@ export interface ModalProps extends HTMLAttributes<HTMLDivElement> {
57
57
  /**
58
58
  * Provide a ref to return focus to once the modal is closed.
59
59
  */
60
- launcherButtonRef?: RefObject<HTMLButtonElement | null>;
60
+ launcherButtonRef?: RefObject<HTMLElement | null>;
61
61
  /**
62
62
  * Specify the description for the loading text
63
63
  */
@@ -9,6 +9,7 @@ import { usePrefix } from "../../internal/usePrefix.js";
9
9
  import { Text } from "../Text/Text.js";
10
10
  import { Enter, Escape, Tab } from "../../internal/keyboard/keys.js";
11
11
  import { match } from "../../internal/keyboard/match.js";
12
+ import { selectorTabbable } from "../../internal/keyboard/navigation.js";
12
13
  import { useId } from "../../internal/useId.js";
13
14
  import { noopFn } from "../../internal/noopFn.js";
14
15
  import { warning } from "../../internal/warning.js";
@@ -31,7 +32,7 @@ import { Dialog } from "../Dialog/Dialog.js";
31
32
  import { usePreviousValue } from "../../internal/usePreviousValue.js";
32
33
  import { ModalPresence, ModalPresenceContext, useExclusiveModalPresenceContext } from "./ModalPresence.js";
33
34
  import classNames from "classnames";
34
- import React, { cloneElement, useContext, useEffect, useRef } from "react";
35
+ import React, { cloneElement, useCallback, useContext, useEffect, useRef } from "react";
35
36
  import PropTypes from "prop-types";
36
37
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
37
38
  import { Close } from "@carbon/icons-react";
@@ -209,25 +210,35 @@ const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLa
209
210
  prefix,
210
211
  enableDialogElement
211
212
  ]);
213
+ const focusLauncherButton = useCallback(() => {
214
+ if (!launcherButtonRef || !launcherButtonRef.current) return;
215
+ const { current: launcherButton } = launcherButtonRef;
216
+ (launcherButton.matches(selectorTabbable) ? launcherButton : launcherButton.querySelector(selectorTabbable))?.focus();
217
+ }, [launcherButtonRef]);
212
218
  useEffect(() => {
213
219
  if (!enableDialogElement && !enablePresence && prevOpen && !open && launcherButtonRef) setTimeout(() => {
214
- if ("current" in launcherButtonRef) launcherButtonRef.current?.focus();
220
+ focusLauncherButton();
215
221
  });
216
222
  }, [
217
223
  open,
218
224
  prevOpen,
219
225
  launcherButtonRef,
220
226
  enableDialogElement,
221
- enablePresence
227
+ enablePresence,
228
+ focusLauncherButton
222
229
  ]);
223
230
  useEffect(() => {
224
231
  const launcherButton = launcherButtonRef?.current;
225
232
  return () => {
226
233
  if (enablePresence && launcherButton) setTimeout(() => {
227
- launcherButton.focus();
234
+ focusLauncherButton();
228
235
  });
229
236
  };
230
- }, [enablePresence, launcherButtonRef]);
237
+ }, [
238
+ enablePresence,
239
+ launcherButtonRef,
240
+ focusLauncherButton
241
+ ]);
231
242
  useEffect(() => {
232
243
  if (!enableDialogElement) {
233
244
  const initialFocus = (focusContainerElement) => {
@@ -463,7 +474,7 @@ Modal.propTypes = {
463
474
  hasScrollingContent: PropTypes.bool,
464
475
  id: PropTypes.string,
465
476
  isFullWidth: PropTypes.bool,
466
- launcherButtonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.oneOfType([typeof HTMLButtonElement !== "undefined" ? PropTypes.instanceOf(HTMLButtonElement) : PropTypes.any, PropTypes.oneOf([null])]).isRequired })]),
477
+ launcherButtonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.oneOfType([typeof HTMLElement !== "undefined" ? PropTypes.instanceOf(HTMLElement) : PropTypes.any, PropTypes.oneOf([null])]).isRequired })]),
467
478
  loadingDescription: PropTypes.string,
468
479
  loadingIconDescription: PropTypes.string,
469
480
  loadingStatus: PropTypes.oneOf([
@@ -269,7 +269,7 @@ const NumberInput = React.forwardRef((props, forwardRef) => {
269
269
  value: allowEmpty && event.target.value === "" ? "" : Number(event.target.value),
270
270
  direction: value < event.target.value ? "up" : "down"
271
271
  };
272
- setValue(state.value);
272
+ if (controlledValue === void 0) setValue(state.value);
273
273
  if (onChange) onChange(event, state);
274
274
  return;
275
275
  }
@@ -312,7 +312,9 @@ const NumberInput = React.forwardRef((props, forwardRef) => {
312
312
  value: newValue,
313
313
  direction
314
314
  };
315
- if (type === "number") setValue(state.value);
315
+ if (type === "number") {
316
+ if (controlledValue === void 0) setValue(state.value);
317
+ }
316
318
  if (type === "text") {
317
319
  const formattedNewValue = format(newValue);
318
320
  const parsedFormattedNewValue = numberParser.parse(formattedNewValue);
@@ -270,11 +270,11 @@ const Slider = (props) => {
270
270
  DRAG_EVENT_TYPES.forEach((element) => {
271
271
  elementRef.current?.ownerDocument.removeEventListener(element, handleDrag);
272
272
  });
273
- setState({
274
- needsOnRelease: true,
275
- isValid: true,
276
- isValidUpper: true
277
- });
273
+ setState({ needsOnRelease: true });
274
+ };
275
+ const getValidityUpdateForHandle = (handle, validity) => {
276
+ if (typeof invalid !== "undefined") return {};
277
+ return handle === "upper" ? { isValidUpper: validity } : { isValid: validity };
278
278
  };
279
279
  /**
280
280
  * Handles a "drag" event by recalculating the value/thumb and setting state
@@ -299,7 +299,7 @@ const Slider = (props) => {
299
299
  else setState({
300
300
  value: nearestStepValue(value),
301
301
  left,
302
- isValid: true
302
+ ...getValidityUpdateForHandle("lower", true)
303
303
  });
304
304
  setState({
305
305
  correctedValue: null,
@@ -338,7 +338,7 @@ const Slider = (props) => {
338
338
  setState({
339
339
  value: nearestStepValue(value),
340
340
  left,
341
- isValid: true
341
+ ...getValidityUpdateForHandle("lower", true)
342
342
  });
343
343
  }
344
344
  setState({
@@ -395,9 +395,7 @@ const Slider = (props) => {
395
395
  const targetValue = Number.parseFloat(input.value);
396
396
  const validity = !isNaN(targetValue);
397
397
  const handlePosition = input.dataset.handlePosition;
398
- if (handlePosition === "lower") setState({ isValid: validity });
399
- else if (handlePosition === "upper") setState({ isValidUpper: validity });
400
- setState({ isValid: validity });
398
+ setState(getValidityUpdateForHandle(handlePosition ?? "lower", validity));
401
399
  if (validity) {
402
400
  const adjustedValue = handlePosition ? getAdjustedValueForPosition({
403
401
  handle: handlePosition,
@@ -507,22 +505,22 @@ const Slider = (props) => {
507
505
  if (handle === "lower") setState({
508
506
  value: valueUpper && newValue > valueUpper ? valueUpper : newValue,
509
507
  left: valueUpper && newValue > valueUpper ? leftUpper : newLeft,
510
- isValid: true
508
+ ...getValidityUpdateForHandle(handle, true)
511
509
  });
512
510
  else setState({
513
511
  valueUpper: value && newValue < value ? value : newValue,
514
512
  leftUpper: value && newValue < value ? left : newLeft,
515
- isValidUpper: true
513
+ ...getValidityUpdateForHandle(handle, true)
516
514
  });
517
515
  };
518
516
  const setValueForHandle = (handle, value) => {
519
517
  if (handle === "lower") setState({
520
518
  value,
521
- isValid: true
519
+ ...getValidityUpdateForHandle(handle, true)
522
520
  });
523
521
  else setState({
524
522
  valueUpper: value,
525
- isValidUpper: true
523
+ ...getValidityUpdateForHandle(handle, true)
526
524
  });
527
525
  };
528
526
  const isValidValueForPosition = ({ handle, value: newValue, min, max }) => {
@@ -4,7 +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 React from 'react';
7
+ import React, { type ReactNode } from 'react';
8
8
  import { IconButtonKind } from '../IconButton';
9
9
  import { PolymorphicComponentPropWithRef } from '../../internal/PolymorphicProps';
10
10
  export declare const ButtonKinds: readonly ["primary", "secondary", "danger", "ghost", "danger--primary", "danger--ghost", "danger--tertiary", "tertiary"];
@@ -82,6 +82,6 @@ export interface ButtonBaseProps extends React.ButtonHTMLAttributes<HTMLButtonEl
82
82
  tooltipPosition?: ButtonTooltipPosition;
83
83
  }
84
84
  export type ButtonProps<T extends React.ElementType> = PolymorphicComponentPropWithRef<T, ButtonBaseProps>;
85
- export type ButtonComponent = <T extends React.ElementType = 'button'>(props: ButtonProps<T>, context?: any) => React.ReactElement | any;
85
+ export type ButtonComponent = <T extends React.ElementType = 'button'>(props: ButtonProps<T>) => ReactNode;
86
86
  declare const _default: ButtonComponent;
87
87
  export default _default;
@@ -42,8 +42,8 @@ const ComboButton = react.default.forwardRef(function ComboButton({ children, cl
42
42
  const id = require_useId.useId("combobutton");
43
43
  const prefix = require_usePrefix.usePrefix();
44
44
  const containerRef = (0, react.useRef)(null);
45
- let middlewares = [];
46
- if (!enableOnlyFloatingStyles) middlewares = [(0, _floating_ui_react.flip)({ crossAxis: false }), (0, _floating_ui_react.hide)()];
45
+ const middlewares = [];
46
+ if (!enableOnlyFloatingStyles) middlewares.push((0, _floating_ui_react.flip)({ crossAxis: false }), (0, _floating_ui_react.hide)());
47
47
  if (menuAlignment === "bottom" || menuAlignment === "top") middlewares.push((0, _floating_ui_react.size)({ apply({ rects, elements }) {
48
48
  Object.assign(elements.floating.style, { width: `${rects.reference.width}px` });
49
49
  } }));
@@ -57,7 +57,7 @@ export interface ModalProps extends HTMLAttributes<HTMLDivElement> {
57
57
  /**
58
58
  * Provide a ref to return focus to once the modal is closed.
59
59
  */
60
- launcherButtonRef?: RefObject<HTMLButtonElement | null>;
60
+ launcherButtonRef?: RefObject<HTMLElement | null>;
61
61
  /**
62
62
  * Specify the description for the loading text
63
63
  */
@@ -10,6 +10,7 @@ const require_usePrefix = require("../../internal/usePrefix.js");
10
10
  const require_Text = require("../Text/Text.js");
11
11
  const require_keys = require("../../internal/keyboard/keys.js");
12
12
  const require_match = require("../../internal/keyboard/match.js");
13
+ const require_navigation = require("../../internal/keyboard/navigation.js");
13
14
  const require_useId = require("../../internal/useId.js");
14
15
  const require_noopFn = require("../../internal/noopFn.js");
15
16
  const require_warning = require("../../internal/warning.js");
@@ -213,25 +214,35 @@ const ModalDialog = react.default.forwardRef(function ModalDialog({ "aria-label"
213
214
  prefix,
214
215
  enableDialogElement
215
216
  ]);
217
+ const focusLauncherButton = (0, react.useCallback)(() => {
218
+ if (!launcherButtonRef || !launcherButtonRef.current) return;
219
+ const { current: launcherButton } = launcherButtonRef;
220
+ (launcherButton.matches(require_navigation.selectorTabbable) ? launcherButton : launcherButton.querySelector(require_navigation.selectorTabbable))?.focus();
221
+ }, [launcherButtonRef]);
216
222
  (0, react.useEffect)(() => {
217
223
  if (!enableDialogElement && !enablePresence && prevOpen && !open && launcherButtonRef) setTimeout(() => {
218
- if ("current" in launcherButtonRef) launcherButtonRef.current?.focus();
224
+ focusLauncherButton();
219
225
  });
220
226
  }, [
221
227
  open,
222
228
  prevOpen,
223
229
  launcherButtonRef,
224
230
  enableDialogElement,
225
- enablePresence
231
+ enablePresence,
232
+ focusLauncherButton
226
233
  ]);
227
234
  (0, react.useEffect)(() => {
228
235
  const launcherButton = launcherButtonRef?.current;
229
236
  return () => {
230
237
  if (enablePresence && launcherButton) setTimeout(() => {
231
- launcherButton.focus();
238
+ focusLauncherButton();
232
239
  });
233
240
  };
234
- }, [enablePresence, launcherButtonRef]);
241
+ }, [
242
+ enablePresence,
243
+ launcherButtonRef,
244
+ focusLauncherButton
245
+ ]);
235
246
  (0, react.useEffect)(() => {
236
247
  if (!enableDialogElement) {
237
248
  const initialFocus = (focusContainerElement) => {
@@ -467,7 +478,7 @@ Modal.propTypes = {
467
478
  hasScrollingContent: prop_types.default.bool,
468
479
  id: prop_types.default.string,
469
480
  isFullWidth: prop_types.default.bool,
470
- launcherButtonRef: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.shape({ current: prop_types.default.oneOfType([typeof HTMLButtonElement !== "undefined" ? prop_types.default.instanceOf(HTMLButtonElement) : prop_types.default.any, prop_types.default.oneOf([null])]).isRequired })]),
481
+ launcherButtonRef: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.shape({ current: prop_types.default.oneOfType([typeof HTMLElement !== "undefined" ? prop_types.default.instanceOf(HTMLElement) : prop_types.default.any, prop_types.default.oneOf([null])]).isRequired })]),
471
482
  loadingDescription: prop_types.default.string,
472
483
  loadingIconDescription: prop_types.default.string,
473
484
  loadingStatus: prop_types.default.oneOf([
@@ -273,7 +273,7 @@ const NumberInput = react.default.forwardRef((props, forwardRef) => {
273
273
  value: allowEmpty && event.target.value === "" ? "" : Number(event.target.value),
274
274
  direction: value < event.target.value ? "up" : "down"
275
275
  };
276
- setValue(state.value);
276
+ if (controlledValue === void 0) setValue(state.value);
277
277
  if (onChange) onChange(event, state);
278
278
  return;
279
279
  }
@@ -316,7 +316,9 @@ const NumberInput = react.default.forwardRef((props, forwardRef) => {
316
316
  value: newValue,
317
317
  direction
318
318
  };
319
- if (type === "number") setValue(state.value);
319
+ if (type === "number") {
320
+ if (controlledValue === void 0) setValue(state.value);
321
+ }
320
322
  if (type === "text") {
321
323
  const formattedNewValue = format(newValue);
322
324
  const parsedFormattedNewValue = numberParser.parse(formattedNewValue);
@@ -274,11 +274,11 @@ const Slider = (props) => {
274
274
  DRAG_EVENT_TYPES.forEach((element) => {
275
275
  elementRef.current?.ownerDocument.removeEventListener(element, handleDrag);
276
276
  });
277
- setState({
278
- needsOnRelease: true,
279
- isValid: true,
280
- isValidUpper: true
281
- });
277
+ setState({ needsOnRelease: true });
278
+ };
279
+ const getValidityUpdateForHandle = (handle, validity) => {
280
+ if (typeof invalid !== "undefined") return {};
281
+ return handle === "upper" ? { isValidUpper: validity } : { isValid: validity };
282
282
  };
283
283
  /**
284
284
  * Handles a "drag" event by recalculating the value/thumb and setting state
@@ -303,7 +303,7 @@ const Slider = (props) => {
303
303
  else setState({
304
304
  value: nearestStepValue(value),
305
305
  left,
306
- isValid: true
306
+ ...getValidityUpdateForHandle("lower", true)
307
307
  });
308
308
  setState({
309
309
  correctedValue: null,
@@ -342,7 +342,7 @@ const Slider = (props) => {
342
342
  setState({
343
343
  value: nearestStepValue(value),
344
344
  left,
345
- isValid: true
345
+ ...getValidityUpdateForHandle("lower", true)
346
346
  });
347
347
  }
348
348
  setState({
@@ -399,9 +399,7 @@ const Slider = (props) => {
399
399
  const targetValue = Number.parseFloat(input.value);
400
400
  const validity = !isNaN(targetValue);
401
401
  const handlePosition = input.dataset.handlePosition;
402
- if (handlePosition === "lower") setState({ isValid: validity });
403
- else if (handlePosition === "upper") setState({ isValidUpper: validity });
404
- setState({ isValid: validity });
402
+ setState(getValidityUpdateForHandle(handlePosition ?? "lower", validity));
405
403
  if (validity) {
406
404
  const adjustedValue = handlePosition ? getAdjustedValueForPosition({
407
405
  handle: handlePosition,
@@ -511,22 +509,22 @@ const Slider = (props) => {
511
509
  if (handle === "lower") setState({
512
510
  value: valueUpper && newValue > valueUpper ? valueUpper : newValue,
513
511
  left: valueUpper && newValue > valueUpper ? leftUpper : newLeft,
514
- isValid: true
512
+ ...getValidityUpdateForHandle(handle, true)
515
513
  });
516
514
  else setState({
517
515
  valueUpper: value && newValue < value ? value : newValue,
518
516
  leftUpper: value && newValue < value ? left : newLeft,
519
- isValidUpper: true
517
+ ...getValidityUpdateForHandle(handle, true)
520
518
  });
521
519
  };
522
520
  const setValueForHandle = (handle, value) => {
523
521
  if (handle === "lower") setState({
524
522
  value,
525
- isValid: true
523
+ ...getValidityUpdateForHandle(handle, true)
526
524
  });
527
525
  else setState({
528
526
  valueUpper: value,
529
- isValidUpper: true
527
+ ...getValidityUpdateForHandle(handle, true)
530
528
  });
531
529
  };
532
530
  const isValidValueForPosition = ({ handle, value: newValue, min, max }) => {
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.107.0-rc.0",
4
+ "version": "1.107.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -53,9 +53,9 @@
53
53
  "dependencies": {
54
54
  "@babel/runtime": "^7.27.3",
55
55
  "@carbon/feature-flags": "^1.3.0",
56
- "@carbon/icons-react": "^11.80.0-rc.0",
56
+ "@carbon/icons-react": "^11.80.0",
57
57
  "@carbon/layout": "^11.52.0",
58
- "@carbon/styles": "^1.106.0-rc.0",
58
+ "@carbon/styles": "^1.106.0",
59
59
  "@carbon/utilities": "^0.19.0",
60
60
  "@floating-ui/react": "^0.27.4",
61
61
  "@ibm/telemetry-js": "^1.5.0",
@@ -79,14 +79,14 @@
79
79
  "@babel/preset-react": "^7.27.1",
80
80
  "@babel/preset-typescript": "^7.27.1",
81
81
  "@carbon/test-utils": "^10.41.0",
82
- "@carbon/themes": "^11.73.0-rc.0",
82
+ "@carbon/themes": "^11.73.0",
83
83
  "@figma/code-connect": "^1.4.2",
84
84
  "@stackblitz/sdk": "^1.11.0",
85
- "@storybook/addon-a11y": "^9.1.8",
86
- "@storybook/addon-docs": "^9.1.8",
87
- "@storybook/addon-links": "^9.1.8",
88
- "@storybook/builder-vite": "^9.1.8",
89
- "@storybook/react-vite": "^9.1.8",
85
+ "@storybook/addon-a11y": "^10.3.5",
86
+ "@storybook/addon-docs": "^10.3.5",
87
+ "@storybook/addon-links": "^10.3.5",
88
+ "@storybook/builder-vite": "^10.3.5",
89
+ "@storybook/react-vite": "^10.3.5",
90
90
  "@types/react-is": "^19.0.0",
91
91
  "@types/use-sync-external-store": "^1",
92
92
  "@vitejs/plugin-react": "^5.0.0",
@@ -106,7 +106,7 @@
106
106
  "remark-gfm": "^4.0.0",
107
107
  "rimraf": "^6.0.1",
108
108
  "sass": "^1.93.2",
109
- "storybook": "^9.1.8",
109
+ "storybook": "^10.3.5",
110
110
  "storybook-addon-accessibility-checker": ">=9.2.0-rc.3",
111
111
  "stream-browserify": "^3.0.0",
112
112
  "tsdown": "^0.21.0",
@@ -125,5 +125,5 @@
125
125
  "**/*.scss",
126
126
  "**/*.css"
127
127
  ],
128
- "gitHead": "32de263c201d0424d86bc47d97d1c085abecb24e"
128
+ "gitHead": "13c5c427ad4413aea5184a0a593e6452bc163e4a"
129
129
  }