XyChart: Fix legend (#87155)

This commit is contained in:
Adela Almasan 2024-05-03 16:06:05 -06:00 committed by GitHub
parent 68fa2110d6
commit 8a96fcedb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 99 additions and 154 deletions

View File

@ -930,9 +930,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
],
"packages/grafana-ui/src/components/uPlot/PlotLegend.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
@ -949,7 +946,8 @@ exports[`better eslint`] = {
],
"packages/grafana-ui/src/components/uPlot/utils.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
"packages/grafana-ui/src/graveyard/Graph/GraphContextMenu.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@ -5792,8 +5790,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/panel/xychart/XYChartPanel.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/panel/xychart/scatter.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],

View File

@ -1,16 +1,6 @@
import React from 'react';
import {
DataFrame,
DisplayProcessor,
DisplayValue,
fieldReducers,
getDisplayProcessor,
getFieldDisplayName,
getFieldSeriesColor,
reduceField,
ReducerID,
} from '@grafana/data';
import { DataFrame, getFieldDisplayName, getFieldSeriesColor } from '@grafana/data';
import { VizLegendOptions, AxisPlacement } from '@grafana/schema';
import { useTheme2 } from '../../themes';
@ -19,8 +9,7 @@ import { VizLegend } from '../VizLegend/VizLegend';
import { VizLegendItem } from '../VizLegend/types';
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
import { getDisplayValuesForCalcs } from './utils';
interface PlotLegendProps extends VizLegendOptions, Omit<VizLayoutLegendProps, 'children'> {
data: DataFrame[];
@ -80,63 +69,7 @@ export const PlotLegend = React.memo(
color: seriesColor,
label,
yAxis: axisPlacement === AxisPlacement.Left || axisPlacement === AxisPlacement.Bottom ? 1 : 2,
getDisplayValues: () => {
if (!calcs?.length) {
return [];
}
const fmt = field.display ?? defaultFormatter;
let countFormatter: DisplayProcessor | null = null;
const fieldCalcs = reduceField({
field,
reducers: calcs,
});
return calcs.map<DisplayValue>((reducerId) => {
const fieldReducer = fieldReducers.get(reducerId);
let formatter = fmt;
if (fieldReducer.id === ReducerID.diffperc) {
formatter = getDisplayProcessor({
field: {
...field,
config: {
...field.config,
unit: 'percentunit',
},
},
theme,
});
}
if (
fieldReducer.id === ReducerID.count ||
fieldReducer.id === ReducerID.changeCount ||
fieldReducer.id === ReducerID.distinctCount
) {
if (!countFormatter) {
countFormatter = getDisplayProcessor({
field: {
...field,
config: {
...field.config,
unit: 'none',
},
},
theme,
});
}
formatter = countFormatter;
}
return {
...formatter(fieldCalcs[reducerId]),
title: fieldReducer.name,
description: fieldReducer.description,
};
});
},
getDisplayValues: () => getDisplayValuesForCalcs(calcs, field, theme),
getItemKey: () => `${label}-${fieldIndex.frameIndex}-${fieldIndex.fieldIndex}`,
};
})

View File

@ -1,6 +1,18 @@
import uPlot, { AlignedData, Options, PaddingSide } from 'uplot';
import { DataFrame, ensureTimeField, FieldType } from '@grafana/data';
import {
DataFrame,
DisplayProcessor,
DisplayValue,
ensureTimeField,
Field,
fieldReducers,
FieldType,
getDisplayProcessor,
GrafanaTheme2,
reduceField,
ReducerID,
} from '@grafana/data';
import { BarAlignment, GraphDrawStyle, GraphTransform, LineInterpolation, StackingMode } from '@grafana/schema';
import { attachDebugger } from '../../utils';
@ -392,6 +404,65 @@ function hasNegSample(data: unknown[], samples = 100) {
return false;
}
export const getDisplayValuesForCalcs = (calcs: string[], field: Field, theme: GrafanaTheme2) => {
if (!calcs?.length) {
return [];
}
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
const fmt = field.display ?? defaultFormatter;
let countFormatter: DisplayProcessor | null = null;
const fieldCalcs = reduceField({
field: field,
reducers: calcs,
});
return calcs.map<DisplayValue>((reducerId) => {
const fieldReducer = fieldReducers.get(reducerId);
let formatter = fmt;
if (fieldReducer.id === ReducerID.diffperc) {
formatter = getDisplayProcessor({
field: {
...field,
config: {
...field.config,
unit: 'percent',
},
},
theme,
});
}
if (
fieldReducer.id === ReducerID.count ||
fieldReducer.id === ReducerID.changeCount ||
fieldReducer.id === ReducerID.distinctCount
) {
if (!countFormatter) {
countFormatter = getDisplayProcessor({
field: {
...field,
config: {
...field.config,
unit: 'none',
},
},
theme,
});
}
formatter = countFormatter;
}
return {
...formatter(fieldCalcs[reducerId]),
title: fieldReducer.name,
description: fieldReducer.description,
};
});
};
// Dev helpers
/** @internal */

