From 0c404a4cd9a7d1186b33c6265cbc6bba232eb569 Mon Sep 17 00:00:00 2001
From: Adela Almasan <88068998+adela-almasan@users.noreply.github.com>
Date: Mon, 2 Oct 2023 22:10:22 -0500
Subject: [PATCH] Tooltip: VizTooltip components (#75794)
---
.../src/components/VizTooltip/HeaderLabel.tsx | 50 +++++++++++
.../VizTooltip/VizTooltipContent.tsx | 54 ++++++++++++
.../VizTooltip/VizTooltipFooter.tsx | 65 +++++++++++++++
.../VizTooltip/VizTooltipHeader.tsx | 42 ++++++++++
.../VizTooltip/VizTooltipHeaderLabelValue.tsx | 83 +++++++++++++++++++
.../src/components/VizTooltip/types.ts | 24 ++++++
.../src/components/VizTooltip/utils.ts | 28 +++++++
7 files changed, 346 insertions(+)
create mode 100644 packages/grafana-ui/src/components/VizTooltip/HeaderLabel.tsx
create mode 100644 packages/grafana-ui/src/components/VizTooltip/VizTooltipContent.tsx
create mode 100644 packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx
create mode 100644 packages/grafana-ui/src/components/VizTooltip/VizTooltipHeader.tsx
create mode 100644 packages/grafana-ui/src/components/VizTooltip/VizTooltipHeaderLabelValue.tsx
create mode 100644 packages/grafana-ui/src/components/VizTooltip/types.ts
diff --git a/packages/grafana-ui/src/components/VizTooltip/HeaderLabel.tsx b/packages/grafana-ui/src/components/VizTooltip/HeaderLabel.tsx
new file mode 100644
index 00000000000..9303073f222
--- /dev/null
+++ b/packages/grafana-ui/src/components/VizTooltip/HeaderLabel.tsx
@@ -0,0 +1,50 @@
+import { css } from '@emotion/css';
+import React from 'react';
+
+import { GrafanaTheme2 } from '@grafana/data';
+
+import { HorizontalGroup, Tooltip } from '..';
+import { useStyles2 } from '../../themes';
+
+import { LabelValue } from './types';
+
+interface Props {
+ headerLabel: LabelValue;
+}
+
+export const HeaderLabel = ({ headerLabel }: Props) => {
+ const styles = useStyles2(getStyles);
+
+ return (
+
+
+ {headerLabel.label}
+
+ {headerLabel.value}
+
+
+
+ );
+};
+
+const getStyles = (theme: GrafanaTheme2) => ({
+ label: css({
+ color: theme.colors.text.secondary,
+ paddingRight: theme.spacing(0.5),
+ fontWeight: 400,
+ }),
+ value: css({
+ fontWeight: 500,
+ lineHeight: '18px',
+ alignSelf: 'center',
+ }),
+ wrapper: css({
+ display: 'flex',
+ flexDirection: 'row',
+ textOverflow: 'ellipsis',
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ width: '250px',
+ maskImage: 'linear-gradient(90deg, rgba(0, 0, 0, 1) 80%, transparent)',
+ }),
+});
diff --git a/packages/grafana-ui/src/components/VizTooltip/VizTooltipContent.tsx b/packages/grafana-ui/src/components/VizTooltip/VizTooltipContent.tsx
new file mode 100644
index 00000000000..6cedcfa9744
--- /dev/null
+++ b/packages/grafana-ui/src/components/VizTooltip/VizTooltipContent.tsx
@@ -0,0 +1,54 @@
+import { css } from '@emotion/css';
+import React, { ReactElement } from 'react';
+
+import { GrafanaTheme2 } from '@grafana/data';
+
+import { HorizontalGroup } from '..';
+import { useStyles2 } from '../../themes';
+
+import { LabelValue } from './types';
+
+interface Props {
+ contentLabelValue: LabelValue[];
+ customContent?: ReactElement | null;
+}
+export const VizTooltipContent = ({ contentLabelValue, customContent }: Props) => {
+ const styles = useStyles2(getStyles);
+
+ return (
+
+
+ {contentLabelValue?.map((labelValue, i) => {
+ return (
+
+ {labelValue.label}
+ {labelValue.value}
+
+ );
+ })}
+
+ {customContent &&
{customContent}
}
+
+ );
+};
+
+const getStyles = (theme: GrafanaTheme2) => ({
+ wrapper: css({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ gap: 4,
+ borderTop: `1px solid ${theme.colors.border.medium}`,
+ padding: `${theme.spacing(1)} 0`,
+ }),
+ customContentPadding: css({
+ padding: `${theme.spacing(1)} 0`,
+ }),
+ label: css({
+ color: theme.colors.text.secondary,
+ fontWeight: 400,
+ }),
+ value: css({
+ fontWeight: 500,
+ }),
+});
diff --git a/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx b/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx
new file mode 100644
index 00000000000..490d62bf582
--- /dev/null
+++ b/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx
@@ -0,0 +1,65 @@
+import { css } from '@emotion/css';
+import React from 'react';
+
+import { Field, GrafanaTheme2, LinkModel } from '@grafana/data';
+
+import { Button, ButtonProps, DataLinkButton, HorizontalGroup } from '..';
+import { useStyles2 } from '../../themes';
+
+interface Props {
+ dataLinks: Array>;
+ canAnnotate: boolean;
+}
+
+export const ADD_ANNOTATION_ID = 'add-annotation-button';
+
+export const VizTooltipFooter = ({ dataLinks, canAnnotate }: Props) => {
+ const styles = useStyles2(getStyles);
+
+ const renderDataLinks = () => {
+ const buttonProps: ButtonProps = {
+ variant: 'secondary',
+ };
+
+ return (
+
+ {dataLinks.map((link, i) => (
+
+ ))}
+
+ );
+ };
+
+ return (
+
+ {dataLinks.length > 0 &&
{renderDataLinks()}
}
+ {canAnnotate && (
+
+
+
+ )}
+
+ );
+};
+
+const getStyles = (theme: GrafanaTheme2) => ({
+ wrapper: css({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ }),
+ dataLinks: css({
+ height: 40,
+ overflowX: 'auto',
+ overflowY: 'hidden',
+ whiteSpace: 'nowrap',
+ borderTop: `1px solid ${theme.colors.border.medium}`,
+ maskImage: 'linear-gradient(90deg, rgba(0, 0, 0, 1) 80%, transparent)',
+ }),
+ addAnnotations: css({
+ borderTop: `1px solid ${theme.colors.border.medium}`,
+ paddingTop: theme.spacing(1),
+ }),
+});
diff --git a/packages/grafana-ui/src/components/VizTooltip/VizTooltipHeader.tsx b/packages/grafana-ui/src/components/VizTooltip/VizTooltipHeader.tsx
new file mode 100644
index 00000000000..fe7233f551d
--- /dev/null
+++ b/packages/grafana-ui/src/components/VizTooltip/VizTooltipHeader.tsx
@@ -0,0 +1,42 @@
+import { css } from '@emotion/css';
+import React, { ReactElement } from 'react';
+
+import { GrafanaTheme2 } from '@grafana/data';
+
+import { useStyles2 } from '../../themes';
+
+import { HeaderLabel } from './HeaderLabel';
+import { VizTooltipHeaderLabelValue } from './VizTooltipHeaderLabelValue';
+import { LabelValue } from './types';
+
+interface Props {
+ headerLabel: LabelValue;
+ keyValuePairs?: LabelValue[];
+ customValueDisplay?: ReactElement | null;
+}
+export const VizTooltipHeader = ({ headerLabel, keyValuePairs, customValueDisplay }: Props) => {
+ const styles = useStyles2(getStyles);
+
+ const renderValue = () => {
+ if (customValueDisplay) {
+ return customValueDisplay;
+ }
+
+ return ;
+ };
+ return (
+
+
+ {renderValue()}
+
+ );
+};
+
+const getStyles = (theme: GrafanaTheme2) => ({
+ wrapper: css({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ paddingBottom: theme.spacing(1),
+ }),
+});
diff --git a/packages/grafana-ui/src/components/VizTooltip/VizTooltipHeaderLabelValue.tsx b/packages/grafana-ui/src/components/VizTooltip/VizTooltipHeaderLabelValue.tsx
new file mode 100644
index 00000000000..13d8db31a8c
--- /dev/null
+++ b/packages/grafana-ui/src/components/VizTooltip/VizTooltipHeaderLabelValue.tsx
@@ -0,0 +1,83 @@
+import { css, cx } from '@emotion/css';
+import React from 'react';
+
+import { GrafanaTheme2 } from '@grafana/data';
+
+import { HorizontalGroup } from '..';
+import { useStyles2 } from '../../themes';
+
+import { LabelValue } from './types';
+import { getColorIndicatorClass } from './utils';
+
+interface Props {
+ keyValuePairs?: LabelValue[];
+}
+
+export type HeaderLabelValueStyles = ReturnType;
+
+export const VizTooltipHeaderLabelValue = ({ keyValuePairs }: Props) => {
+ const styles = useStyles2(getStyles);
+
+ return (
+ <>
+ {keyValuePairs?.map((keyValuePair, i) => {
+ return (
+
+ {keyValuePair.label}
+ <>
+
+ {keyValuePair.value}
+ >
+
+ );
+ })}
+ >
+ );
+};
+
+// @TODO Update classes/add svgs?
+const getStyles = (theme: GrafanaTheme2) => ({
+ hgContainer: css({
+ flexGrow: 1,
+ }),
+ colorIndicator: css({
+ marginRight: theme.spacing(0.5),
+ }),
+ label: css({
+ color: theme.colors.text.secondary,
+ fontWeight: 400,
+ }),
+ series: css({
+ width: '14px',
+ height: '4px',
+ borderRadius: theme.shape.radius.pill,
+ }),
+ value: css({
+ width: '12px',
+ height: '12px',
+ borderRadius: theme.shape.radius.default,
+ fontWeight: 500,
+ }),
+ hexagon: css({}),
+ pie_1_4: css({}),
+ pie_2_4: css({}),
+ pie_3_4: css({}),
+ marker_sm: css({
+ width: '4px',
+ height: '4px',
+ borderRadius: theme.shape.radius.circle,
+ }),
+ marker_md: css({
+ width: '8px',
+ height: '8px',
+ borderRadius: theme.shape.radius.circle,
+ }),
+ marker_lg: css({
+ width: '12px',
+ height: '12px',
+ borderRadius: theme.shape.radius.circle,
+ }),
+});
diff --git a/packages/grafana-ui/src/components/VizTooltip/types.ts b/packages/grafana-ui/src/components/VizTooltip/types.ts
new file mode 100644
index 00000000000..615ad81b331
--- /dev/null
+++ b/packages/grafana-ui/src/components/VizTooltip/types.ts
@@ -0,0 +1,24 @@
+export enum ColorIndicator {
+ series = 'series',
+ value = 'value',
+ hexagon = 'hexagon',
+ pie_1_4 = 'pie_1_4',
+ pie_2_4 = 'pie_2_4',
+ pie_3_4 = 'pie_3_4',
+ marker_sm = 'marker_sm',
+ marker_md = 'marker_md',
+ marker_lg = 'marker_lg',
+}
+
+export enum LabelValuePlacement {
+ hidden = 'hidden',
+ leading = 'leading',
+ trailing = 'trailing',
+}
+
+export interface LabelValue {
+ label: string;
+ value: string | number | null;
+ color?: string;
+ colorIndicator?: ColorIndicator;
+}
diff --git a/packages/grafana-ui/src/components/VizTooltip/utils.ts b/packages/grafana-ui/src/components/VizTooltip/utils.ts
index 6517962fc07..44f65d28ffa 100644
--- a/packages/grafana-ui/src/components/VizTooltip/utils.ts
+++ b/packages/grafana-ui/src/components/VizTooltip/utils.ts
@@ -1,3 +1,6 @@
+import { HeaderLabelValueStyles } from './VizTooltipHeaderLabelValue';
+import { ColorIndicator } from './types';
+
export const calculateTooltipPosition = (
xPos = 0,
yPos = 0,
@@ -38,3 +41,28 @@ export const calculateTooltipPosition = (
}
return { x, y };
};
+
+export const getColorIndicatorClass = (colorIndicator: string, styles: HeaderLabelValueStyles) => {
+ switch (colorIndicator) {
+ case ColorIndicator.value:
+ return styles.value;
+ case ColorIndicator.series:
+ return styles.series;
+ case ColorIndicator.hexagon:
+ return styles.hexagon;
+ case ColorIndicator.pie_1_4:
+ return styles.pie_1_4;
+ case ColorIndicator.pie_2_4:
+ return styles.pie_2_4;
+ case ColorIndicator.pie_3_4:
+ return styles.pie_3_4;
+ case ColorIndicator.marker_sm:
+ return styles.marker_sm;
+ case ColorIndicator.marker_md:
+ return styles.marker_md;
+ case ColorIndicator.marker_lg:
+ return styles.marker_lg;
+ default:
+ return styles.value;
+ }
+};