Chore: remove react-popper from AnnotationEditor and AnnotationMarker (#82090)

update AnnotationEditor and AnnotationMarker to use floating-ui/react instead of react-popper
This commit is contained in:
Ashley Harrison
2024-02-12 17:18:10 +00:00
committed by GitHub
parent 29be9b127a
commit fea23862b4
5 changed files with 120 additions and 129 deletions

View File

@@ -6270,12 +6270,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "6"]
],
"public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditor.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Styles should be written using objects.", "2"],
[0, 0, 0, "Styles should be written using objects.", "3"],
[0, 0, 0, "Styles should be written using objects.", "4"],
[0, 0, 0, "Styles should be written using objects.", "5"]
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditorForm.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
@@ -6286,11 +6281,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "5"],
[0, 0, 0, "Styles should be written using objects.", "6"]
],
"public/app/plugins/panel/timeseries/plugins/annotations/AnnotationMarker.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
[0, 0, 0, "Styles should be written using objects.", "2"]
],
"public/app/plugins/panel/timeseries/plugins/annotations/AnnotationTooltip.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],

View File

@@ -1,8 +1,9 @@
/**
* This API allows popovers to update Popper's position when e.g. popover content changes
* update is delivered to content by react-popper.
*/
export interface ToggletipContentProps {
/**
* @deprecated
* This prop is deprecated and no longer has any effect as popper position updates automatically.
* It will be removed in a future release.
*/
update?: () => void;
}

View File

@@ -1,10 +1,11 @@
import { Placement } from '@floating-ui/react';
/**
* This API allows popovers to update Popper's position when e.g. popover content changes
* updatePopperPosition is delivered to content by react-popper.
*/
export interface PopoverContentProps {
// Is this used anywhere in plugins? Can we remove it or rename it to just update?
/**
* @deprecated
* This prop is deprecated and no longer has any effect as popper position updates automatically.
* It will be removed in a future release.
*/
updatePopperPosition?: () => void;
}

View File