View File

@ -2,15 +2,7 @@ import { css } from '@emotion/css';
import React, { useState, useEffect, useCallback } from 'react';
import { usePrevious } from 'react-use';
import {
DisplayProcessor,
DisplayValue,
fieldReducers,
PanelProps,
reduceField,
ReducerID,
getDisplayProcessor,
} from '@grafana/data';
import { PanelProps } from '@grafana/data';
import { alpha } from '@grafana/data/src/themes/colorManipulator';
import { config } from '@grafana/runtime';
import {
@ -18,12 +10,14 @@ import {
TooltipPlugin2,
UPlotChart,
UPlotConfigBuilder,
useTheme2,
VizLayout,
VizLegend,
VizLegendItem,
} from '@grafana/ui';
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
import { FacetedData } from '@grafana/ui/src/components/uPlot/types';
import { getDisplayValuesForCalcs } from '@grafana/ui/src/components/uPlot/utils';
import { XYChartTooltip } from './XYChartTooltip';
import { Options, SeriesMapping } from './panelcfg.gen';
@ -33,6 +27,8 @@ import { ScatterSeries } from './types';
type Props = PanelProps<Options>;
export const XYChartPanel = (props: Props) => {
const theme = useTheme2();
const [error, setError] = useState<string | undefined>();
const [series, setSeries] = useState<ScatterSeries[]>([]);
const [builder, setBuilder] = useState<UPlotConfigBuilder | undefined>();
@ -70,76 +66,14 @@ export const XYChartPanel = (props: Props) => {
const renderLegend = () => {
const items: VizLegendItem[] = [];
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
const theme = config.theme2;
for (let si = 0; si < series.length; si++) {
const s = series[si];
const frame = s.frame(props.data.series);
if (frame) {
for (const item of s.legend()) {
item.getDisplayValues = () => {
const calcs = props.options.legend.calcs;
if (!calcs?.length) {
return [];
}
const field = s.y(frame);
const fmt = field.display ?? defaultFormatter;
let countFormatter: DisplayProcessor | null = null;
const fieldCalcs = reduceField({
field,
reducers: calcs,
});
return calcs.map<DisplayValue>((reducerId) => {
const fieldReducer = fieldReducers.get(reducerId);
let formatter = fmt;
if (fieldReducer.id === ReducerID.diffperc) {
formatter = getDisplayProcessor({
field: {
...field,
config: {
...field.config,
unit: 'percent',
},
},
theme,
});
}
if (
fieldReducer.id === ReducerID.count ||
fieldReducer.id === ReducerID.changeCount ||
fieldReducer.id === ReducerID.distinctCount
) {
if (!countFormatter) {
countFormatter = getDisplayProcessor({
field: {
...field,
config: {
...field.config,
unit: 'none',
},
},
theme,
});
}
formatter = countFormatter;
}
return {
...formatter(fieldCalcs[reducerId]),
title: fieldReducer.name,
description: fieldReducer.description,
};
});
};
item.getDisplayValues = () => getDisplayValuesForCalcs(props.options.legend.calcs, field, theme);
item.disabled = !(s.show ?? true);
if (props.options.seriesMapping === SeriesMapping.Manual) {

View File

@ -12,8 +12,10 @@ import {
VizLegend,
VizLegendItem,
useStyles2,
useTheme2,
} from '@grafana/ui';
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
import { getDisplayValuesForCalcs } from '@grafana/ui/src/components/uPlot/utils';
import { XYChartTooltip } from './XYChartTooltip';
import { Options } from './panelcfg.gen';
@ -24,6 +26,7 @@ type Props2 = PanelProps<Options>;
export const XYChartPanel2 = (props: Props2) => {
const styles = useStyles2(getStyles);
const theme = useTheme2();
let { mapping, series: mappedSeries } = props.options;
@ -72,17 +75,24 @@ export const XYChartPanel2 = (props: Props2) => {
getItemKey: () => `${idx}-${s.name.value}`,
fieldName: yField.state?.displayName ?? yField.name,
disabled: yField.state?.hideFrom?.viz ?? false,
getDisplayValues: () => getDisplayValuesForCalcs(props.options.legend.calcs, yField, theme),
});
}
});
// sort series by calcs? table mode?
const { placement, displayMode, width } = props.options.legend;
const { placement, displayMode, width, sortBy, sortDesc } = props.options.legend;
return (
<VizLayout.Legend placement={placement} width={width}>
<VizLegend className={styles.legend} placement={placement} items={items} displayMode={displayMode} />
<VizLegend
className={styles.legend}
placement={placement}
items={items}
displayMode={displayMode}
sortBy={sortBy}
sortDesc={sortDesc}
isSortable={true}
/>
</VizLayout.Legend>
);
};