mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
XyChart: Fix legend (#87155)
This commit is contained in:
parent
68fa2110d6
commit
8a96fcedb2
@ -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"],
|
||||
|
@ -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}`,
|
||||
};
|
||||
})
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const field = s.y(frame);
|
||||
item.getDisplayValues = () => getDisplayValuesForCalcs(props.options.legend.calcs, field, theme);
|
||||
item.disabled = !(s.show ?? true);
|
||||
|
||||
if (props.options.seriesMapping === SeriesMapping.Manual) {
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user