From c63456612eeff8538772dce8a83dc0d56bdfb1ea Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Fri, 23 Feb 2024 10:45:04 +0000 Subject: [PATCH] Chore: replace `react-popper` with `floating-ui` in time pickers (#82640) * replace react-popper with floating-ui in RelativeTimeRangePicker * replace react-popper with floating-ui in DateTimePicker --- .../DateTimePicker/DateTimePicker.tsx | 27 ++++++++--- .../RelativeTimeRangePicker.tsx | 46 +++++++++++++------ 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.tsx b/packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.tsx index e0e7f45a113..4dfb5180d4f 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.tsx @@ -1,10 +1,10 @@ import { css, cx } from '@emotion/css'; +import { autoUpdate, flip, shift, useFloating } from '@floating-ui/react'; import { useDialog } from '@react-aria/dialog'; import { FocusScope } from '@react-aria/focus'; import { useOverlay } from '@react-aria/overlays'; import React, { FormEvent, ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import Calendar from 'react-calendar'; -import { usePopper } from 'react-popper'; import { useMedia } from 'react-use'; import { dateTimeFormat, DateTime, dateTime, GrafanaTheme2, isDateTime } from '@grafana/data'; @@ -76,11 +76,24 @@ export const DateTimePicker = ({ const isFullscreen = useMedia(`(min-width: ${theme.breakpoints.values.lg}px)`); const styles = useStyles2(getStyles); - const [markerElement, setMarkerElement] = useState(); - const [selectorElement, setSelectorElement] = useState(); + // the order of middleware is important! + // see https://floating-ui.com/docs/arrow#order + const middleware = [ + flip({ + // see https://floating-ui.com/docs/flip#combining-with-shift + crossAxis: false, + boundary: document.body, + }), + shift(), + ]; - const popper = usePopper(markerElement, selectorElement, { + const { refs, floatingStyles } = useFloating({ + open: isOpen, placement: 'bottom-start', + onOpenChange: setOpen, + middleware, + whileElementsMounted: autoUpdate, + strategy: 'fixed', }); const onApply = useCallback( @@ -107,7 +120,7 @@ export const DateTimePicker = ({ isFullscreen={isFullscreen} onOpen={onOpen} label={label} - ref={setMarkerElement} + ref={refs.setReference} showSeconds={showSeconds} /> {isOpen ? ( @@ -122,8 +135,8 @@ export const DateTimePicker = ({ onClose={() => setOpen(false)} maxDate={maxDate} minDate={minDate} - ref={setSelectorElement} - style={popper.styles.popper} + ref={refs.setFloating} + style={floatingStyles} showSeconds={showSeconds} disabledHours={disabledHours} disabledMinutes={disabledMinutes} diff --git a/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx b/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx index ec797abbb77..b7f5b76bda3 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx @@ -1,9 +1,9 @@ import { css, cx } from '@emotion/css'; +import { autoUpdate, flip, shift, useClick, useDismiss, useFloating, useInteractions } from '@floating-ui/react'; import { useDialog } from '@react-aria/dialog'; import { FocusScope } from '@react-aria/focus'; import { useOverlay } from '@react-aria/overlays'; import React, { FormEvent, useCallback, useRef, useState } from 'react'; -import { usePopper } from 'react-popper'; import { RelativeTimeRange, GrafanaTheme2, TimeOption } from '@grafana/data'; @@ -59,12 +59,31 @@ export function RelativeTimeRangePicker(props: RelativeTimeRangePickerProps) { ); const { dialogProps } = useDialog({}, ref); - const [markerElement, setMarkerElement] = useState(null); - const [selectorElement, setSelectorElement] = useState(null); - const popper = usePopper(markerElement, selectorElement, { - placement: 'auto-start', + // the order of middleware is important! + // see https://floating-ui.com/docs/arrow#order + const middleware = [ + flip({ + // see https://floating-ui.com/docs/flip#combining-with-shift + crossAxis: false, + boundary: document.body, + }), + shift(), + ]; + + const { context, refs, floatingStyles } = useFloating({ + open: isOpen, + placement: 'bottom-start', + onOpenChange: setIsOpen, + middleware, + whileElementsMounted: autoUpdate, + strategy: 'fixed', }); + const click = useClick(context); + const dismiss = useDismiss(context); + + const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]); + const styles = useStyles2(getStyles(from.validation.errorMessage, to.validation.errorMessage)); const onChangeTimeOption = (option: TimeOption) => { @@ -109,8 +128,14 @@ export function RelativeTimeRangePicker(props: RelativeTimeRangePickerProps) { }; return ( -
-