diff --git a/package.json b/package.json index 8c6e29fc051..d6696636581 100644 --- a/package.json +++ b/package.json @@ -337,6 +337,7 @@ "react-loadable": "5.5.0", "react-moveable": "0.30.3", "react-popper": "2.2.5", + "react-popper-tooltip": "^4.3.1", "react-redux": "7.2.6", "react-resizable": "3.0.4", "react-reverse-portal": "^2.0.1", diff --git a/packages/grafana-data/src/themes/createComponents.ts b/packages/grafana-data/src/themes/createComponents.ts index 914837195e2..4896c9b3609 100644 --- a/packages/grafana-data/src/themes/createComponents.ts +++ b/packages/grafana-data/src/themes/createComponents.ts @@ -69,7 +69,7 @@ export function createComponents(colors: ThemeColors, shadows: ThemeShadows): Th background: input.background, }, tooltip: { - background: colors.mode === 'light' ? '#555' : colors.background.secondary, + background: colors.mode === 'light' ? '#555' : '#35383e', text: colors.mode === 'light' ? '#FFF' : colors.text.primary, }, dashboard: { diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index cb88b47db82..c4cb25d69da 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -77,6 +77,7 @@ "react-hook-form": "7.5.3", "react-inlinesvg": "2.3.0", "react-popper": "2.2.5", + "react-popper-tooltip": "^4.3.1", "react-router-dom": "^5.2.0", "react-select": "5.2.2", "react-select-event": "^5.1.0", diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 84767c4fb92..4bfa15710c2 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { NamedColorsPalette } from './NamedColorsPalette'; -import { PopoverContentProps } from '../Tooltip/Tooltip'; +import { PopoverContentProps } from '../Tooltip'; import SpectrumPalette from './SpectrumPalette'; import { Themeable2 } from '../../types/theme'; import { warnAboutColorPickerPropsDeprecation } from './warnAboutColorPickerPropsDeprecation'; diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 37c806c6550..0cf754b2db3 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from 'react'; import { ColorPickerPopover, ColorPickerProps } from './ColorPickerPopover'; -import { PopoverContentProps } from '../Tooltip/Tooltip'; +import { PopoverContentProps } from '../Tooltip'; import { Switch } from '../Forms/Legacy/Switch/Switch'; import { css } from '@emotion/css'; import { withTheme2, useStyles } from '../../themes'; diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx index cfe06fa4fe2..167cc9d4e6a 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx @@ -105,7 +105,7 @@ export function UnthemedTimeRangePicker(props: TimeRangePickerProps): ReactEleme /> )} - } placement="bottom"> + } placement="bottom" interactive> { label: string; diff --git a/packages/grafana-ui/src/components/FormLabel/FormLabel.tsx b/packages/grafana-ui/src/components/FormLabel/FormLabel.tsx index d7cbf80428a..b80178030ba 100644 --- a/packages/grafana-ui/src/components/FormLabel/FormLabel.tsx +++ b/packages/grafana-ui/src/components/FormLabel/FormLabel.tsx @@ -1,6 +1,6 @@ import React, { FunctionComponent, ReactNode } from 'react'; import classNames from 'classnames'; -import { Tooltip, PopoverContent } from '../Tooltip/Tooltip'; +import { Tooltip, PopoverContent } from '../Tooltip'; import { Icon } from '../Icon/Icon'; interface Props { diff --git a/packages/grafana-ui/src/components/Forms/InlineField.tsx b/packages/grafana-ui/src/components/Forms/InlineField.tsx index 60ff61e36a5..ae0f9b9bfb1 100644 --- a/packages/grafana-ui/src/components/Forms/InlineField.tsx +++ b/packages/grafana-ui/src/components/Forms/InlineField.tsx @@ -3,7 +3,7 @@ import { cx, css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { useTheme2 } from '../../themes'; import { InlineLabel } from './InlineLabel'; -import { PopoverContent } from '../Tooltip/Tooltip'; +import { PopoverContent } from '../Tooltip'; import { FieldProps } from './Field'; import { getChildId } from '../../utils/reactUtils'; import { FieldValidationMessage } from './FieldValidationMessage'; diff --git a/packages/grafana-ui/src/components/Forms/InlineLabel.tsx b/packages/grafana-ui/src/components/Forms/InlineLabel.tsx index 9c146e6aef2..c9c6cc8a98c 100644 --- a/packages/grafana-ui/src/components/Forms/InlineLabel.tsx +++ b/packages/grafana-ui/src/components/Forms/InlineLabel.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from 'react'; import { GrafanaTheme } from '@grafana/data'; import { css, cx } from '@emotion/css'; -import { Tooltip, PopoverContent } from '../Tooltip/Tooltip'; +import { Tooltip, PopoverContent } from '../Tooltip'; import { Icon } from '../Icon/Icon'; import { useTheme } from '../../themes'; import { LabelProps } from './Label'; diff --git a/packages/grafana-ui/src/components/Forms/Legacy/Select/Select.tsx b/packages/grafana-ui/src/components/Forms/Legacy/Select/Select.tsx index b8df2c4d405..a45de860d6c 100644 --- a/packages/grafana-ui/src/components/Forms/Legacy/Select/Select.tsx +++ b/packages/grafana-ui/src/components/Forms/Legacy/Select/Select.tsx @@ -14,7 +14,7 @@ import IndicatorsContainer from './IndicatorsContainer'; import NoOptionsMessage from './NoOptionsMessage'; import resetSelectStyles from '../../../Select/resetSelectStyles'; import { CustomScrollbar } from '../../../CustomScrollbar/CustomScrollbar'; -import { PopoverContent, Tooltip } from '../../../Tooltip/Tooltip'; +import { Tooltip, PopoverContent } from '../../../Tooltip'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { ThemeContext } from '../../../../themes'; diff --git a/packages/grafana-ui/src/components/IconButton/IconButton.tsx b/packages/grafana-ui/src/components/IconButton/IconButton.tsx index c52de18b45b..e24d53d8274 100644 --- a/packages/grafana-ui/src/components/IconButton/IconButton.tsx +++ b/packages/grafana-ui/src/components/IconButton/IconButton.tsx @@ -5,8 +5,7 @@ import { stylesFactory } from '../../themes/stylesFactory'; import { css, cx } from '@emotion/css'; import { useTheme2 } from '../../themes/ThemeContext'; import { GrafanaTheme2, colorManipulator } from '@grafana/data'; -import { PopoverContent, Tooltip } from '../Tooltip/Tooltip'; -import { TooltipPlacement } from '../Tooltip/PopoverController'; +import { TooltipPlacement, PopoverContent, Tooltip } from '../Tooltip'; import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins'; export type IconButtonVariant = 'primary' | 'secondary' | 'destructive'; diff --git a/packages/grafana-ui/src/components/InfoTooltip/InfoTooltip.tsx b/packages/grafana-ui/src/components/InfoTooltip/InfoTooltip.tsx index 75536701d35..b0fd4d8202f 100644 --- a/packages/grafana-ui/src/components/InfoTooltip/InfoTooltip.tsx +++ b/packages/grafana-ui/src/components/InfoTooltip/InfoTooltip.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { PopoverContent, TooltipProps } from '../Tooltip/Tooltip'; +import { TooltipProps, PopoverContent } from '../Tooltip'; import { IconButton } from '../IconButton/IconButton'; interface InfoTooltipProps extends Omit { diff --git a/packages/grafana-ui/src/components/SecretFormField/SecretFormField.tsx b/packages/grafana-ui/src/components/SecretFormField/SecretFormField.tsx index 17b2cdc13da..d71dc7a68e4 100644 --- a/packages/grafana-ui/src/components/SecretFormField/SecretFormField.tsx +++ b/packages/grafana-ui/src/components/SecretFormField/SecretFormField.tsx @@ -3,7 +3,7 @@ import React, { InputHTMLAttributes, FunctionComponent } from 'react'; import { FormField } from '../FormField/FormField'; import { Button } from '../Button/Button'; import { css, cx } from '@emotion/css'; -import { PopoverContent } from '../Tooltip/Tooltip'; +import { PopoverContent } from '../Tooltip'; export interface Props extends Omit, 'onReset'> { // Function to use when reset is clicked. Means you have to reset the input value yourself as this is uncontrolled diff --git a/packages/grafana-ui/src/components/Table/JSONViewCell.tsx b/packages/grafana-ui/src/components/Table/JSONViewCell.tsx index 3f118d24dc7..e2cfe1d87f1 100644 --- a/packages/grafana-ui/src/components/Table/JSONViewCell.tsx +++ b/packages/grafana-ui/src/components/Table/JSONViewCell.tsx @@ -29,7 +29,7 @@ export function JSONViewCell(props: TableCellProps): JSX.Element { const content = ; return ( - + {displayValue} diff --git a/packages/grafana-ui/src/components/Tooltip/Popover.tsx b/packages/grafana-ui/src/components/Tooltip/Popover.tsx index 9ca6f7e5354..b9197a90baa 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popover.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popover.tsx @@ -3,7 +3,7 @@ import { Placement, VirtualElement } from '@popperjs/core'; import { Manager, Popper as ReactPopper, PopperArrowProps } from 'react-popper'; import { Portal } from '../Portal/Portal'; import Transition from 'react-transition-group/Transition'; -import { PopoverContent } from './Tooltip'; +import { PopoverContent } from './types'; const defaultTransitionStyles = { transitionProperty: 'opacity', diff --git a/packages/grafana-ui/src/components/Tooltip/PopoverController.tsx b/packages/grafana-ui/src/components/Tooltip/PopoverController.tsx index 42ad4e43776..c68cac0271d 100644 --- a/packages/grafana-ui/src/components/Tooltip/PopoverController.tsx +++ b/packages/grafana-ui/src/components/Tooltip/PopoverController.tsx @@ -1,33 +1,6 @@ import React from 'react'; import { Placement } from '@popperjs/core'; -import { PopoverContent } from './Tooltip'; - -// 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 UsingPopperProps { - show?: boolean; - placement?: TooltipPlacement; - content: PopoverContent; - children: JSX.Element; -} - -export type TooltipPlacement = - | 'auto-start' - | 'auto' - | 'auto-end' - | 'top-start' - | 'top' - | 'top-end' - | 'right-start' - | 'right' - | 'right-end' - | 'bottom-end' - | 'bottom' - | 'bottom-start' - | 'left-end' - | 'left' - | 'left-start'; +import { PopoverContent } from './types'; type PopperControllerRenderProp = ( showPopper: () => void, diff --git a/packages/grafana-ui/src/components/Tooltip/Tooltip.story.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.story.tsx index 921e791bd0e..a3ab4d1f1f2 100644 --- a/packages/grafana-ui/src/components/Tooltip/Tooltip.story.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.story.tsx @@ -61,9 +61,10 @@ export const Basic: Story = ({ content, ...args }) => { ); }; + Basic.args = { content: 'This is a tooltip', theme: 'info', - show: true, + show: undefined, placement: 'auto', }; diff --git a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx index 80de3ea5793..053539acbe1 100644 --- a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx @@ -1,61 +1,204 @@ -import React, { createRef, FC } from 'react'; -import { VirtualElement } from '@popperjs/core'; -import { Popover } from './Popover'; -import { PopoverController, UsingPopperProps } from './PopoverController'; -import { closePopover } from '../../utils/closePopover'; +import React from 'react'; +import { usePopperTooltip } from 'react-popper-tooltip'; +import { colorManipulator, GrafanaTheme2 } from '@grafana/data'; +import { css } from '@emotion/css'; +import { useStyles2 } from '../../themes/ThemeContext'; +import { PopoverContent, TooltipPlacement } from './types'; +import { Portal } from '../Portal/Portal'; -export interface TooltipProps extends UsingPopperProps { +export interface TooltipProps { theme?: 'info' | 'error' | 'info-alt'; + show?: boolean; + placement?: TooltipPlacement; + content: PopoverContent; + children: JSX.Element; + /** + * Set to true if you want the tooltip to stay long enough so the user can move mouse over content to select text or click a link + */ + interactive?: boolean; } -export interface PopoverContentProps { - updatePopperPosition?: () => void; -} +export const Tooltip = React.memo(({ children, theme, interactive, show, placement, content }: TooltipProps) => { + const { getArrowProps, getTooltipProps, setTooltipRef, setTriggerRef, visible, update } = usePopperTooltip({ + visible: show, + placement: placement, + interactive: interactive, + delayHide: interactive ? 100 : 0, + delayShow: 150, + offset: [0, 8], + trigger: ['hover', 'focus'], + }); -export type PopoverContent = string | React.ReactElement | ((props: PopoverContentProps) => JSX.Element); - -export const Tooltip: FC = React.memo(({ children, theme, ...controllerProps }: TooltipProps) => { - const tooltipTriggerRef = createRef(); - const popperBackgroundClassName = 'popper__background' + (theme ? ' popper__background--' + theme : ''); + const styles = useStyles2(getStyles); + const containerStyle = styles[theme ?? 'info']; return ( - - {(showPopper, hidePopper, popperProps) => { - { - /* Override internal 'show' state if passed in as prop */ - } - const payloadProps = { - ...popperProps, - show: controllerProps.show !== undefined ? controllerProps.show : popperProps.show, - }; - return ( - <> - {tooltipTriggerRef.current && controllerProps.content && ( - ( - - )} - /> - )} - {React.cloneElement(children, { - ref: tooltipTriggerRef, - onMouseEnter: showPopper, - onMouseLeave: hidePopper, - onKeyDown: (event: React.KeyboardEvent) => closePopover(event, hidePopper), - onFocus: showPopper, - onBlur: hidePopper, - })} - > - ); - }} - + <> + {React.cloneElement(children, { + ref: setTriggerRef, + })} + {visible && ( + + + + {typeof content === 'string' && content} + {React.isValidElement(content) && React.cloneElement(content)} + {typeof content === 'function' && + content({ + updatePopperPosition: update as any, + })} + + + )} + > ); }); Tooltip.displayName = 'Tooltip'; + +function getStyles(theme: GrafanaTheme2) { + function buildTooltipTheme(tooltipBg: string, tooltipBorder: string, tooltipText: string) { + return css` + background-color: ${tooltipBg}; + border-radius: 3px; + border: 1px solid ${tooltipBorder}; + box-shadow: ${theme.shadows.z2}; + color: ${tooltipText}; + font-size: ${theme.typography.bodySmall.fontSize}; + padding: ${theme.spacing(0.5, 1)}; + transition: opacity 0.3s; + z-index: ${theme.zIndex.tooltip}; + max-width: 400px; + + &[data-popper-interactive='false'] { + pointer-events: none; + } + + .tooltip-arrow { + height: 1rem; + position: absolute; + width: 1rem; + pointer-events: none; + } + + .tooltip-arrow::before { + border-style: solid; + content: ''; + display: block; + height: 0; + margin: auto; + width: 0; + } + + .tooltip-arrow::after { + border-style: solid; + content: ''; + display: block; + height: 0; + margin: auto; + position: absolute; + width: 0; + } + + &[data-popper-placement*='bottom'] .tooltip-arrow { + left: 0; + margin-top: -10px; + top: 0; + } + + &[data-popper-placement*='bottom'] .tooltip-arrow::before { + border-color: transparent transparent ${tooltipBorder} transparent; + border-width: 0 8px 7px 8px; + position: absolute; + top: -1px; + } + + &[data-popper-placement*='bottom'] .tooltip-arrow::after { + border-color: transparent transparent ${tooltipBg} transparent; + border-width: 0 8px 7px 8px; + } + + &[data-popper-placement*='top'] .tooltip-arrow { + bottom: 0; + left: 0; + margin-bottom: -11px; + } + + &[data-popper-placement*='top'] .tooltip-arrow::before { + border-color: ${tooltipBorder} transparent transparent transparent; + border-width: 7px 8px 0 7px; + position: absolute; + top: 1px; + } + + &[data-popper-placement*='top'] .tooltip-arrow::after { + border-color: ${tooltipBg} transparent transparent transparent; + border-width: 7px 8px 0 7px; + } + + &[data-popper-placement*='right'] .tooltip-arrow { + left: 0; + margin-left: -11px; + } + + &[data-popper-placement*='right'] .tooltip-arrow::before { + border-color: transparent ${tooltipBorder} transparent transparent; + border-width: 7px 6px 7px 0; + } + + &[data-popper-placement*='right'] .tooltip-arrow::after { + border-color: transparent ${tooltipBg} transparent transparent; + border-width: 6px 7px 7px 0; + left: 2px; + top: 1px; + } + + &[data-popper-placement*='left'] .tooltip-arrow { + margin-right: -10px; + right: 0; + } + + &[data-popper-placement*='left'] .tooltip-arrow::before { + border-color: transparent transparent transparent ${tooltipBorder}; + border-width: 7px 0px 6px 7px; + } + + &[data-popper-placement*='left'] .tooltip-arrow::after { + border-color: transparent transparent transparent ${tooltipBg}; + border-width: 6px 0 5px 5px; + left: 1px; + top: 1px; + } + + code { + border: none; + display: inline; + background: ${colorManipulator.darken(tooltipBg, 0.3)}; + color: ${tooltipText}; + } + + strong, + em { + color: ${colorManipulator.emphasize(tooltipBg)}; + } + + a { + color: ${theme.colors.text.link}; + text-decoration: underline; + } + `; + } + + const info = buildTooltipTheme( + theme.components.tooltip.background, + theme.components.tooltip.background, + theme.components.tooltip.text + ); + const error = buildTooltipTheme(theme.colors.error.main, theme.colors.error.main, theme.colors.error.contrastText); + + return { + info: info, + ['info-alt']: info, + error, + }; +} diff --git a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss deleted file mode 100644 index 0288c42ee98..00000000000 --- a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss +++ /dev/null @@ -1,156 +0,0 @@ -$popper-margin-from-ref: 5px; - -@mixin popper-theme($backgroundColor, $textColor) { - background: $backgroundColor; - color: $textColor; - - .popper__arrow { - border-color: $backgroundColor; - } - - code { - border: none; - background: darken($backgroundColor, 15%); - color: lighten($textColor, 20%); - } - - strong, - em { - color: lighten($textColor, 20%); - } - a { - color: $tooltipLinkColor; - text-decoration: underline; - } - a.external-link { - color: $tooltipExternalLinkColor; - } -} - -.popper { - position: absolute; - z-index: $zindex-tooltip; - color: $tooltipColor; - max-width: 400px; -} - -.popper__background { - background: $tooltipBackground; - border-radius: $border-radius-sm; - box-shadow: $tooltipShadow; - padding: $space-xs $space-sm; - font-size: $font-size-sm; - color: $tooltipColor; - font-weight: $font-weight-semi-bold; - - .popper__arrow { - border-color: $tooltipBackground; - } - - // Themes - &.popper__background--error { - @include popper-theme($tooltipBackgroundError, $white); - } - - &.popper__background--info { - @include popper-theme($popover-help-bg, $popover-help-color); - } - - &.popper__background--info-alt { - @include popper-theme($popover-code-bg, $text-color); - } -} - -.popper__arrow { - width: 0; - height: 0; - border-style: solid; - position: absolute; - margin: 0px; -} - -// Top -.popper[data-placement^='top'] { - padding-bottom: $popper-margin-from-ref; -} -.popper[data-placement^='top'] .popper__arrow { - border-width: 5px 5px 0 5px; - border-left-color: transparent; - border-right-color: transparent; - border-bottom-color: transparent; - bottom: -5px; - left: calc(50% - 5px); - padding-top: 5px; -} - -// Bottom -.popper[data-placement^='bottom'] { - padding-top: $popper-margin-from-ref; -} -.popper[data-placement^='bottom'] .popper__arrow { - border-width: 0 5px 5px 5px; - border-left-color: transparent; - border-right-color: transparent; - border-top-color: transparent; - top: 0; - left: calc(50% - 5px); -} - -.popper[data-placement^='bottom-start'] { - padding-top: $popper-margin-from-ref; -} -.popper[data-placement^='bottom-start'] .popper__arrow { - border-width: 0 5px 5px 5px; - border-left-color: transparent; - border-right-color: transparent; - border-top-color: transparent; - top: 0; - left: 5px; -} - -.popper[data-placement^='bottom-end'] { - padding-top: $popper-margin-from-ref; -} -.popper[data-placement^='bottom-end'] .popper__arrow { - border-width: 0 5px 5px 5px; - border-left-color: transparent; - border-right-color: transparent; - border-top-color: transparent; - top: 0; - left: calc(100% - 5px); -} - -// Right -.popper[data-placement^='right'] { - padding-left: $popper-margin-from-ref; -} -.popper[data-placement^='right'] .popper__arrow { - border-width: 5px 5px 5px 0; - border-left-color: transparent; - border-top-color: transparent; - border-bottom-color: transparent; - left: 0; - top: calc(50% - 5px); -} - -// Left -.popper[data-placement^='left'] { - padding-right: $popper-margin-from-ref; -} -.popper[data-placement^='left'] .popper__arrow { - border-width: 5px 0 5px 5px; - border-top-color: transparent; - border-right-color: transparent; - border-bottom-color: transparent; - right: -5px; - top: calc(50% - 5px); -} - -.popper__target, -.popper__manager { - display: inline-block; -} - -.popper__manager--block { - display: block; -} diff --git a/packages/grafana-ui/src/components/Tooltip/index.ts b/packages/grafana-ui/src/components/Tooltip/index.ts new file mode 100644 index 00000000000..9261fe33c04 --- /dev/null +++ b/packages/grafana-ui/src/components/Tooltip/index.ts @@ -0,0 +1,3 @@ +export { Tooltip, TooltipProps } from './Tooltip'; +export { PopoverController } from './PopoverController'; +export { TooltipPlacement, PopoverContent, PopoverContentProps } from './types'; diff --git a/packages/grafana-ui/src/components/Tooltip/types.ts b/packages/grafana-ui/src/components/Tooltip/types.ts new file mode 100644 index 00000000000..5c4aedeb9da --- /dev/null +++ b/packages/grafana-ui/src/components/Tooltip/types.ts @@ -0,0 +1,27 @@ +/** + * 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? + updatePopperPosition?: () => void; +} + +export type PopoverContent = string | React.ReactElement | ((props: PopoverContentProps) => JSX.Element); + +export type TooltipPlacement = + | 'auto-start' + | 'auto' + | 'auto-end' + | 'top-start' + | 'top' + | 'top-end' + | 'right-start' + | 'right' + | 'right-end' + | 'bottom-end' + | 'bottom' + | 'bottom-start' + | 'left-end' + | 'left' + | 'left-start'; diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index 97796ad1188..984a9dc466d 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -2,6 +2,5 @@ @import 'Drawer/Drawer'; @import 'Forms/Legacy/Select/Select'; @import 'DateTimePickers/TimeOfDayPicker'; -@import 'Tooltip/Tooltip'; @import 'Slider/Slider'; @import 'uPlot/Plot'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 2d8ae40140f..afd45c7cc6b 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -2,7 +2,8 @@ export { Icon } from './Icon/Icon'; export { IconButton, IconButtonVariant } from './IconButton/IconButton'; export { ConfirmButton } from './ConfirmButton/ConfirmButton'; export { DeleteButton } from './ConfirmButton/DeleteButton'; -export { Tooltip, PopoverContent } from './Tooltip/Tooltip'; +export { Tooltip } from './Tooltip/Tooltip'; +export { PopoverContent } from './Tooltip/types'; export { PopoverController } from './Tooltip/PopoverController'; export { Popover } from './Tooltip/Popover'; export { Portal } from './Portal/Portal'; diff --git a/public/app/features/alerting/unified/Receivers.test.tsx b/public/app/features/alerting/unified/Receivers.test.tsx index e75408c7c80..6fa8e3a65f7 100644 --- a/public/app/features/alerting/unified/Receivers.test.tsx +++ b/public/app/features/alerting/unified/Receivers.test.tsx @@ -174,23 +174,28 @@ describe('Receivers', () => { await renderReceivers(); // go to new contact point page - userEvent.click(await ui.newContactPointButton.find()); + await act(async () => { + userEvent.click(await ui.newContactPointButton.find()); + }); await byRole('heading', { name: /create contact point/i }).find(); + expect(locationService.getLocation().pathname).toEqual('/alerting/notifications/receivers/new'); - // type in a name for the new receiver - userEvent.type(ui.inputs.name.get(), 'my new receiver'); + await act(async () => { + // type in a name for the new receiver + userEvent.type(ui.inputs.name.get(), 'my new receiver'); - // enter some email - const email = ui.inputs.email.addresses.get(); - userEvent.clear(email); - userEvent.type(email, 'tester@grafana.com'); + // enter some email + const email = ui.inputs.email.addresses.get(); + userEvent.clear(email); + userEvent.type(email, 'tester@grafana.com'); - // try to test the contact point - userEvent.click(ui.testContactPointButton.get()); + // try to test the contact point + userEvent.click(await ui.testContactPointButton.find()); + }); - await waitFor(() => expect(ui.testContactPointModal.get()).toBeInTheDocument()); + await waitFor(() => expect(ui.testContactPointModal.get()).toBeInTheDocument(), { timeout: 1000 }); userEvent.click(ui.customContactPointOption.get()); await waitFor(() => expect(ui.contactPointAnnotationSelect(0).get()).toBeInTheDocument()); @@ -254,7 +259,7 @@ describe('Receivers', () => { // it seems react-hook-form does some async state updates after submit await act(async () => { - await userEvent.click(ui.saveContactButton.get()); + userEvent.click(await ui.saveContactButton.find()); }); // see that we're back to main page and proper api calls have been made diff --git a/public/app/features/alerting/unified/components/rules/ActionIcon.tsx b/public/app/features/alerting/unified/components/rules/ActionIcon.tsx index 90c066b3259..d38406a105a 100644 --- a/public/app/features/alerting/unified/components/rules/ActionIcon.tsx +++ b/public/app/features/alerting/unified/components/rules/ActionIcon.tsx @@ -1,6 +1,5 @@ import { Icon, IconName, useStyles, Tooltip } from '@grafana/ui'; -import { PopoverContent } from '@grafana/ui/src/components/Tooltip/Tooltip'; -import { TooltipPlacement } from '@grafana/ui/src/components/Tooltip/PopoverController'; +import { PopoverContent, TooltipPlacement } from '@grafana/ui/src/components/Tooltip'; import React, { FC } from 'react'; import { css, cx } from '@emotion/css'; import { Link } from 'react-router-dom'; diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx index e1c4ab791d1..0448a550a08 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx @@ -86,7 +86,7 @@ export class PanelHeaderCorner extends Component { const ariaLabel = selectors.components.Panels.Panel.headerCornerInfo(infoMode.toLowerCase()); return ( - + diff --git a/public/app/plugins/datasource/graphite/components/FunctionEditor.tsx b/public/app/plugins/datasource/graphite/components/FunctionEditor.tsx index 38932d077c3..eecbf5b8673 100644 --- a/public/app/plugins/datasource/graphite/components/FunctionEditor.tsx +++ b/public/app/plugins/datasource/graphite/components/FunctionEditor.tsx @@ -1,5 +1,5 @@ -import React, { useRef } from 'react'; -import { PopoverController, Popover, ClickOutsideWrapper, Icon, Tooltip, useStyles2 } from '@grafana/ui'; +import React from 'react'; +import { Icon, Tooltip, useStyles2 } from '@grafana/ui'; import { FunctionEditorControls, FunctionEditorControlsProps } from './FunctionEditorControls'; import { FuncInstance } from '../gfunc'; import { css } from '@emotion/css'; @@ -19,13 +19,11 @@ const getStyles = (theme: GrafanaTheme2) => { fontSize: theme.typography.bodySmall.fontSize, // to match .gf-form-label cursor: 'pointer', display: 'inline-block', - paddingBottom: '2px', }), }; }; const FunctionEditor: React.FC = ({ onMoveLeft, onMoveRight, func, ...props }) => { - const triggerRef = useRef(null); const styles = useStyles2(getStyles); const renderContent = ({ updatePopperPosition }: any) => ( @@ -44,41 +42,16 @@ const FunctionEditor: React.FC = ({ onMoveLeft, onMoveRight ); return ( - - {(showPopper, hidePopper, popperProps) => { - return ( - <> - {triggerRef.current && ( - ( - - )} - /> - )} - { - if (popperProps.show) { - hidePopper(); - } - }} - > - - {func.def.unknown && ( - } placement="bottom"> - - - )} - {func.def.name} - - - > - ); - }} - + <> + {func.def.unknown && ( + } placement="bottom" interactive> + + + )} + + {func.def.name} + + > ); }; diff --git a/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx b/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx index 5fe1b311646..4ac712f23fe 100644 --- a/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx +++ b/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx @@ -265,9 +265,6 @@ describe('AnnoListPanel', () => { getMock.mockClear(); expect(screen.getByRole('img')).toBeInTheDocument(); - userEvent.hover(screen.getByRole('img')); - - expect(screen.getByText(/result email/i)).toBeInTheDocument(); }); }); }); diff --git a/yarn.lock b/yarn.lock index d08e00918a1..39cf993b453 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4300,6 +4300,7 @@ __metadata: react-hook-form: 7.5.3 react-inlinesvg: 2.3.0 react-popper: 2.2.5 + react-popper-tooltip: ^4.3.1 react-router-dom: ^5.2.0 react-select: 5.2.2 react-select-event: ^5.1.0 @@ -6613,7 +6614,7 @@ __metadata: languageName: node linkType: hard -"@popperjs/core@npm:2.11.2": +"@popperjs/core@npm:2.11.2, @popperjs/core@npm:^2.10.2": version: 2.11.2 resolution: "@popperjs/core@npm:2.11.2" checksum: 5695bf020eda54636e16a62dc9b5fdd92beaf7b2d19f62fcef049d57c5cff92773562d80cbf760b217c3ec928da310eb24994ab6a00fd39dffa0af9b5dfc01a6 @@ -20311,6 +20312,7 @@ __metadata: react-loadable: 5.5.0 react-moveable: 0.30.3 react-popper: 2.2.5 + react-popper-tooltip: ^4.3.1 react-redux: 7.2.6 react-refresh: 0.11.0 react-resizable: 3.0.4 @@ -30408,7 +30410,21 @@ __metadata: languageName: node linkType: hard -"react-popper@npm:2.2.5, react-popper@npm:^2.2.4": +"react-popper-tooltip@npm:^4.3.1": + version: 4.3.1 + resolution: "react-popper-tooltip@npm:4.3.1" + dependencies: + "@babel/runtime": ^7.15.4 + "@popperjs/core": ^2.10.2 + react-popper: ^2.2.5 + peerDependencies: + react: ">=16.6.0" + react-dom: ">=16.6.0" + checksum: 82ae84c3b75324ddcafbc3ff4358d77780d20ff28dc5b01a3500b31b34e84caae5dbe3b569165b967b4cdbbacce7af6886e006e0921ee0da5b73638893014e0e + languageName: node + linkType: hard + +"react-popper@npm:2.2.5, react-popper@npm:^2.2.4, react-popper@npm:^2.2.5": version: 2.2.5 resolution: "react-popper@npm:2.2.5" dependencies: