diff --git a/package.json b/package.json index 5f7b46c629..b7e3e20f55 100644 --- a/package.json +++ b/package.json @@ -124,9 +124,10 @@ "keycode": "^2.2.0", "prop-types": "^15.6.1", "prop-types-extra": "^1.0.1", - "react-overlays": "^0.9.3", + "react-is": "^18.3.1", + "react-overlays": "^5.2.0", "react-prop-types": "^0.4.0", - "react-transition-group": "^2.0.0", + "react-transition-group": "^4.4.0", "uncontrollable": "^5.0.0", "warning": "^3.0.0" }, diff --git a/patches/react-overlays+0.9.3.patch b/patches/react-overlays+0.9.3.patch deleted file mode 100644 index 5539968862..0000000000 --- a/patches/react-overlays+0.9.3.patch +++ /dev/null @@ -1,35 +0,0 @@ -diff --git a/node_modules/react-overlays/lib/Modal.js b/node_modules/react-overlays/lib/Modal.js -index 9fbba3b..c876341 100644 ---- a/node_modules/react-overlays/lib/Modal.js -+++ b/node_modules/react-overlays/lib/Modal.js -@@ -576,7 +576,7 @@ var _initialiseProps = function _initialiseProps() { - }; - - this.handleDocumentKeyDown = function (e) { -- if (_this2.props.keyboard && e.keyCode === 27 && _this2.isTopModal()) { -+ if (_this2.props.keyboard && (e.keyCode === 27 || e.key === 'Escape') && _this2.isTopModal()) { - if (_this2.props.onEscapeKeyDown) { - _this2.props.onEscapeKeyDown(e); - } -@@ -586,7 +586,7 @@ var _initialiseProps = function _initialiseProps() { - }; - - this.handleDocumentKeyUp = function (e) { -- if (_this2.props.keyboard && e.keyCode === 27 && _this2.isTopModal()) { -+ if (_this2.props.keyboard && (e.keyCode === 27 || e.key === 'Escape') && _this2.isTopModal()) { - if (_this2.props.onEscapeKeyUp) { - _this2.props.onEscapeKeyUp(e); - } -diff --git a/node_modules/react-overlays/lib/RootCloseWrapper.js b/node_modules/react-overlays/lib/RootCloseWrapper.js -index 683e1cf..c561092 100644 ---- a/node_modules/react-overlays/lib/RootCloseWrapper.js -+++ b/node_modules/react-overlays/lib/RootCloseWrapper.js -@@ -115,7 +115,7 @@ var RootCloseWrapper = function (_React$Component) { - return; - } - -- if (e.keyCode === escapeKeyCode && _this.props.onRootClose) { -+ if ((e.keyCode === escapeKeyCode || e.key === 'Escape') && _this.props.onRootClose) { - _this.props.onRootClose(e); - } - }; diff --git a/patches/react-overlays+5.2.1.patch b/patches/react-overlays+5.2.1.patch new file mode 100644 index 0000000000..3789e904b2 --- /dev/null +++ b/patches/react-overlays+5.2.1.patch @@ -0,0 +1,52 @@ +diff --git a/node_modules/react-overlays/cjs/Modal.js b/node_modules/react-overlays/cjs/Modal.js +index 2247ad0..71c9f4a 100644 +--- a/node_modules/react-overlays/cjs/Modal.js ++++ b/node_modules/react-overlays/cjs/Modal.js +@@ -215,7 +215,7 @@ var Modal = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) { + } + }); + var handleDocumentKeyDown = (0, _useEventCallback["default"])(function (e) { +- if (keyboard && e.keyCode === 27 && modal.isTopModal()) { ++ if (keyboard && (e.keyCode === 27 || e.key === 'Escape') && modal.isTopModal()) { + onEscapeKeyDown == null ? void 0 : onEscapeKeyDown(e); + + if (!e.defaultPrevented) { +diff --git a/node_modules/react-overlays/cjs/useRootClose.js b/node_modules/react-overlays/cjs/useRootClose.js +index 3fc40a7..cc16674 100644 +--- a/node_modules/react-overlays/cjs/useRootClose.js ++++ b/node_modules/react-overlays/cjs/useRootClose.js +@@ -66,7 +66,7 @@ function useRootClose(ref, onRootClose, _temp) { + } + }); + var handleKeyUp = (0, _useEventCallback["default"])(function (e) { +- if (e.keyCode === escapeKeyCode) { ++ if (e.keyCode === escapeKeyCode || e.key === 'Escape') { + onClose(e); + } + }); +diff --git a/node_modules/react-overlays/esm/Modal.js b/node_modules/react-overlays/esm/Modal.js +index 42f79e4..a12ebac 100644 +--- a/node_modules/react-overlays/esm/Modal.js ++++ b/node_modules/react-overlays/esm/Modal.js +@@ -193,7 +193,7 @@ var Modal = /*#__PURE__*/forwardRef(function (_ref, ref) { + } + }); + var handleDocumentKeyDown = useEventCallback(function (e) { +- if (keyboard && e.keyCode === 27 && modal.isTopModal()) { ++ if (keyboard && (e.keyCode === 27 || e.key === 'Escape') && modal.isTopModal()) { + onEscapeKeyDown == null ? void 0 : onEscapeKeyDown(e); + + if (!e.defaultPrevented) { +diff --git a/node_modules/react-overlays/esm/useRootClose.js b/node_modules/react-overlays/esm/useRootClose.js +index 8c7882c..30d0d56 100644 +--- a/node_modules/react-overlays/esm/useRootClose.js ++++ b/node_modules/react-overlays/esm/useRootClose.js +@@ -53,7 +53,7 @@ function useRootClose(ref, onRootClose, _temp) { + } + }); + var handleKeyUp = useEventCallback(function (e) { +- if (e.keyCode === escapeKeyCode) { ++ if (e.keyCode === escapeKeyCode || e.key === 'Escape') { + onClose(e); + } + }); diff --git a/src/CarouselItem.js b/src/CarouselItem.js index 4c7d0eeda1..e4a11abbf8 100644 --- a/src/CarouselItem.js +++ b/src/CarouselItem.js @@ -1,7 +1,6 @@ import classNames from 'classnames'; import React from 'react'; import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; import transition from 'dom-helpers/transition'; const propTypes = { @@ -29,6 +28,7 @@ class CarouselItem extends React.Component { direction: null }; + this.containerRef = React.createRef(); this.isUnmounted = false; } @@ -43,7 +43,7 @@ class CarouselItem extends React.Component { const prevActive = prevProps.active; if (!active && prevActive) { - transition.end(ReactDOM.findDOMNode(this), this.handleAnimateOutEnd); + transition.end(this.containerRef.current, this.handleAnimateOutEnd); } if (active !== prevActive) { @@ -99,7 +99,13 @@ class CarouselItem extends React.Component { classes[this.state.direction] = true; } - return
; + return ( +
+ ); } } diff --git a/src/Collapse.js b/src/Collapse.js index 6d181aeec0..3fe186808e 100644 --- a/src/Collapse.js +++ b/src/Collapse.js @@ -11,6 +11,8 @@ import Transition, { import capitalize from './utils/capitalize'; import createChainedFunction from './utils/createChainedFunction'; +import { getElementRef, makeMergedRef } from './utils/mergeRefs'; +import withRef from './utils/withRef'; const MARGINS = { height: ['marginTop', 'marginBottom'], @@ -134,6 +136,12 @@ const defaultProps = { }; class Collapse extends React.Component { + constructor(props) { + super(props); + + this.childRef = React.createRef(); + } + getDimension() { return typeof this.props.dimension === 'function' ? this.props.dimension() @@ -146,31 +154,34 @@ class Collapse extends React.Component { } /* -- Expanding -- */ - handleEnter = elem => { - elem.style[this.getDimension()] = '0'; + handleEnter = () => { + this.childRef.current.style[this.getDimension()] = '0'; }; - handleEntering = elem => { + handleEntering = () => { const dimension = this.getDimension(); - elem.style[dimension] = this._getScrollDimensionValue(elem, dimension); + this.childRef.current.style[dimension] = this._getScrollDimensionValue( + this.childRef.current, + dimension + ); }; - handleEntered = elem => { - elem.style[this.getDimension()] = null; + handleEntered = () => { + this.childRef.current.style[this.getDimension()] = null; }; /* -- Collapsing -- */ - handleExit = elem => { + handleExit = () => { const dimension = this.getDimension(); - elem.style[dimension] = `${this.props.getDimensionValue( + this.childRef.current.style[dimension] = `${this.props.getDimensionValue( dimension, - elem + this.childRef.current )}px`; - triggerBrowserReflow(elem); + triggerBrowserReflow(this.childRef.current); }; - handleExiting = elem => { - elem.style[this.getDimension()] = '0'; + handleExiting = () => { + this.childRef.current.style[this.getDimension()] = '0'; }; render() { @@ -188,19 +199,34 @@ class Collapse extends React.Component { delete props.dimension; delete props.getDimensionValue; - const handleEnter = createChainedFunction(this.handleEnter, onEnter); + const handleEnter = createChainedFunction( + this.handleEnter, + withRef(onEnter, this.childRef) + ); const handleEntering = createChainedFunction( this.handleEntering, - onEntering + withRef(onEntering, this.childRef) ); - const handleEntered = createChainedFunction(this.handleEntered, onEntered); - const handleExit = createChainedFunction(this.handleExit, onExit); - const handleExiting = createChainedFunction(this.handleExiting, onExiting); + const handleEntered = createChainedFunction( + this.handleEntered, + withRef(onEntered, this.childRef) + ); + const handleExit = createChainedFunction( + this.handleExit, + withRef(onExit, this.childRef) + ); + const handleExiting = createChainedFunction( + this.handleExiting, + withRef(onExiting, this.childRef) + ); + + const ref = makeMergedRef([this.childRef, getElementRef(children)]); return ( React.cloneElement(children, { ...innerProps, + ref, className: classNames( className, children.props.className, diff --git a/src/Dropdown.js b/src/Dropdown.js index 1fd68d3c4a..f5db1aaae2 100644 --- a/src/Dropdown.js +++ b/src/Dropdown.js @@ -3,7 +3,6 @@ import activeElement from 'dom-helpers/activeElement'; import contains from 'dom-helpers/query/contains'; import React, { cloneElement } from 'react'; import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; import all from 'prop-types-extra/lib/all'; import elementType from 'prop-types-extra/lib/elementType'; import isRequiredForA11y from 'prop-types-extra/lib/isRequiredForA11y'; @@ -16,6 +15,7 @@ import { bsClass as setBsClass, prefix } from './utils/bootstrapUtils'; import createChainedFunction from './utils/createChainedFunction'; import { exclusiveRoles, requiredRoles } from './utils/PropTypes'; import ValidComponentChildren from './utils/ValidComponentChildren'; +import { getElementRef, makeMergedRef } from './utils/mergeRefs'; const TOGGLE_ROLE = DropdownToggle.defaultProps.bsRole; const MENU_ROLE = DropdownMenu.defaultProps.bsRole; @@ -123,6 +123,7 @@ class Dropdown extends React.Component { this.handleClose = this.handleClose.bind(this); this._focusInDropdown = false; + this.containerRef = React.createRef(); this.lastOpenEventType = null; } @@ -133,7 +134,7 @@ class Dropdown extends React.Component { UNSAFE_componentWillUpdate(nextProps) { if (!nextProps.open && this.props.open) { this._focusInDropdown = contains( - ReactDOM.findDOMNode(this.menu), + this.containerRef.current.querySelector('[role=menu]'), activeElement(document) ); } @@ -158,7 +159,9 @@ class Dropdown extends React.Component { } focus() { - const toggle = ReactDOM.findDOMNode(this.toggle); + const toggle = this.containerRef.current.querySelector( + '[role=button][aria-haspopup]' + ); if (toggle && toggle.focus) { toggle.focus(); @@ -237,11 +240,12 @@ class Dropdown extends React.Component { } renderMenu(child, { id, onSelect, rootCloseEvent, ...props }) { - let ref = c => { - this.menu = c; - }; - - ref = createChainedFunction(child.ref, ref); + const ref = makeMergedRef([ + el => { + this.menu = el; + }, + getElementRef(child) + ]); return cloneElement(child, { ...props, @@ -259,15 +263,8 @@ class Dropdown extends React.Component { } renderToggle(child, props) { - let ref = c => { - this.toggle = c; - }; - - ref = createChainedFunction(child.ref, ref); - return cloneElement(child, { ...props, - ref, bsClass: prefix(props, 'toggle'), onClick: createChainedFunction(child.props.onClick, this.handleClick), onKeyDown: createChainedFunction( @@ -311,31 +308,33 @@ class Dropdown extends React.Component { // underlying component, to allow it to render size and style variants. return ( - - {ValidComponentChildren.map(children, child => { - switch (child.props.bsRole) { - case TOGGLE_ROLE: - return this.renderToggle(child, { - id, - disabled, - open, - role, - bsClass - }); - case MENU_ROLE: - return this.renderMenu(child, { - id, - open, - pullRight, - bsClass, - onSelect, - rootCloseEvent - }); - default: - return child; - } - })} - +
+ + {ValidComponentChildren.map(children, child => { + switch (child.props.bsRole) { + case TOGGLE_ROLE: + return this.renderToggle(child, { + id, + disabled, + open, + role, + bsClass + }); + case MENU_ROLE: + return this.renderMenu(child, { + id, + open, + pullRight, + bsClass, + onSelect, + rootCloseEvent + }); + default: + return child; + } + })} + +
); } } diff --git a/src/DropdownMenu.js b/src/DropdownMenu.js index 8d4d9fad87..7beaeba0c6 100644 --- a/src/DropdownMenu.js +++ b/src/DropdownMenu.js @@ -1,8 +1,7 @@ import classNames from 'classnames'; import React from 'react'; import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; -import RootCloseWrapper from 'react-overlays/lib/RootCloseWrapper'; +import { useRootClose } from 'react-overlays'; import { bsClass, @@ -33,10 +32,12 @@ class DropdownMenu extends React.Component { this.handleRootClose = this.handleRootClose.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this); + + this.menuRef = React.createRef(); } getFocusableMenuItems() { - const node = ReactDOM.findDOMNode(this); + const node = this.menuRef.current; if (!node) { return []; } @@ -117,8 +118,10 @@ class DropdownMenu extends React.Component { disabled={!open} onRootClose={this.handleRootClose} event={rootCloseEvent} + menuRef={this.menuRef} >
    { + const childRef = useRef(null); + + const setChildRef = useMergedRef([childRef, getElementRef(children), ref]); + + // Transition doesn't pass the node as the first parameter of these callbacks when nodeRef is used, + // so we add that ourselves to keep the API for Fade consistent + const callbacks = { + onEnter: useMemo(() => withRef(onEnter, childRef), [onEnter]), + onEntering: useMemo(() => withRef(onEntering, childRef), [onEntering]), + onEntered: useMemo(() => withRef(onEntered, childRef), [onEntered]), + onExit: useMemo(() => withRef(onExit, childRef), [onExit]), + onExiting: useMemo(() => withRef(onExiting, childRef), [onExiting]), + onExited: useMemo(() => withRef(onExited, childRef), [onExited]) + }; return ( - + {(status, innerProps) => React.cloneElement(children, { ...innerProps, + ref: setChildRef, className: classNames( 'fade', className, @@ -94,9 +129,9 @@ class Fade extends React.Component { ); } -} +); +Fade.displayName = 'Fade'; Fade.propTypes = propTypes; -Fade.defaultProps = defaultProps; export default Fade; diff --git a/src/Modal.js b/src/Modal.js index c643d6dc25..943cac0bb4 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -5,10 +5,8 @@ import canUseDOM from 'dom-helpers/util/inDOM'; import getScrollbarSize from 'dom-helpers/util/scrollbarSize'; import React from 'react'; import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; -import BaseModal from 'react-overlays/lib/Modal'; -import isOverflowing from 'react-overlays/lib/utils/isOverflowing'; -import elementType from 'prop-types-extra/lib/elementType'; +import isOverflowing from 'react-overlays/isOverflowing'; +import BaseModal from 'react-overlays/Modal'; import Fade from './Fade'; import Body from './ModalBody'; @@ -38,6 +36,11 @@ const propTypes = { */ backdropClassName: PropTypes.string, + /** + * Add optional styles to .modal-backdrop + */ + backdropStyle: PropTypes.object, + /** * Close the modal when escape key is pressed */ @@ -48,13 +51,6 @@ const propTypes = { */ animation: PropTypes.bool, - /** - * A Component type that provides the modal content Markup. This is a useful - * prop when you want to use your own styles and markup to create a custom - * modal component. - */ - dialogComponentClass: elementType, - /** * When `true` The modal will automatically shift focus to itself when it * opens, and replace it to the last focused element when it closes. @@ -115,28 +111,33 @@ const propTypes = { /** * Callback fired after the Modal finishes transitioning out */ - onExited: PropTypes.func, - - /** - * @private - */ - container: BaseModal.propTypes.container + onExited: PropTypes.func }; const defaultProps = { - ...BaseModal.defaultProps, - animation: true, - dialogComponentClass: ModalDialog + show: false, + backdrop: true, + keyboard: true, + autoFocus: true, + enforceFocus: true, + restoreFocus: true, + onHide: function onHide() {}, + renderBackdrop: function renderBackdrop(props) { + return
    ; + }, + animation: true }; /* eslint-disable no-use-before-define, react/no-multi-comp */ -function DialogTransition(props) { - return ; -} +const DialogTransition = React.forwardRef((props, ref) => ( + +)); +DialogTransition.displayName = 'DialogTransition'; -function BackdropTransition(props) { - return ; -} +const BackdropTransition = React.forwardRef((props, ref) => ( + +)); +BackdropTransition.displayName = 'BackdropTransition'; /* eslint-enable no-use-before-define */ @@ -200,13 +201,11 @@ class Modal extends React.Component { return; } - const dialogNode = this._modal.getDialogElement(); + const dialogNode = this._modal.dialog; const dialogHeight = dialogNode.scrollHeight; const document = ownerDocument(dialogNode); - const bodyIsOverflowing = isOverflowing( - ReactDOM.findDOMNode(this.props.container || document.body) - ); + const bodyIsOverflowing = isOverflowing(document.body); const modalIsOverflowing = dialogHeight > document.documentElement.clientHeight; @@ -228,9 +227,9 @@ class Modal extends React.Component { const { backdrop, backdropClassName, + backdropStyle, animation, show, - dialogComponentClass: Dialog, className, style, children, // Just in case this get added to BaseModal propTypes. @@ -253,24 +252,39 @@ class Modal extends React.Component { transition={animation ? DialogTransition : undefined} backdrop={backdrop} backdropTransition={animation ? BackdropTransition : undefined} - backdropClassName={classNames( - prefix(props, 'backdrop'), - backdropClassName, - inClassName + renderBackdrop={backdropProps => ( +
    + )} + renderDialog={renderProps => ( + + {children} + )} onEntering={createChainedFunction(onEntering, this.handleEntering)} onExited={createChainedFunction(onExited, this.handleExited)} - > - - {children} - - + /> ); } diff --git a/src/ModalDialog.js b/src/ModalDialog.js index 34b0b5a5a3..c8306ebe16 100644 --- a/src/ModalDialog.js +++ b/src/ModalDialog.js @@ -2,15 +2,11 @@ import classNames from 'classnames'; import React from 'react'; import PropTypes from 'prop-types'; -import { - bsClass, - bsSizes, - getClassSet, - prefix, - splitBsProps -} from './utils/bootstrapUtils'; +import { getClassSet, prefix, splitBsProps } from './utils/bootstrapUtils'; import { Size } from './utils/StyleConfig'; +const SIZES = [Size.LARGE, Size.SMALL]; + const propTypes = { /** * A css class to apply to the Modal dialog DOM node. @@ -20,20 +16,31 @@ const propTypes = { /** * A method to run for the mousedown event on the dialog. */ - handleDialogMouseDown: PropTypes.func + handleDialogMouseDown: PropTypes.func, + + bsClass: PropTypes.string, + bsSize: PropTypes.oneOf(SIZES) }; -class ModalDialog extends React.Component { - render() { - const { +const ModalDialog = React.forwardRef( + ( + { dialogClassName, className, style, children, handleDialogMouseDown, + bsClass = 'modal', + bsSize, ...props - } = this.props; - const [bsProps, elementProps] = splitBsProps(props); + }, + ref + ) => { + const [bsProps, elementProps] = splitBsProps({ + ...props, + bsClass, + bsSize + }); const bsClassName = prefix(bsProps); @@ -47,6 +54,7 @@ class ModalDialog extends React.Component { return (
    ); } -} +); +ModalDialog.displayName = 'ModalDialog'; ModalDialog.propTypes = propTypes; +ModalDialog.SIZES = SIZES; -export default bsClass('modal', bsSizes([Size.LARGE, Size.SMALL], ModalDialog)); +export default ModalDialog; diff --git a/src/Nav.js b/src/Nav.js index 9b20a2476e..439a4172c6 100644 --- a/src/Nav.js +++ b/src/Nav.js @@ -1,7 +1,6 @@ import classNames from 'classnames'; import React, { cloneElement, useContext } from 'react'; import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; import all from 'prop-types-extra/lib/all'; import warning from 'warning'; @@ -98,6 +97,12 @@ const defaultProps = { }; class Nav extends React.Component { + constructor(props) { + super(props); + + this.containerRef = React.createRef(); + } + componentDidUpdate() { if (!this._needsRefocus) { return; @@ -115,7 +120,7 @@ class Nav extends React.Component { const childrenArray = ValidComponentChildren.toArray(children); const activeChildIndex = childrenArray.indexOf(activeChild); - const childNodes = ReactDOM.findDOMNode(this).children; + const childNodes = this.containerRef.current.children; const activeNode = childNodes && childNodes[activeChildIndex]; if (!activeNode || !activeNode.firstChild) { @@ -309,6 +314,7 @@ class Nav extends React.Component { return (
      - {child} + + {({ arrowProps, placement, props: overlayProps }) => { + const positionTop = + overlayProps && overlayProps.style && overlayProps.style.top; + const positionLeft = + overlayProps && overlayProps.style && overlayProps.style.left; + + // Passing the positionX and arrowOffsetX props is redundant with the style props, + // but it matches how react-overlays used to behave. + return React.cloneElement(child, { + ...overlayProps, + placement, + positionTop, + positionLeft, + arrowOffsetTop: + arrowProps && arrowProps.style && arrowProps.style.top, + arrowOffsetLeft: + arrowProps && arrowProps.style && arrowProps.style.left, + arrowRef: arrowProps.ref, + arrowStyle: arrowProps.style, + style: { + ...child.props.style, + ...overlayProps.style, + left: positionLeft, + top: positionTop + } + }); + }} ); } diff --git a/src/OverlayTrigger.js b/src/OverlayTrigger.js index 3adda67446..ba40324d0c 100644 --- a/src/OverlayTrigger.js +++ b/src/OverlayTrigger.js @@ -115,6 +115,8 @@ class OverlayTrigger extends React.Component { this.state = { show: props.defaultOverlayShown }; + + this.triggerRef = React.createRef(); } componentWillUnmount() { @@ -207,7 +209,7 @@ class OverlayTrigger extends React.Component { {...props} show={this.state.show} onHide={this.handleHide} - target={this} + target={this.triggerRef} > {overlay} @@ -292,7 +294,9 @@ class OverlayTrigger extends React.Component { return ( <> - {cloneElement(child, triggerProps)} +
      + {cloneElement(child, triggerProps)} +
      {ReactDOM.createPortal(this.makeOverlay(overlay, props), document.body)} ); diff --git a/src/Popover.js b/src/Popover.js index 8b78ceef3c..b69772ccd8 100644 --- a/src/Popover.js +++ b/src/Popover.js @@ -49,25 +49,24 @@ const propTypes = { title: PropTypes.node }; -const defaultProps = { - placement: 'right' -}; - -class Popover extends React.Component { - render() { - const { - placement, +const Popover = React.forwardRef( + ( + { + placement = 'right', positionTop, positionLeft, arrowOffsetTop, arrowOffsetLeft, + arrowRef, + arrowStyle, title, className, style, children, ...props - } = this.props; - + }, + ref + ) => { const [bsProps, elementProps] = splitBsProps(props); const classes = { @@ -82,19 +81,21 @@ class Popover extends React.Component { ...style }; - const arrowStyle = { + const combinedArrowStyle = { top: arrowOffsetTop, - left: arrowOffsetLeft + left: arrowOffsetLeft, + ...arrowStyle }; return (
      -
      +
      {title &&

      {title}

      } @@ -102,9 +103,8 @@ class Popover extends React.Component {
      ); } -} +); Popover.propTypes = propTypes; -Popover.defaultProps = defaultProps; export default bsClass('popover', Popover); diff --git a/src/TabPane.js b/src/TabPane.js index 08895bdb2b..14334790cf 100644 --- a/src/TabPane.js +++ b/src/TabPane.js @@ -91,6 +91,8 @@ class TabPane extends React.Component { this.handleExited = this.handleExited.bind(this); this.in = false; + + this.paneRef = React.createRef(); } componentDidMount() { @@ -220,6 +222,7 @@ class TabPane extends React.Component { const pane = (
      {pane} diff --git a/src/Tooltip.js b/src/Tooltip.js index 5a9a7cdaa4..aa07909d9f 100644 --- a/src/Tooltip.js +++ b/src/Tooltip.js @@ -44,24 +44,23 @@ const propTypes = { arrowOffsetLeft: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }; -const defaultProps = { - placement: 'right' -}; - -class Tooltip extends React.Component { - render() { - const { - placement, +const Tooltip = React.forwardRef( + ( + { + placement = 'right', positionTop, positionLeft, arrowOffsetTop, arrowOffsetLeft, + arrowRef, + arrowStyle, className, style, children, ...props - } = this.props; - + }, + ref + ) => { const [bsProps, elementProps] = splitBsProps(props); const classes = { @@ -75,27 +74,32 @@ class Tooltip extends React.Component { ...style }; - const arrowStyle = { + const combinedArrowStyle = { top: arrowOffsetTop, - left: arrowOffsetLeft + left: arrowOffsetLeft, + ...arrowStyle }; return (
      -
      +
      {children}
      ); } -} - +); +Tooltip.displayName = 'Tooltip'; Tooltip.propTypes = propTypes; -Tooltip.defaultProps = defaultProps; export default bsClass('tooltip', Tooltip); diff --git a/src/utils/bootstrapUtils.js b/src/utils/bootstrapUtils.js index 5735d17a21..da59292407 100644 --- a/src/utils/bootstrapUtils.js +++ b/src/utils/bootstrapUtils.js @@ -1,6 +1,7 @@ // TODO: The publicly exposed parts of this should be in lib/BootstrapUtils. import invariant from 'invariant'; +import { isValidElementType } from 'react-is'; import PropTypes from 'prop-types'; import { SIZE_MAP } from './StyleConfig'; @@ -8,9 +9,12 @@ import { SIZE_MAP } from './StyleConfig'; function curry(fn) { return (...args) => { let last = args[args.length - 1]; - if (typeof last === 'function') { + if (typeof last !== 'string' && isValidElementType(last)) { + // Supports calling curry(...args, Component) return fn(...args); } + + // Supports calling curry(...args)(Component) return Component => fn(...args, Component); }; } diff --git a/src/utils/mergeRefs.js b/src/utils/mergeRefs.js new file mode 100644 index 0000000000..0497882706 --- /dev/null +++ b/src/utils/mergeRefs.js @@ -0,0 +1,41 @@ +import React, { useCallback } from 'react'; + +const hasRefProp = parseInt(React.version, 10) >= 19; + +/** + * Given any number of React ref callbacks or ref objects, returns a new ref callback + * that sets all of the provided refs. This result isn't memoized, so useMergedRef + * should be preferred. + */ +export function makeMergedRef(refs) { + return el => { + refs.forEach(ref => { + if (typeof ref === 'function') { + ref(el); + } else if (ref != null) { + ref.current = el; + } + }); + }; +} + +/** + * Given any number of React ref callbacks or ref objects, returns a new ref callback + * that sets all of the provided refs. + */ +export function useMergedRef(refs) { + return useCallback(makeMergedRef(refs), refs); +} + +/** + * Given a React element, returns the ref of that object in a way that's compatible + * with different React versions. + */ +export function getElementRef(reactElement) { + if (!reactElement) { + return null; + } + + // Accessing reactElement.ref prints a warning in React 19 and will likely be removed eventually + return hasRefProp ? reactElement.props.ref : reactElement.ref; +} diff --git a/src/utils/withRef.js b/src/utils/withRef.js new file mode 100644 index 0000000000..2871a09b54 --- /dev/null +++ b/src/utils/withRef.js @@ -0,0 +1,6 @@ +/** + * Given a function, returns that function with the first parameter set to the referenced element. + */ +export default function withRef(callback, ref) { + return callback ? (...args) => callback(ref.current, ...args) : undefined; +} diff --git a/test/DropdownSpec.js b/test/DropdownSpec.js index 314e937bdc..cc05ffaa74 100644 --- a/test/DropdownSpec.js +++ b/test/DropdownSpec.js @@ -408,7 +408,6 @@ describe('', () => { outerRef.dropdown.menu.should.exist; outerRef.toggle.should.exist; - outerRef.dropdown.toggle.should.exist; }); describe('focusable state', () => { diff --git a/test/FadeSpec.js b/test/FadeSpec.js index 8f7e1f87da..6a50c9d74b 100644 --- a/test/FadeSpec.js +++ b/test/FadeSpec.js @@ -21,21 +21,22 @@ describe('Fade', () => { }); it('Should default to hidden', () => { - let instance; - render( - (instance = element)}>Panel content - ); + render(Panel content); - assert.ok(instance.fade.props.in === false); + assert.equal(screen.getByText('Panel content').className, 'fade'); }); - it('Should always have the "fade" class', () => { + it('Should forward its ref to the child element', () => { let instance; render( (instance = element)}>Panel content ); - assert.ok(instance.fade.props.in === false); + assert.equal(instance.fade, screen.getByText('Panel content')); + }); + + it('Should always have the "fade" class', () => { + render(Panel content); assert.equal(screen.getByText('Panel content').className, 'fade'); }); @@ -51,7 +52,7 @@ describe('Fade', () => { done(); } - assert.ok(instance.fade.props.in === false); + assert.equal(screen.getByText('Panel content').className, 'fade'); act(() => { instance.setState({ in: true, onEntering }); diff --git a/test/ModalSpec.js b/test/ModalSpec.js index ba3948f4c3..d004a798e0 100644 --- a/test/ModalSpec.js +++ b/test/ModalSpec.js @@ -20,7 +20,7 @@ describe('', () => { ); - assert.ok(instance._modal.getDialogElement().querySelector('strong')); + assert.ok(instance._modal.dialog.querySelector('strong')); }); it('Should close the modal when the modal dialog is clicked', done => { @@ -35,7 +35,7 @@ describe('', () => { ); - const dialog = instance._modal.getDialogElement(); + const dialog = instance._modal.dialog; userEvent.click(dialog); }); @@ -54,7 +54,7 @@ describe('', () => { ); - const dialog = instance._modal.getDialogElement(); + const dialog = instance._modal.dialog; userEvent.click(dialog); @@ -74,15 +74,13 @@ describe('', () => { ); - const button = instance._modal - .getDialogElement() - .getElementsByClassName('close')[0]; + const button = instance._modal.dialog.getElementsByClassName('close')[0]; userEvent.click(button); }); it('Should close the modal when the escape key is pressed with keyboard=true', async () => { - const handleHide = sinon.spy(); + const handleHide = sinon.spy(() => {}); render( @@ -125,7 +123,7 @@ describe('', () => { ); - const dialog = instance._modal.getDialogElement(); + const dialog = instance._modal.dialog; assert.ok(dialog.className.match(/\bmymodal\b/)); }); @@ -144,7 +142,7 @@ describe('', () => { ); - const modal = instance._modal.getDialogElement(); + const modal = instance._modal.dialog; assert.ok(modal.className.match(/\bmymodal\b/)); assert.ok(modal.children[0].className.match(/\bmymodal-dialog\b/)); @@ -180,9 +178,9 @@ describe('', () => { ); - const dialog = instance._modal - .getDialogElement() - .getElementsByClassName('modal-dialog')[0]; + const dialog = instance._modal.dialog.getElementsByClassName( + 'modal-dialog' + )[0]; assert.ok(dialog.className.match(/\bmodal-sm\b/)); }); @@ -201,7 +199,7 @@ describe('', () => { ); - const dialog = instance._modal.getDialogElement(); + const dialog = instance._modal.dialog; assert.ok(dialog.style.top === '1000px'); }); @@ -220,35 +218,11 @@ describe('', () => { ); - const dialog = instance._modal - .getDialogElement() - .querySelector('.modal-dialog'); + const dialog = instance._modal.dialog.querySelector('.modal-dialog'); assert.ok(dialog.className.match(/\btestCss\b/)); }); - it('Should use dialogComponentClass', () => { - const noOp = () => {}; - - function CustomDialog() { - return
      ; - } - - let instance; - render( - (instance = element)} - show - dialogComponentClass={CustomDialog} - onHide={noOp} - > - Message - - ); - - assert.equal(instance._modal.getDialogElement().className, 'custom-dialog'); - }); - it('Should pass transition callbacks to Transition', done => { let count = 0; const increment = () => { diff --git a/test/OverlayTriggerSpec.js b/test/OverlayTriggerSpec.js index 3ae8166cbc..23c8232114 100644 --- a/test/OverlayTriggerSpec.js +++ b/test/OverlayTriggerSpec.js @@ -1,7 +1,6 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useContext, useEffect } from 'react'; import OverlayTrigger from '../src/OverlayTrigger'; import Popover from '../src/Popover'; @@ -9,9 +8,11 @@ import Tooltip from '../src/Tooltip'; describe('', () => { // Swallow extra props. - const Div = ({ className, children }) => ( -
      {children}
      - ); + const Div = React.forwardRef(({ className, children }, ref) => ( +
      + {children} +
      + )); it('Should create OverlayTrigger element', () => { render( @@ -140,31 +141,36 @@ describe('', () => { }); it('Should forward requested context', async () => { - const contextTypes = { - key: PropTypes.string - }; + const TestContext = React.createContext(undefined); + TestContext.displayName = 'TestContext'; const contextSpy = sinon.spy(); - class ContextReader extends React.Component { - render() { - contextSpy(this.context.key); - return
      ; - } - } + const ContextReader = React.forwardRef((props, ref) => { + const context = useContext(TestContext); + const contextValue = context.key; + useEffect( + () => { + contextSpy(contextValue); + }, + [contextValue] + ); - ContextReader.contextTypes = contextTypes; + return
      ; + }); + ContextReader.displayName = 'ContextReader'; class ContextHolder extends React.Component { render() { return ( - }> - - + + }> + + + ); } } - ContextHolder.childContextTypes = contextTypes; render(); const overlayTrigger = screen.getByText('button'); @@ -290,7 +296,7 @@ describe('', () => { } return ( -
      +
      original @@ -299,9 +305,13 @@ describe('', () => { } } + const WrappedReplacedOverlay = React.forwardRef((props, ref) => ( + + )); + render( } + overlay={} trigger="click" rootClose > diff --git a/test/index.js b/test/index.js index adaa3351d2..3b8d18f81c 100644 --- a/test/index.js +++ b/test/index.js @@ -26,11 +26,6 @@ beforeEach(() => { return; } - if (msg.includes('findDOMNode')) { - // @hmhealey This is removed in React 19 - return; - } - console.error.threw = true; throw new Error(msg); }); diff --git a/yarn.lock b/yarn.lock index 80ea5abf38..3f1524ae8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -796,18 +796,16 @@ core-js "^2.5.7" regenerator-runtime "^0.12.0" -"@babel/runtime@^7.1.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/runtime@^7.12.5": version "7.28.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.2.tgz#2ae5a9d51cc583bd1f5673b3bb70d6d819682473" integrity sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA== +"@babel/runtime@^7.13.8", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.7.tgz#12022450c45a4da6d8d8287b18a4ff2ddb23f768" + integrity sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw== + "@babel/template@7.0.0-beta.51": version "7.0.0-beta.51" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.51.tgz#9602a40aebcf357ae9677e2532ef5fc810f5fbff" @@ -944,6 +942,18 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@popperjs/core@^2.11.6": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + +"@restart/hooks@^0.4.7": + version "0.4.16" + resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.16.tgz#95ae8ac1cc7e2bd4fed5e39800ff85604c6d59fb" + integrity sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w== + dependencies: + dequal "^2.0.3" + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -1030,6 +1040,18 @@ dependencies: undici-types "~7.10.0" +"@types/react@>=16.9.11": + version "19.2.17" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.17.tgz#dccac365baa0f1734ec270ff4b51c89465e8dc7f" + integrity sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw== + dependencies: + csstype "^3.2.2" + +"@types/warning@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.4.tgz#ebc0c83180dc83994d902bbd51ab0af8a445b1f9" + integrity sha512-CqN8MnISMwQbLJXO3doBAV4Yw9hx9/Pyr2rZ78+NfaCnhyRA/nKrpyk6E7mKw17ZOaQdLpK9GiUjrqLzBlN3sg== + "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": version "1.14.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" @@ -2180,6 +2202,11 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +csstype@^3.0.2, csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + custom-event@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" @@ -2373,16 +2400,17 @@ dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== -dom-helpers@^3.2.0, dom-helpers@^3.2.1, dom-helpers@^3.3.1: +dom-helpers@^3.2.0: version "3.3.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6" -dom-helpers@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" - integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== +dom-helpers@^5.0.1, dom-helpers@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== dependencies: - "@babel/runtime" "^7.1.2" + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" dom-serialize@^2.2.1: version "2.2.1" @@ -5191,13 +5219,22 @@ prop-types-extra@^1.0.1: react-is "^16.3.2" warning "^3.0.0" -prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2: +prop-types@^15.6.1, prop-types@^15.6.2: version "15.6.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: loose-envify "^1.3.1" object-assign "^4.1.1" +prop-types@^15.7.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -5316,6 +5353,11 @@ react-dom@^18.3.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-is@^16.3.2: version "16.4.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.2.tgz#84891b56c2b6d9efdee577cc83501dfc5ecead88" @@ -5329,17 +5371,19 @@ react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" -react-overlays@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.9.3.tgz#5bac8c1e9e7e057a125181dee2d784864dd62902" - integrity sha512-u2T7nOLnK+Hrntho4p0Nxh+BsJl0bl4Xuwj/Y0a56xywLMetgAfyjnDVrudLXsNcKGaspoC+t3C1V80W9QQTdQ== - dependencies: - classnames "^2.2.5" - dom-helpers "^3.2.1" - prop-types "^15.5.10" - prop-types-extra "^1.0.1" - react-transition-group "^2.2.1" - warning "^3.0.0" +react-overlays@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.2.1.tgz#49dc007321adb6784e1f212403f0fb37a74ab86b" + integrity sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA== + dependencies: + "@babel/runtime" "^7.13.8" + "@popperjs/core" "^2.11.6" + "@restart/hooks" "^0.4.7" + "@types/warning" "^3.0.0" + dom-helpers "^5.2.0" + prop-types "^15.7.2" + uncontrollable "^7.2.1" + warning "^4.0.3" react-prop-types@^0.4.0: version "0.4.0" @@ -5364,24 +5408,15 @@ react-test-renderer@^18.3.1: react-shallow-renderer "^16.15.0" scheduler "^0.23.2" -react-transition-group@^2.0.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.4.0.tgz#1d9391fabfd82e016f26fabd1eec329dbd922b5a" +react-transition-group@^4.4.0: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== dependencies: - dom-helpers "^3.3.1" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-lifecycles-compat "^3.0.4" - -react-transition-group@^2.2.1: - version "2.9.0" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" - integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== - dependencies: - dom-helpers "^3.4.0" + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" loose-envify "^1.4.0" prop-types "^15.6.2" - react-lifecycles-compat "^3.0.4" react@^18.3.1: version "18.3.1" @@ -5462,11 +5497,6 @@ regenerator-runtime@^0.12.0: version "0.12.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - regenerator-transform@^0.13.3: version "0.13.3" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb" @@ -6402,6 +6432,16 @@ uncontrollable@^5.0.0: dependencies: invariant "^2.2.4" +uncontrollable@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738" + integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ== + dependencies: + "@babel/runtime" "^7.6.3" + "@types/react" ">=16.9.11" + invariant "^2.2.4" + react-lifecycles-compat "^3.0.4" + undici-types@~7.10.0: version "7.10.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" @@ -6553,6 +6593,13 @@ warning@^3.0.0: dependencies: loose-envify "^1.0.0" +warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + watchpack@^2.4.1: version "2.4.4" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.4.tgz#473bda72f0850453da6425081ea46fc0d7602947"