diff --git a/docs-site/src/components/Examples/index.js b/docs-site/src/components/Examples/index.js
index 052ce886c..30329a1b4 100644
--- a/docs-site/src/components/Examples/index.js
+++ b/docs-site/src/components/Examples/index.js
@@ -45,7 +45,7 @@ import DisabledKeyboardNavigation from "../../examples/disabledKeyboardNavigatio
import ReadOnly from "../../examples/readOnly";
import ClearInput from "../../examples/clearInput";
import OnBlurCallbacks from "../../examples/onBlurCallbacks";
-import ConfigurePopper from "../../examples/configurePopper";
+import ConfigureFloatingUI from "../../examples/configureFloatingUI";
import Portal from "../../examples/portal";
import PortalById from "../../examples/portalById";
import WithPortalById from "../../examples/withPortalById";
@@ -159,17 +159,18 @@ export default class exampleComponents extends React.Component {
component: CloseOnScrollCallback,
},
{
- title: "Configure Popper Properties",
- component: ConfigurePopper,
+ title: "Configure Floating UI Properties",
+ component: ConfigureFloatingUI,
description: (
- Full docs for the popper can be found at{" "}
+ Full docs for the underlying library that manages the overlay used can
+ be found at{" "}
- popper.js.org
+ floating-ui.com
),
diff --git a/docs-site/src/examples/configurePopper.js b/docs-site/src/examples/configureFloatingUI.js
similarity index 55%
rename from docs-site/src/examples/configurePopper.js
rename to docs-site/src/examples/configureFloatingUI.js
index 591a2eb05..eb8311fd7 100644
--- a/docs-site/src/examples/configurePopper.js
+++ b/docs-site/src/examples/configureFloatingUI.js
@@ -8,17 +8,10 @@
popperPlacement="top-end"
popperModifiers={[
{
- name: "offset",
- options: {
- offset: [5, 10],
- },
- },
- {
- name: "preventOverflow",
- options: {
- rootBoundary: "viewport",
- tether: false,
- altAxis: true,
+ name: "myModifier",
+ fn(state) {
+ // Do something with the state
+ return state;
},
},
]}
diff --git a/jest.config.js b/jest.config.js
index 51267065e..acf5eebf7 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -8,7 +8,7 @@ module.exports = {
"!**/node_modules/**",
"!**/vendor/**",
],
- transformIgnorePatterns: ["/node_modules/(?!(@popperjs)|date-fns)"],
+ transformIgnorePatterns: ["/node_modules/(?!date-fns)"],
transform: {
"^.+\\.(js|jsx)$": "babel-jest",
"^.+\\.ts?$": "ts-jest",
diff --git a/package.json b/package.json
index 619e29259..e911453bb 100644
--- a/package.json
+++ b/package.json
@@ -104,12 +104,11 @@
"react-dom": "^16.9.0 || ^17 || ^18"
},
"dependencies": {
- "@popperjs/core": "^2.11.8",
+ "@floating-ui/react": "^0.26.2",
"classnames": "^2.2.6",
"date-fns": "^2.30.0",
"prop-types": "^15.7.2",
- "react-onclickoutside": "^6.13.0",
- "react-popper": "^2.3.0"
+ "react-onclickoutside": "^6.13.0"
},
"scripts": {
"eslint": "eslint --ext .js,.jsx src test",
diff --git a/rollup.config.js b/rollup.config.js
index 423761ed7..31c9c840f 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -22,7 +22,6 @@ const globals = {
react: "React",
"prop-types": "PropTypes",
"react-onclickoutside": "onClickOutside",
- "react-popper": "ReactPopper",
classnames: "classNames",
};
diff --git a/src/calendar.jsx b/src/calendar.jsx
index 5adf39214..0cad36898 100644
--- a/src/calendar.jsx
+++ b/src/calendar.jsx
@@ -76,7 +76,6 @@ export default class Calendar extends React.Component {
static propTypes = {
adjustDateOnChange: PropTypes.bool,
- arrowProps: PropTypes.object,
chooseDayAriaLabelPrefix: PropTypes.string,
className: PropTypes.string,
children: PropTypes.node,
@@ -1074,13 +1073,11 @@ export default class Calendar extends React.Component {
render() {
const Container = this.props.container || CalendarContainer;
return (
-
+
{this.renderAriaLiveRegion()}
{this.renderPreviousButton()}
diff --git a/src/calendar_container.jsx b/src/calendar_container.jsx
index dcf7924e8..0b863ee55 100644
--- a/src/calendar_container.jsx
+++ b/src/calendar_container.jsx
@@ -1,25 +1,11 @@
import PropTypes from "prop-types";
import React from "react";
-export default function CalendarContainer({
- className,
- children,
- showPopperArrow,
- arrowProps = {},
-}) {
- return (
-
- {showPopperArrow && (
-
- )}
- {children}
-
- );
+export default function CalendarContainer({ className, children }) {
+ return {children}
;
}
CalendarContainer.propTypes = {
className: PropTypes.string,
children: PropTypes.node,
- arrowProps: PropTypes.object, // react-popper arrow props
- showPopperArrow: PropTypes.bool,
};
diff --git a/src/index.jsx b/src/index.jsx
index ed748d64f..79050b74e 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -3,7 +3,8 @@ import PropTypes from "prop-types";
import Calendar from "./calendar";
import CalendarIcon from "./calendar_icon";
import Portal from "./portal";
-import PopperComponent, { popperPlacementPositions } from "./popper_component";
+import PopperComponent from "./popper_component";
+import { popperPlacementPositions } from "./with_floating";
import classnames from "classnames";
import set from "date-fns/set";
import startOfDay from "date-fns/startOfDay";
@@ -1091,7 +1092,6 @@ export default class DatePicker extends React.Component {
showYearPicker={this.props.showYearPicker}
showQuarterYearPicker={this.props.showQuarterYearPicker}
showWeekPicker={this.props.showWeekPicker}
- showPopperArrow={this.props.showPopperArrow}
excludeScrollbar={this.props.excludeScrollbar}
handleOnKeyDown={this.props.onKeyDown}
handleOnDayKeyDown={this.onDayKeyDown}
@@ -1340,6 +1340,7 @@ export default class DatePicker extends React.Component {
popperProps={this.props.popperProps}
popperOnKeyDown={this.onPopperKeyDown}
enableTabLoop={this.props.enableTabLoop}
+ showArrow={this.props.showPopperArrow}
/>
);
}
diff --git a/src/popper_component.jsx b/src/popper_component.jsx
index 4558e2cd6..72a17dbd6 100644
--- a/src/popper_component.jsx
+++ b/src/popper_component.jsx
@@ -1,20 +1,16 @@
import classnames from "classnames";
import React from "react";
import PropTypes from "prop-types";
-import { Manager, Reference, Popper } from "react-popper";
-import { placements } from "@popperjs/core/lib";
+import { FloatingArrow } from "@floating-ui/react";
import TabLoop from "./tab_loop";
import Portal from "./portal";
+import withFloating from "./with_floating";
-export const popperPlacementPositions = placements;
-
-export default class PopperComponent extends React.Component {
+// Exported for testing purposes
+export class PopperComponent extends React.Component {
static get defaultProps() {
return {
hidePopper: true,
- popperModifiers: [],
- popperProps: {},
- popperPlacement: "bottom-start",
};
}
@@ -23,13 +19,12 @@ export default class PopperComponent extends React.Component {
wrapperClassName: PropTypes.string,
hidePopper: PropTypes.bool,
popperComponent: PropTypes.element,
- popperModifiers: PropTypes.arrayOf(PropTypes.object), // props
- popperPlacement: PropTypes.oneOf(popperPlacementPositions), // props
popperContainer: PropTypes.func,
popperProps: PropTypes.object,
targetComponent: PropTypes.element,
enableTabLoop: PropTypes.bool,
popperOnKeyDown: PropTypes.func,
+ showArrow: PropTypes.bool,
portalId: PropTypes.string,
portalHost: PropTypes.instanceOf(ShadowRoot),
};
@@ -40,14 +35,13 @@ export default class PopperComponent extends React.Component {
wrapperClassName,
hidePopper,
popperComponent,
- popperModifiers,
- popperPlacement,
- popperProps,
targetComponent,
enableTabLoop,
popperOnKeyDown,
portalId,
portalHost,
+ popperProps,
+ showArrow,
} = this.props;
let popper;
@@ -55,24 +49,29 @@ export default class PopperComponent extends React.Component {
if (!hidePopper) {
const classes = classnames("react-datepicker-popper", className);
popper = (
-
- {({ ref, style, placement, arrowProps }) => (
-
-
- {React.cloneElement(popperComponent, { arrowProps })}
-
-
- )}
-
+
+
+ {popperComponent}
+ {showArrow && (
+
+ )}
+
+
);
}
@@ -94,16 +93,14 @@ export default class PopperComponent extends React.Component {
);
return (
-
-
- {({ ref }) => (
-
- {targetComponent}
-
- )}
-
+
+
+ {targetComponent}
+
{popper}
-
+
);
}
}
+
+export default withFloating(PopperComponent);
diff --git a/src/stylesheets/datepicker.scss b/src/stylesheets/datepicker.scss
index f76865114..38eb047fc 100644
--- a/src/stylesheets/datepicker.scss
+++ b/src/stylesheets/datepicker.scss
@@ -16,13 +16,12 @@
border-radius: $datepicker__border-radius;
display: inline-block;
position: relative;
+
+ // Reverting value set in .react-datepicker-popper
+ line-height: initial;
}
.react-datepicker--time-only {
- .react-datepicker__triangle {
- left: 35px;
- }
-
.react-datepicker__time-container {
border-left: 0;
}
@@ -34,55 +33,23 @@
}
}
-.react-datepicker__triangle {
- position: absolute;
- left: 50px;
-}
-
.react-datepicker-popper {
z-index: 1;
- &[data-placement^="bottom"] {
- padding-top: $datepicker__triangle-size + 2px;
+ // Eliminating extra space at the bottom of the container
+ line-height: 0;
+ &[data-placement^="bottom"] {
.react-datepicker__triangle {
@extend %triangle-arrow-up;
}
}
- &[data-placement="bottom-end"],
- &[data-placement="top-end"] {
- .react-datepicker__triangle {
- left: auto;
- right: 50px;
- }
- }
-
&[data-placement^="top"] {
- padding-bottom: $datepicker__triangle-size + 2px;
-
.react-datepicker__triangle {
@extend %triangle-arrow-down;
}
}
-
- &[data-placement^="right"] {
- padding-left: $datepicker__triangle-size;
-
- .react-datepicker__triangle {
- left: auto;
- right: 42px;
- }
- }
-
- &[data-placement^="left"] {
- padding-right: $datepicker__triangle-size;
-
- .react-datepicker__triangle {
- left: 42px;
- right: auto;
- }
- }
}
.react-datepicker__header {
diff --git a/src/stylesheets/mixins.scss b/src/stylesheets/mixins.scss
index 1cc671150..9df1fea79 100644
--- a/src/stylesheets/mixins.scss
+++ b/src/stylesheets/mixins.scss
@@ -16,69 +16,14 @@
}
}
-%triangle-arrow {
- margin-left: -$datepicker__triangle-size * 0.5;
- position: absolute;
- width: 0;
-
- &::before,
- &::after {
- box-sizing: content-box;
- position: absolute;
- border: $datepicker__triangle-size solid transparent;
- height: 0;
- width: 1px;
- content: "";
- z-index: -1;
- border-width: $datepicker__triangle-size;
- left: -$datepicker__triangle-size;
- }
-
- &::before {
- border-bottom-color: $datepicker__border-color;
- }
-}
-
%triangle-arrow-up {
- @extend %triangle-arrow;
-
- top: 0;
- margin-top: -$datepicker__triangle-size;
-
- &::before,
- &::after {
- border-top: none;
- border-bottom-color: $datepicker__background-color;
- }
-
- &::after {
- top: 0;
- }
-
- &::before {
- top: -1px;
- border-bottom-color: $datepicker__border-color;
- }
+ fill: $datepicker__background-color;
+ color: $datepicker__background-color;
+ stroke: $datepicker__border-color;
}
%triangle-arrow-down {
- @extend %triangle-arrow;
-
- bottom: 0;
- margin-bottom: -$datepicker__triangle-size;
-
- &::before,
- &::after {
- border-bottom: none;
- border-top-color: #fff;
- }
-
- &::after {
- bottom: 0;
- }
-
- &::before {
- bottom: -1px;
- border-top-color: $datepicker__border-color;
- }
+ fill: #fff;
+ color: #fff;
+ stroke: $datepicker__border-color;
}
diff --git a/src/with_floating.jsx b/src/with_floating.jsx
new file mode 100644
index 000000000..66ac1d87e
--- /dev/null
+++ b/src/with_floating.jsx
@@ -0,0 +1,61 @@
+import React from "react";
+import {
+ useFloating,
+ arrow,
+ offset,
+ flip,
+ autoUpdate,
+} from "@floating-ui/react";
+import PropTypes from "prop-types";
+
+export const popperPlacementPositions = [
+ "top-start",
+ "top-end",
+ "bottom-start",
+ "bottom-end",
+ "right-start",
+ "right-end",
+ "left-start",
+ "left-end",
+ "top",
+ "right",
+ "bottom",
+ "left",
+];
+
+export default function withFloating(Component) {
+ const WithFloating = (props) => {
+ const arrowRef = React.useRef();
+ const floatingProps = useFloating({
+ open: !props.hidePopper,
+ whileElementsMounted: autoUpdate,
+ placement: props.popperPlacement,
+ middleware: [
+ flip({ padding: 15 }),
+ offset(10),
+ arrow({ element: arrowRef }),
+ ...props.popperModifiers,
+ ],
+ ...props.popperProps,
+ });
+
+ return (
+
+ );
+ };
+
+ WithFloating.propTypes = {
+ popperPlacement: PropTypes.oneOf(popperPlacementPositions),
+ popperModifiers: PropTypes.arrayOf(PropTypes.object),
+ popperProps: PropTypes.object,
+ hidePopper: PropTypes.bool,
+ };
+
+ WithFloating.defaultProps = {
+ popperModifiers: [],
+ popperProps: {},
+ hidePopper: true,
+ };
+
+ return WithFloating;
+}
diff --git a/test/datepicker_test.test.js b/test/datepicker_test.test.js
index a2a0fbdc2..d1b2b7d1f 100644
--- a/test/datepicker_test.test.js
+++ b/test/datepicker_test.test.js
@@ -10,7 +10,7 @@ import DatePicker, { registerLocale } from "../src/index.jsx";
import Day from "../src/day.jsx";
import WeekNumber from "../src/week_number.jsx";
import TestWrapper from "./test_wrapper.jsx";
-import PopperComponent from "../src/popper_component.jsx";
+import { PopperComponent } from "../src/popper_component.jsx";
import CustomInput from "./helper_components/custom_input.jsx";
import * as utils from "../src/date_utils.js";
import Month from "../src/month.jsx";
@@ -1694,12 +1694,12 @@ describe("DatePicker", () => {
var node = findDOMNode(dateInput);
TestUtils.Simulate.click(node);
- const arrow = TestUtils.scryRenderedDOMComponentsWithClass(
- datePicker.calendar,
+ const arrow = TestUtils.findRenderedDOMComponentWithClass(
+ datePicker,
"react-datepicker__triangle",
);
- expect(Object.keys(arrow)).not.toHaveLength(0);
+ expect(arrow).not.toBeNull();
});
it("should not show the popper arrow when showPopperArrow is false", () => {
diff --git a/yarn.lock b/yarn.lock
index d3864a2d4..c6302d314 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1250,6 +1250,42 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.55.0.tgz#b721d52060f369aa259cf97392403cb9ce892ec6"
integrity sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==
+"@floating-ui/core@^1.4.2":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.0.tgz#5c05c60d5ae2d05101c3021c1a2a350ddc027f8c"
+ integrity sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==
+ dependencies:
+ "@floating-ui/utils" "^0.1.3"
+
+"@floating-ui/dom@^1.5.1":
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa"
+ integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==
+ dependencies:
+ "@floating-ui/core" "^1.4.2"
+ "@floating-ui/utils" "^0.1.3"
+
+"@floating-ui/react-dom@^2.0.3":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.4.tgz#b076fafbdfeb881e1d86ae748b7ff95150e9f3ec"
+ integrity sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==
+ dependencies:
+ "@floating-ui/dom" "^1.5.1"
+
+"@floating-ui/react@^0.26.2":
+ version "0.26.2"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.2.tgz#1a548f0f9aae64a742c868ae36aa620d15dec728"
+ integrity sha512-ocpz3MxYoZlgsASiVFayiTnKukR8QZDQUMqxMdF0YFLbu8lw/IL6AHKLROI8SOpp6CxpUGPh9Q4a03eBAVEZNQ==
+ dependencies:
+ "@floating-ui/react-dom" "^2.0.3"
+ "@floating-ui/utils" "^0.1.5"
+ tabbable "^6.0.1"
+
+"@floating-ui/utils@^0.1.3", "@floating-ui/utils@^0.1.5":
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9"
+ integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==
+
"@humanwhocodes/config-array@^0.11.13":
version "0.11.13"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297"
@@ -1642,11 +1678,6 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
-"@popperjs/core@^2.11.8":
- version "2.11.8"
- resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
- integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
-
"@sigstore/protobuf-specs@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz#957cb64ea2f5ce527cc9cf02a096baeb0d2b99b4"
@@ -7495,11 +7526,6 @@ react-dom@^18.2.0:
loose-envify "^1.1.0"
scheduler "^0.23.0"
-react-fast-compare@^3.0.1:
- version "3.2.2"
- resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49"
- integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==
-
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
@@ -7520,14 +7546,6 @@ react-onclickoutside@^6.13.0:
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz#e165ea4e5157f3da94f4376a3ab3e22a565f4ffc"
integrity sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==
-react-popper@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba"
- integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==
- dependencies:
- react-fast-compare "^3.0.1"
- warning "^4.0.2"
-
react-proxy@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a"
@@ -8769,6 +8787,11 @@ symbol-tree@^3.2.4:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+tabbable@^6.0.1:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97"
+ integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
+
table@^6.8.1:
version "6.8.1"
resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf"
@@ -9174,13 +9197,6 @@ walker@^1.0.8:
dependencies:
makeerror "1.0.12"
-warning@^4.0.2:
- 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.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"