@@ -1,6 +1,6 @@
import { css, cx } from '@emotion/css';
import React, { HTMLAttributes, useState } from 'react';
import { usePopper } from 'react-popper';
import { autoUpdate, flip, shift, useDismiss, useFloating, useInteractions } from '@floating-ui/react';
import React, { HTMLAttributes } from 'react';
import { colorManipulator, DataFrame, getDisplayProcessor, GrafanaTheme2, TimeZone } from '@grafana/data';
import { PlotSelection, useStyles2, useTheme2, Portal, DEFAULT_ANNOTATION_COLOR } from '@grafana/ui';
@@ -31,22 +31,35 @@ export const AnnotationEditor = ({
const theme = useTheme2();
const styles = useStyles2(getStyles);
const commonStyles = useStyles2(getCommonAnnotationStyles);
const [popperTrigger, setPopperTrigger] = useState<HTMLDivElement | null>(null);
const [editorPopover, setEditorPopover] = useState<HTMLDivElement | null>(null);
const popper = usePopper(popperTrigger, editorPopover, {
modifiers: [
{ name: 'arrow', enabled: false },
{
name: 'preventOverflow',
enabled: true,
options: {
rootBoundary: 'viewport',
},
},
],
// the order of middleware is important!
const middleware = [
flip({
fallbackAxisSideDirection: 'end',
// see https://floating-ui.com/docs/flip#combining-with-shift
crossAxis: false,
boundary: document.body,
}),
shift(),
];
const { context, refs, floatingStyles } = useFloating({
open: true,
placement: 'bottom',
onOpenChange: (open) => {
if (!open) {
onDismiss();
}
},
middleware,
whileElementsMounted: autoUpdate,
strategy: 'fixed',
});
const dismiss = useDismiss(context);
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
let xField = data.fields[0];
if (!xField) {
return null;
@@ -62,23 +75,24 @@ export const AnnotationEditor = ({
>
<div // Annotation marker
className={cx(
css`
position: absolute;
top: ${selection.bbox.top}px;
left: ${selection.bbox.left}px;
width: ${selection.bbox.width}px;
height: ${selection.bbox.height}px;
`,
css({
position: 'absolute',
top: selection.bbox.top,
left: selection.bbox.left,
width: selection.bbox.width,
height: selection.bbox.height,
}),
isRegionAnnotation ? styles.overlayRange(annotation) : styles.overlay(annotation)
)}
>
<div
ref={setPopperTrigger}
ref={refs.setReference}
className={
isRegionAnnotation
? cx(commonStyles(annotation).markerBar, styles.markerBar)
: cx(commonStyles(annotation).markerTriangle, styles.markerTriangle)
}
{...getReferenceProps()}
/>
</div>
</div>
@@ -88,9 +102,9 @@ export const AnnotationEditor = ({
timeFormatter={(v) => xFieldFmt(v).text}
onSave={onSave}
onDismiss={onDismiss}
ref={setEditorPopover}
style={popper.styles.popper}
{...popper.attributes.popper}
ref={refs.setFloating}
style={floatingStyles}
{...getFloatingProps()}
/>
</>
</Portal>
@@ -101,27 +115,27 @@ const getStyles = (theme: GrafanaTheme2) => {
return {
overlay: (annotation?: AnnotationsDataFrameViewDTO) => {
const color = theme.visualization.getColorByName(annotation?.color || DEFAULT_ANNOTATION_COLOR);
return css`
border-left: 1px dashed ${color};
`;
return css({
borderLeft: `1px dashed ${color}`,
});
},
overlayRange: (annotation?: AnnotationsDataFrameViewDTO) => {
const color = theme.visualization.getColorByName(annotation?.color || DEFAULT_ANNOTATION_COLOR);
return css`
background: ${colorManipulator.alpha(color, 0.1)};
border-left: 1px dashed ${color};
border-right: 1px dashed ${color};
`;
return css({
background: colorManipulator.alpha(color, 0.1),
borderLeft: `1px dashed ${color}`,
borderRight: `1px dashed ${color}`,
});
},
markerTriangle: css`
top: calc(100% + 2px);
left: -4px;
position: absolute;
`,
markerBar: css`
top: 100%;
left: 0;
position: absolute;
`,
markerTriangle: css({
top: `calc(100% + 2px)`,
left: '-4px',
position: 'absolute',
}),
markerBar: css({
top: '100%',
left: 0,
position: 'absolute',
}),
};
};

View File

@@ -1,6 +1,15 @@
import { css } from '@emotion/css';
import React, { HTMLAttributes, useCallback, useRef, useState } from 'react';
import { usePopper } from 'react-popper';
import {
autoUpdate,
flip,
safePolygon,
shift,
useDismiss,
useFloating,
useHover,
useInteractions,
} from '@floating-ui/react';
import React, { HTMLAttributes, useCallback, useState } from 'react';
import { GrafanaTheme2, dateTimeFormat, systemDateFormats, TimeZone } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
@@ -21,19 +30,6 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
const MIN_REGION_ANNOTATION_WIDTH = 6;
const POPPER_CONFIG = {
modifiers: [
{ name: 'arrow', enabled: false },
{
name: 'preventOverflow',
enabled: true,
options: {
rootBoundary: 'viewport',
},
},
],
};
export function AnnotationMarker({ annotation, timeZone, width }: Props) {
const { canEditAnnotations, canDeleteAnnotations, ...panelCtx } = usePanelContext();
const commonStyles = useStyles2(getCommonAnnotationStyles);
@@ -41,14 +37,33 @@ export function AnnotationMarker({ annotation, timeZone, width }: Props) {
const [isOpen, setIsOpen] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [markerRef, setMarkerRef] = useState<HTMLDivElement | null>(null);
const [tooltipRef, setTooltipRef] = useState<HTMLDivElement | null>(null);
const [editorRef, setEditorRef] = useState<HTMLDivElement | null>(null);
const popoverRenderTimeout = useRef<NodeJS.Timeout>();
// the order of middleware is important!
const middleware = [
flip({
fallbackAxisSideDirection: 'end',
// see https://floating-ui.com/docs/flip#combining-with-shift
crossAxis: false,
boundary: document.body,
}),
shift(),
];
const popper = usePopper(markerRef, tooltipRef, POPPER_CONFIG);
const editorPopper = usePopper(markerRef, editorRef, POPPER_CONFIG);
const { context, refs, floatingStyles } = useFloating({
open: isOpen,
placement: 'bottom',
onOpenChange: setIsOpen,
middleware,
whileElementsMounted: autoUpdate,
strategy: 'fixed',
});
const hover = useHover(context, {
handleClose: safePolygon(),
});
const dismiss = useDismiss(context);
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, hover]);
const onAnnotationEdit = useCallback(() => {
setIsEditing(true);
@@ -61,25 +76,6 @@ export function AnnotationMarker({ annotation, timeZone, width }: Props) {
}
}, [annotation, panelCtx]);
const onMouseEnter = useCallback(() => {
if (popoverRenderTimeout.current) {
clearTimeout(popoverRenderTimeout.current);
}
setIsOpen(true);
}, [setIsOpen]);
const onPopoverMouseEnter = useCallback(() => {
if (popoverRenderTimeout.current) {
clearTimeout(popoverRenderTimeout.current);
}
}, []);
const onMouseLeave = useCallback(() => {
popoverRenderTimeout.current = setTimeout(() => {
setIsOpen(false);
}, 100);
}, [setIsOpen]);
const timeFormatter = useCallback(
(value: number) => {
return dateTimeFormat(value, {
@@ -124,25 +120,17 @@ export function AnnotationMarker({ annotation, timeZone, width }: Props) {
return (
<>
<div
ref={setMarkerRef}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
ref={refs.setReference}
className={!isRegionAnnotation ? styles.markerWrapper : undefined}
data-testid={selectors.pages.Dashboard.Annotations.marker}
{...getReferenceProps()}
>
{marker}
</div>
{isOpen && (
<Portal>
<div
ref={setTooltipRef}
style={popper.styles.popper}
{...popper.attributes.popper}
className={styles.tooltip}
onMouseEnter={onPopoverMouseEnter}
onMouseLeave={onMouseLeave}
>
<div className={styles.tooltip} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
{renderTooltip()}
</div>
</Portal>
@@ -155,9 +143,9 @@ export function AnnotationMarker({ annotation, timeZone, width }: Props) {
onSave={() => setIsEditing(false)}
timeFormatter={timeFormatter}
annotation={annotation}
ref={setEditorRef}
style={editorPopper.styles.popper}
{...editorPopper.attributes.popper}
ref={refs.setFloating}
style={floatingStyles}
{...getFloatingProps()}
/>
</Portal>
)}
@@ -167,16 +155,13 @@ export function AnnotationMarker({ annotation, timeZone, width }: Props) {
const getStyles = (theme: GrafanaTheme2) => {
return {
markerWrapper: css`
label: markerWrapper;
padding: 0 4px 4px 4px;
`,
wrapper: css`
max-width: 400px;
`,
tooltip: css`
${getTooltipContainerStyles(theme)};
padding: 0;
`,
markerWrapper: css({
label: 'markerWrapper',
padding: theme.spacing(0, 0.5, 0.5, 0.5),
}),
tooltip: css({
...getTooltipContainerStyles(theme),
padding: 0,
}),
};
};