Skip to content

Commit

Permalink
VizLegend: Represent line style in series legend and tooltip (#87558)
Browse files Browse the repository at this point in the history
* wip

* aand finished

* remove console log

* use css for styling

* user boerder for solid as well

* misc fixes

* attempt css based approach

---------

Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
  • Loading branch information
domasx2 and leeoniya committed May 10, 2024
1 parent f880abc commit 708bcda
Show file tree
Hide file tree
Showing 11 changed files with 76 additions and 28 deletions.
50 changes: 36 additions & 14 deletions packages/grafana-ui/src/components/VizLegend/SeriesIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { css, cx } from '@emotion/css';
import React, { CSSProperties } from 'react';

import { fieldColorModeRegistry } from '@grafana/data';
import { GrafanaTheme2, fieldColorModeRegistry } from '@grafana/data';
import { LineStyle } from '@grafana/schema';

import { useTheme2, useStyles2 } from '../../themes';

export interface Props extends React.HTMLAttributes<HTMLDivElement> {
color?: string;
gradient?: string;
lineStyle?: LineStyle;
}

export const SeriesIcon = React.memo(
React.forwardRef<HTMLDivElement, Props>(({ color, className, gradient, ...restProps }, ref) => {
React.forwardRef<HTMLDivElement, Props>(({ color, className, gradient, lineStyle, ...restProps }, ref) => {
const theme = useTheme2();
const styles2 = useStyles2(getStyles);
const styles = useStyles2(getStyles);

let cssColor: string;

Expand All @@ -29,28 +31,48 @@ export const SeriesIcon = React.memo(
cssColor = color!;
}

const styles: CSSProperties = {
background: cssColor,
width: '14px',
height: '4px',
borderRadius: theme.shape.radius.pill,
display: 'inline-block',
marginRight: '8px',
};
let customStyle: CSSProperties;

if (lineStyle?.fill === 'dot' && !gradient) {
// make a circle bg image and repeat it
customStyle = {
backgroundImage: `radial-gradient(circle at 2px 2px, ${color} 2px, transparent 0)`,
backgroundSize: '4px 4px',
backgroundRepeat: 'space',
};
} else if (lineStyle?.fill === 'dash' && !gradient) {
// make a rectangle bg image and repeat it
customStyle = {
backgroundImage: `linear-gradient(to right, ${color} 100%, transparent 0%)`,
backgroundSize: '6px 4px',
backgroundRepeat: 'space',
};
} else {
customStyle = {
background: cssColor,
borderRadius: theme.shape.radius.pill,
};
}

return (
<div
data-testid="series-icon"
ref={ref}
className={cx(className, styles2.forcedColors)}
style={styles}
className={cx(className, styles.forcedColors, styles.container)}
style={customStyle}
{...restProps}
/>
);
})
);

const getStyles = () => ({
const getStyles = (theme: GrafanaTheme2) => ({
container: css({
marginRight: '8px',
display: 'inline-block',
width: '14px',
height: '4px',
}),
forcedColors: css({
'@media (forced-colors: active)': {
forcedColorAdjust: 'none',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const VizLegendListItem = <T = unknown,>({
color={item.color}
gradient={item.gradient}
readonly={readonly}
lineStyle={item.lineStyle}
/>
<button
disabled={readonly}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useCallback } from 'react';

import { LineStyle } from '@grafana/schema';

import { SeriesColorPicker } from '../ColorPicker/ColorPicker';
import { usePanelContext } from '../PanelChrome';

Expand All @@ -10,12 +12,13 @@ interface Props {
color?: string;
gradient?: string;
readonly?: boolean;
lineStyle?: LineStyle;
}

/**
* @internal
*/
export const VizLegendSeriesIcon = React.memo(({ seriesName, color, gradient, readonly }: Props) => {
export const VizLegendSeriesIcon = React.memo(({ seriesName, color, gradient, readonly, lineStyle }: Props) => {
const { onSeriesColorChange } = usePanelContext();
const onChange = useCallback(
(color: string) => {
Expand All @@ -34,12 +37,13 @@ export const VizLegendSeriesIcon = React.memo(({ seriesName, color, gradient, re
ref={ref}
onClick={showColorPicker}
onMouseLeave={hideColorPicker}
lineStyle={lineStyle}
/>
)}
</SeriesColorPicker>
);
}
return <SeriesIcon color={color} gradient={gradient} />;
return <SeriesIcon color={color} gradient={gradient} lineStyle={lineStyle} />;
});

VizLegendSeriesIcon.displayName = 'VizLegendSeriesIcon';
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ export const LegendTableItem = ({
<tr className={cx(styles.row, className)}>
<td>
<span className={styles.itemWrapper}>
<VizLegendSeriesIcon color={item.color} seriesName={item.fieldName ?? item.label} readonly={readonly} />
<VizLegendSeriesIcon
color={item.color}
seriesName={item.fieldName ?? item.label}
readonly={readonly}
lineStyle={item.lineStyle}
/>
<button
disabled={readonly}
type="button"
Expand Down
3 changes: 2 additions & 1 deletion packages/grafana-ui/src/components/VizLegend/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

import { DataFrameFieldIndex, DisplayValue } from '@grafana/data';
import { LegendDisplayMode, LegendPlacement } from '@grafana/schema';
import { LegendDisplayMode, LegendPlacement, LineStyle } from '@grafana/schema';

export enum SeriesVisibilityChangeBehavior {
Isolate,
Expand Down Expand Up @@ -49,4 +49,5 @@ export interface VizLegendItem<T = any> {
fieldIndex?: DataFrameFieldIndex;
fieldName?: string;
data?: T;
lineStyle?: LineStyle;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { css, cx } from '@emotion/css';
import React from 'react';

import { FALLBACK_COLOR, GrafanaTheme2 } from '@grafana/data';
import { LineStyle } from '@grafana/schema';

import { useStyles2 } from '../../themes';
import { SeriesIcon } from '../VizLegend/SeriesIcon';

import { ColorIndicator, DEFAULT_COLOR_INDICATOR } from './types';
import { getColorIndicatorClass } from './utils';
Expand All @@ -17,6 +19,7 @@ interface Props {
color?: string;
colorIndicator?: ColorIndicator;
position?: ColorIndicatorPosition;
lineStyle?: LineStyle;
}

export type ColorIndicatorStyles = ReturnType<typeof getStyles>;
Expand All @@ -25,9 +28,20 @@ export const VizTooltipColorIndicator = ({
color = FALLBACK_COLOR,
colorIndicator = DEFAULT_COLOR_INDICATOR,
position = ColorIndicatorPosition.Leading,
lineStyle,
}: Props) => {
const styles = useStyles2(getStyles);

if (colorIndicator === ColorIndicator.series) {
return (
<SeriesIcon
color={color}
lineStyle={lineStyle}
className={position === ColorIndicatorPosition.Leading ? styles.leading : styles.trailing}
/>
);
}

return (
<span
style={{ backgroundColor: color }}
Expand All @@ -47,12 +61,6 @@ const getStyles = (theme: GrafanaTheme2) => ({
trailing: css({
marginLeft: theme.spacing(0.5),
}),
series: css({
width: '14px',
height: '4px',
borderRadius: theme.shape.radius.pill,
minWidth: '14px',
}),
value: css({
width: '12px',
height: '12px',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const VizTooltipContent = ({

return (
<div className={styles.wrapper} style={scrollableStyle}>
{items.map(({ label, value, color, colorIndicator, colorPlacement, isActive }, i) => (
{items.map(({ label, value, color, colorIndicator, colorPlacement, isActive, lineStyle }, i) => (
<VizTooltipRow
key={i}
label={label}
Expand All @@ -45,6 +45,7 @@ export const VizTooltipContent = ({
isActive={isActive}
justify={'space-between'}
isPinned={isPinned}
lineStyle={lineStyle}
/>
))}
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const VizTooltipRow = ({
isActive = false,
marginRight = '0px',
isPinned,
lineStyle,
}: VizTooltipRowProps) => {
const styles = useStyles2(getStyles, justify, marginRight);

Expand Down Expand Up @@ -118,7 +119,7 @@ export const VizTooltipRow = ({
{(color || label) && (
<div className={styles.valueWrapper}>
{color && colorPlacement === ColorPlacement.first && (
<VizTooltipColorIndicator color={color} colorIndicator={colorIndicator} />
<VizTooltipColorIndicator color={color} colorIndicator={colorIndicator} lineStyle={lineStyle} />
)}
{!isPinned ? (
<div className={cx(styles.label, isActive && styles.activeSeries)}>{label}</div>
Expand Down Expand Up @@ -154,6 +155,7 @@ export const VizTooltipRow = ({
color={color}
colorIndicator={colorIndicator}
position={ColorIndicatorPosition.Leading}
lineStyle={lineStyle}
/>
)}

Expand Down Expand Up @@ -186,6 +188,7 @@ export const VizTooltipRow = ({
color={color}
colorIndicator={colorIndicator}
position={ColorIndicatorPosition.Trailing}
lineStyle={lineStyle}
/>
)}
</div>
Expand Down
3 changes: 3 additions & 0 deletions packages/grafana-ui/src/components/VizTooltip/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LineStyle } from '@grafana/schema';

export enum ColorIndicator {
series = 'series',
value = 'value',
Expand All @@ -24,6 +26,7 @@ export interface VizTooltipItem {
colorIndicator?: ColorIndicator;
colorPlacement?: ColorPlacement;
isActive?: boolean;
lineStyle?: LineStyle;

// internal/tmp for sorting
numeric?: number;
Expand Down
3 changes: 1 addition & 2 deletions packages/grafana-ui/src/components/VizTooltip/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ export const getColorIndicatorClass = (colorIndicator: string, styles: ColorIndi
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:
Expand Down Expand Up @@ -149,6 +147,7 @@ export const getContentItems = (
colorPlacement,
isActive: mode === TooltipDisplayMode.Multi && seriesIdx === i,
numeric,
lineStyle: field.config.custom?.lineStyle,
});
}

Expand Down
1 change: 1 addition & 0 deletions packages/grafana-ui/src/components/uPlot/PlotLegend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const PlotLegend = React.memo(
yAxis: axisPlacement === AxisPlacement.Left || axisPlacement === AxisPlacement.Bottom ? 1 : 2,
getDisplayValues: () => getDisplayValuesForCalcs(calcs, field, theme),
getItemKey: () => `${label}-${fieldIndex.frameIndex}-${fieldIndex.fieldIndex}`,
lineStyle: seriesConfig.lineStyle,
};
})
.filter((i): i is VizLegendItem => i !== undefined);
Expand Down

0 comments on commit 708bcda

Please sign in to comment.