Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VizLegend: Represent line style in series legend and tooltip #87558

Merged
merged 8 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 35 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,47 @@ 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',
};
const lineStyleStyle =
lineStyle?.fill === 'dot' ? styles.dot : lineStyle?.fill === 'dash' ? styles.dash : styles.solid;

const colorStyle: CSSProperties =
lineStyle?.fill === 'dot' || lineStyle?.fill === 'dash'
? {
borderTopColor: cssColor,
}
: {
backgroundColor: cssColor,
};
leeoniya marked this conversation as resolved.
Show resolved Hide resolved

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

const getStyles = () => ({
const getStyles = (theme: GrafanaTheme2) => ({
container: css({
marginRight: '8px',
display: 'inline-block',
width: '14px',
}),
dot: css({
borderTop: 'dotted 3.99px',
domasx2 marked this conversation as resolved.
Show resolved Hide resolved
}),
dash: css({
borderTop: 'dashed 3px',
}),
solid: css({
height: '4px',
borderRadius: theme.shape.radius.pill,
display: 'inline-block',
}),
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 @@ -139,6 +137,7 @@ export const getContentItems = (
colorPlacement: ColorPlacement.first,
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