mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 01:53:33 -06:00
Tooltip: VizTooltip components (#75794)
This commit is contained in:
parent
a7cedee7eb
commit
0c404a4cd9
@ -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 (
|
||||
<HorizontalGroup justify-content="space-between" spacing="lg" wrap>
|
||||
<div className={styles.wrapper}>
|
||||
<span className={styles.label}>{headerLabel.label}</span>
|
||||
<Tooltip content={headerLabel.value ? headerLabel.value.toString() : ''}>
|
||||
<span className={styles.value}>{headerLabel.value}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
};
|
||||
|
||||
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)',
|
||||
}),
|
||||
});
|
@ -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 (
|
||||
<div className={styles.wrapper}>
|
||||
<div>
|
||||
{contentLabelValue?.map((labelValue, i) => {
|
||||
return (
|
||||
<HorizontalGroup justify="space-between" spacing="lg" key={i}>
|
||||
<div className={styles.label}>{labelValue.label}</div>
|
||||
<div className={styles.value}>{labelValue.value}</div>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{customContent && <div className={styles.customContentPadding}>{customContent}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
}),
|
||||
});
|
@ -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<LinkModel<Field>>;
|
||||
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 (
|
||||
<HorizontalGroup>
|
||||
{dataLinks.map((link, i) => (
|
||||
<DataLinkButton key={i} link={link} buttonProps={buttonProps} />
|
||||
))}
|
||||
</HorizontalGroup>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
{dataLinks.length > 0 && <div className={styles.dataLinks}>{renderDataLinks()}</div>}
|
||||
{canAnnotate && (
|
||||
<div className={styles.addAnnotations}>
|
||||
<Button icon="comment-alt" variant="secondary" size="sm" id={ADD_ANNOTATION_ID}>
|
||||
Add annotation
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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),
|
||||
}),
|
||||
});
|
@ -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 <VizTooltipHeaderLabelValue keyValuePairs={keyValuePairs} />;
|
||||
};
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<HeaderLabel headerLabel={headerLabel} />
|
||||
{renderValue()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
wrapper: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 1,
|
||||
paddingBottom: theme.spacing(1),
|
||||
}),
|
||||
});
|
@ -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<typeof getStyles>;
|
||||
|
||||
export const VizTooltipHeaderLabelValue = ({ keyValuePairs }: Props) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<>
|
||||
{keyValuePairs?.map((keyValuePair, i) => {
|
||||
return (
|
||||
<HorizontalGroup justify="space-between" spacing="md" className={styles.hgContainer} key={i}>
|
||||
<div className={styles.label}>{keyValuePair.label}</div>
|
||||
<>
|
||||
<span
|
||||
style={{ backgroundColor: keyValuePair.color }}
|
||||
className={cx(styles.colorIndicator, getColorIndicatorClass(keyValuePair.colorIndicator!, styles))}
|
||||
/>
|
||||
{keyValuePair.value}
|
||||
</>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// @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,
|
||||
}),
|
||||
});
|
24
packages/grafana-ui/src/components/VizTooltip/types.ts
Normal file
24
packages/grafana-ui/src/components/VizTooltip/types.ts
Normal file
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user