mirror of https://github.com/grafana/grafana
Tooltips: Support long labels (#77735)
* feat(tooltips): support long labels Co-authored-by: Adela Almasan <adela.almasan@grafana.com>pull/78484/head
parent
2659409191
commit
6f0c5395ac
@ -1,46 +1,24 @@ |
|||||||
import { css } from '@emotion/css'; |
|
||||||
import React from 'react'; |
import React from 'react'; |
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data'; |
import { VizTooltipRow } from './VizTooltipRow'; |
||||||
|
|
||||||
import { HorizontalGroup } from '..'; |
|
||||||
import { useStyles2 } from '../../themes'; |
|
||||||
|
|
||||||
import { VizTooltipColorIndicator } from './VizTooltipColorIndicator'; |
|
||||||
import { LabelValue } from './types'; |
import { LabelValue } from './types'; |
||||||
|
|
||||||
interface Props { |
interface Props { |
||||||
keyValuePairs?: LabelValue[]; |
keyValuePairs?: LabelValue[]; |
||||||
} |
} |
||||||
|
|
||||||
export const VizTooltipHeaderLabelValue = ({ keyValuePairs }: Props) => { |
export const VizTooltipHeaderLabelValue = ({ keyValuePairs }: Props) => ( |
||||||
const styles = useStyles2(getStyles); |
<> |
||||||
|
{keyValuePairs?.map((keyValuePair, i) => ( |
||||||
return ( |
<VizTooltipRow |
||||||
<> |
key={i} |
||||||
{keyValuePairs?.map((keyValuePair, i) => { |
label={keyValuePair.label} |
||||||
return ( |
value={keyValuePair.value} |
||||||
<HorizontalGroup justify="space-between" spacing="md" className={styles.hgContainer} key={i}> |
color={keyValuePair.color} |
||||||
<div className={styles.label}>{keyValuePair.label}</div> |
colorIndicator={keyValuePair.colorIndicator!} |
||||||
<> |
colorFirst={false} |
||||||
{keyValuePair.color && ( |
justify={'space-between'} |
||||||
<VizTooltipColorIndicator color={keyValuePair.color} colorIndicator={keyValuePair.colorIndicator!} /> |
/> |
||||||
)} |
))} |
||||||
{keyValuePair.value} |
</> |
||||||
</> |
); |
||||||
</HorizontalGroup> |
|
||||||
); |
|
||||||
})} |
|
||||||
</> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({ |
|
||||||
hgContainer: css({ |
|
||||||
flexGrow: 1, |
|
||||||
}), |
|
||||||
label: css({ |
|
||||||
color: theme.colors.text.secondary, |
|
||||||
fontWeight: 400, |
|
||||||
}), |
|
||||||
}); |
|
||||||
|
@ -0,0 +1,119 @@ |
|||||||
|
import { css, cx } from '@emotion/css'; |
||||||
|
import React, { useState } from 'react'; |
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data'; |
||||||
|
|
||||||
|
import { useStyles2 } from '../../themes'; |
||||||
|
import { Tooltip } from '../Tooltip'; |
||||||
|
|
||||||
|
import { VizTooltipColorIndicator } from './VizTooltipColorIndicator'; |
||||||
|
import { LabelValue } from './types'; |
||||||
|
|
||||||
|
interface Props extends LabelValue { |
||||||
|
justify?: string; |
||||||
|
colorFirst?: boolean; |
||||||
|
isActive?: boolean; // for series list
|
||||||
|
marginRight?: string; |
||||||
|
} |
||||||
|
|
||||||
|
export const VizTooltipRow = ({ |
||||||
|
label, |
||||||
|
value, |
||||||
|
color, |
||||||
|
colorIndicator, |
||||||
|
justify = 'flex-start', |
||||||
|
colorFirst = true, |
||||||
|
isActive = false, |
||||||
|
marginRight = '0px', |
||||||
|
}: Props) => { |
||||||
|
const styles = useStyles2(getStyles, justify, marginRight); |
||||||
|
|
||||||
|
const [showLabelTooltip, setShowLabelTooltip] = useState(false); |
||||||
|
const [showValueTooltip, setShowValueTooltip] = useState(false); |
||||||
|
|
||||||
|
const onMouseEnterLabel = (event: React.MouseEvent<HTMLDivElement>) => { |
||||||
|
if (event.currentTarget.offsetWidth < event.currentTarget.scrollWidth) { |
||||||
|
setShowLabelTooltip(true); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const onMouseLeaveLabel = () => setShowLabelTooltip(false); |
||||||
|
|
||||||
|
const onMouseEnterValue = (event: React.MouseEvent<HTMLDivElement>) => { |
||||||
|
if (event.currentTarget.offsetWidth < event.currentTarget.scrollWidth) { |
||||||
|
setShowValueTooltip(true); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const onMouseLeaveValue = () => setShowValueTooltip(false); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className={styles.contentWrapper}> |
||||||
|
{(color || label) && ( |
||||||
|
<div className={styles.valueWrapper}> |
||||||
|
{color && colorFirst && <VizTooltipColorIndicator color={color} colorIndicator={colorIndicator!} />} |
||||||
|
<Tooltip content={label} interactive={false} show={showLabelTooltip}> |
||||||
|
<div |
||||||
|
className={cx(styles.label, isActive && styles.activeSeries)} |
||||||
|
onMouseEnter={onMouseEnterLabel} |
||||||
|
onMouseLeave={onMouseLeaveLabel} |
||||||
|
> |
||||||
|
{label} |
||||||
|
</div> |
||||||
|
</Tooltip> |
||||||
|
</div> |
||||||
|
)} |
||||||
|
|
||||||
|
<div className={styles.valueWrapper}> |
||||||
|
{color && !colorFirst && <VizTooltipColorIndicator color={color} colorIndicator={colorIndicator!} />} |
||||||
|
<Tooltip content={value ? value.toString() : ''} interactive={false} show={showValueTooltip}> |
||||||
|
<div className={cx(styles.value, isActive)} onMouseEnter={onMouseEnterValue} onMouseLeave={onMouseLeaveValue}> |
||||||
|
{value} |
||||||
|
</div> |
||||||
|
</Tooltip> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2, justify: string, marginRight: string) => ({ |
||||||
|
wrapper: css({ |
||||||
|
display: 'flex', |
||||||
|
flexDirection: 'column', |
||||||
|
flex: 1, |
||||||
|
gap: 4, |
||||||
|
borderTop: `1px solid ${theme.colors.border.medium}`, |
||||||
|
padding: theme.spacing(1), |
||||||
|
}), |
||||||
|
contentWrapper: css({ |
||||||
|
display: 'flex', |
||||||
|
alignItems: 'center', |
||||||
|
justifyContent: justify, |
||||||
|
flexWrap: 'wrap', |
||||||
|
marginRight: marginRight, |
||||||
|
}), |
||||||
|
customContentPadding: css({ |
||||||
|
padding: `${theme.spacing(1)} 0`, |
||||||
|
}), |
||||||
|
label: css({ |
||||||
|
color: theme.colors.text.secondary, |
||||||
|
fontWeight: 400, |
||||||
|
textOverflow: 'ellipsis', |
||||||
|
overflow: 'hidden', |
||||||
|
marginRight: theme.spacing(0.5), |
||||||
|
}), |
||||||
|
value: css({ |
||||||
|
fontWeight: 500, |
||||||
|
textOverflow: 'ellipsis', |
||||||
|
overflow: 'hidden', |
||||||
|
}), |
||||||
|
valueWrapper: css({ |
||||||
|
display: 'flex', |
||||||
|
alignItems: 'center', |
||||||
|
minWidth: 0, |
||||||
|
}), |
||||||
|
activeSeries: css({ |
||||||
|
fontWeight: theme.typography.fontWeightBold, |
||||||
|
color: theme.colors.text.maxContrast, |
||||||
|
}), |
||||||
|
}); |
Loading…
Reference in new issue