Tooltips: Make tooltips non interactive by default (#45053)

* Tooltips: Make tooltips non interactive by default

* More updates and cleanup

* Update

* Make time range picker tooltip interactive

* Fix issue

* Trying to make Receivers test faster

* Make JSONCell tooltip interactive
This commit is contained in:
Torkel Ödegaard 2022-02-08 22:58:20 +01:00 committed by GitHub
parent 16e001e762
commit 7c0b453e19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 293 additions and 311 deletions

View File

@ -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",

View File

@ -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: {

View File

@ -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",

View File

@ -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';

View File

@ -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';

View File

@ -105,7 +105,7 @@ export function UnthemedTimeRangePicker(props: TimeRangePickerProps): ReactEleme
/>
)}
<Tooltip content={<TimePickerTooltip timeRange={value} timeZone={timeZone} />} placement="bottom">
<Tooltip content={<TimePickerTooltip timeRange={value} timeZone={timeZone} />} placement="bottom" interactive>
<ToolbarButton
data-testid={selectors.components.TimePicker.openButton}
aria-label={`Time range picker with current time range ${formattedRange(value, timeZone)} selected`}

View File

@ -1,5 +1,5 @@
import React, { HTMLAttributes } from 'react';
import { PopoverContent } from '../Tooltip/Tooltip';
import { PopoverContent } from '../Tooltip';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { ToolbarButtonVariant, ToolbarButton, ButtonGroup } from '../Button';
import { ClickOutsideWrapper } from '../ClickOutsideWrapper/ClickOutsideWrapper';

View File

@ -1,7 +1,7 @@
import React, { InputHTMLAttributes, FunctionComponent } from 'react';
import { css, cx } from '@emotion/css';
import { InlineFormLabel } from '../FormLabel/FormLabel';
import { PopoverContent } from '../Tooltip/Tooltip';
import { PopoverContent } from '../Tooltip';
export interface Props extends InputHTMLAttributes<HTMLInputElement> {
label: string;

View File

@ -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 {

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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<TooltipProps, 'children' | 'content'> {

View File

@ -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<InputHTMLAttributes<HTMLInputElement>, 'onReset'> {
// Function to use when reset is clicked. Means you have to reset the input value yourself as this is uncontrolled

View File

@ -29,7 +29,7 @@ export function JSONViewCell(props: TableCellProps): JSX.Element {
const content = <JSONTooltip value={value} />;
return (
<Tooltip placement="auto-start" content={content} theme="info-alt">
<Tooltip placement="auto-start" content={content} theme="info" interactive>
<div {...cellProps} className={tableStyles.cellContainer}>
<div className={cx(tableStyles.cellText, txt)}>{displayValue}</div>
</div>

View File

@ -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',

View File

@ -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,

View File

@ -61,9 +61,10 @@ export const Basic: Story = ({ content, ...args }) => {
</Tooltip>
);
};
Basic.args = {
content: 'This is a tooltip',
theme: 'info',
show: true,
show: undefined,
placement: 'auto',
};

View File

@ -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<any> | ((props: PopoverContentProps) => JSX.Element);
export const Tooltip: FC<TooltipProps> = React.memo(({ children, theme, ...controllerProps }: TooltipProps) => {
const tooltipTriggerRef = createRef<HTMLElement | VirtualElement>();
const popperBackgroundClassName = 'popper__background' + (theme ? ' popper__background--' + theme : '');
const styles = useStyles2(getStyles);
const containerStyle = styles[theme ?? 'info'];
return (
<PopoverController {...controllerProps}>
{(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 && (
<Popover
{...payloadProps}
onMouseEnter={showPopper}
onMouseLeave={hidePopper}
referenceElement={tooltipTriggerRef.current}
wrapperClassName="popper"
className={popperBackgroundClassName}
renderArrow={({ arrowProps, placement }) => (
<div className="popper__arrow" data-placement={placement} {...arrowProps} />
)}
/>
)}
{React.cloneElement(children, {
ref: tooltipTriggerRef,
onMouseEnter: showPopper,
onMouseLeave: hidePopper,
onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => closePopover(event, hidePopper),
onFocus: showPopper,
onBlur: hidePopper,
})}
</>
);
}}
</PopoverController>
<>
{React.cloneElement(children, {
ref: setTriggerRef,
})}
{visible && (
<Portal>
<div ref={setTooltipRef} {...getTooltipProps({ className: containerStyle })}>
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
{typeof content === 'string' && content}
{React.isValidElement(content) && React.cloneElement(content)}
{typeof content === 'function' &&
content({
updatePopperPosition: update as any,
})}
</div>
</Portal>
)}
</>
);
});
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,
};
}

View File

@ -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;
}

View File

@ -0,0 +1,3 @@
export { Tooltip, TooltipProps } from './Tooltip';
export { PopoverController } from './PopoverController';
export { TooltipPlacement, PopoverContent, PopoverContentProps } from './types';

View File

@ -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<any> | ((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';

View File

@ -2,6 +2,5 @@
@import 'Drawer/Drawer';
@import 'Forms/Legacy/Select/Select';
@import 'DateTimePickers/TimeOfDayPicker';
@import 'Tooltip/Tooltip';
@import 'Slider/Slider';
@import 'uPlot/Plot';

View File

@ -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';

View File

@ -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

View File

@ -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';

View File

@ -86,7 +86,7 @@ export class PanelHeaderCorner extends Component<Props> {
const ariaLabel = selectors.components.Panels.Panel.headerCornerInfo(infoMode.toLowerCase());
return (
<Tooltip content={content} placement="top-start" theme={theme}>
<Tooltip content={content} placement="top-start" theme={theme} interactive>
<section className={className} onClick={onClick} aria-label={ariaLabel}>
<i aria-hidden className="fa" />
<span className="panel-info-corner-inner" />

View File

@ -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<FunctionEditorProps> = ({ onMoveLeft, onMoveRight, func, ...props }) => {
const triggerRef = useRef<HTMLSpanElement>(null);
const styles = useStyles2(getStyles);
const renderContent = ({ updatePopperPosition }: any) => (
@ -44,41 +42,16 @@ const FunctionEditor: React.FC<FunctionEditorProps> = ({ onMoveLeft, onMoveRight
);
return (
<PopoverController content={renderContent} placement="top" hideAfter={100}>
{(showPopper, hidePopper, popperProps) => {
return (
<>
{triggerRef.current && (
<Popover
{...popperProps}
referenceElement={triggerRef.current}
wrapperClassName="popper"
className="popper__background"
renderArrow={({ arrowProps, placement }) => (
<div className="popper__arrow" data-placement={placement} {...arrowProps} />
)}
/>
)}
<ClickOutsideWrapper
onClick={() => {
if (popperProps.show) {
hidePopper();
}
}}
>
<span ref={triggerRef} onClick={popperProps.show ? hidePopper : showPopper} className={styles.label}>
{func.def.unknown && (
<Tooltip content={<TooltipContent />} placement="bottom">
<Icon data-testid="warning-icon" name="exclamation-triangle" size="xs" className={styles.icon} />
</Tooltip>
)}
{func.def.name}
</span>
</ClickOutsideWrapper>
</>
);
}}
</PopoverController>
<>
{func.def.unknown && (
<Tooltip content={<TooltipContent />} placement="bottom" interactive>
<Icon data-testid="warning-icon" name="exclamation-triangle" size="xs" className={styles.icon} />
</Tooltip>
)}
<Tooltip content={renderContent} placement="top" interactive>
<span className={styles.label}>{func.def.name}</span>
</Tooltip>
</>
);
};

View File

@ -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();
});
});
});

View File

@ -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: