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.", "0"],
|
||||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
[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": [
|
"packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[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.", "1"],
|
||||||
@ -949,7 +946,8 @@ exports[`better eslint`] = {
|
|||||||
],
|
],
|
||||||
"packages/grafana-ui/src/components/uPlot/utils.ts:5381": [
|
"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.", "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": [
|
"packages/grafana-ui/src/graveyard/Graph/GraphContextMenu.tsx:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
[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"]
|
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||||
],
|
],
|
||||||
"public/app/plugins/panel/xychart/XYChartPanel.tsx:5381": [
|
"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.", "0"]
|
||||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
|
||||||
],
|
],
|
||||||
"public/app/plugins/panel/xychart/scatter.ts:5381": [
|
"public/app/plugins/panel/xychart/scatter.ts:5381": [
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||||
|
@ -1,16 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import { DataFrame, getFieldDisplayName, getFieldSeriesColor } from '@grafana/data';
|
||||||
DataFrame,
|
|
||||||
DisplayProcessor,
|
|
||||||
DisplayValue,
|
|
||||||
fieldReducers,
|
|
||||||
getDisplayProcessor,
|
|
||||||
getFieldDisplayName,
|
|
||||||
getFieldSeriesColor,
|
|
||||||
reduceField,
|
|
||||||
ReducerID,
|
|
||||||
} from '@grafana/data';
|
|
||||||
import { VizLegendOptions, AxisPlacement } from '@grafana/schema';
|
import { VizLegendOptions, AxisPlacement } from '@grafana/schema';
|
||||||
|
|
||||||
import { useTheme2 } from '../../themes';
|
import { useTheme2 } from '../../themes';
|
||||||
@ -19,8 +9,7 @@ import { VizLegend } from '../VizLegend/VizLegend';
|
|||||||
import { VizLegendItem } from '../VizLegend/types';
|
import { VizLegendItem } from '../VizLegend/types';
|
||||||
|
|
||||||
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
|
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
|
||||||
|
import { getDisplayValuesForCalcs } from './utils';
|
||||||
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
|
|
||||||
|
|
||||||
interface PlotLegendProps extends VizLegendOptions, Omit<VizLayoutLegendProps, 'children'> {
|
interface PlotLegendProps extends VizLegendOptions, Omit<VizLayoutLegendProps, 'children'> {
|
||||||
data: DataFrame[];
|
data: DataFrame[];
|
||||||
@ -80,63 +69,7 @@ export const PlotLegend = React.memo(
|
|||||||
color: seriesColor,
|
color: seriesColor,
|
||||||
label,
|
label,
|
||||||
yAxis: axisPlacement === AxisPlacement.Left || axisPlacement === AxisPlacement.Bottom ? 1 : 2,
|
yAxis: axisPlacement === AxisPlacement.Left || axisPlacement === AxisPlacement.Bottom ? 1 : 2,
|
||||||
getDisplayValues: () => {
|
getDisplayValues: () => getDisplayValuesForCalcs(calcs, field, theme),
|
||||||
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,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getItemKey: () => `${label}-${fieldIndex.frameIndex}-${fieldIndex.fieldIndex}`,
|
getItemKey: () => `${label}-${fieldIndex.frameIndex}-${fieldIndex.fieldIndex}`,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
import uPlot, { AlignedData, Options, PaddingSide } from 'uplot';
|
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 { BarAlignment, GraphDrawStyle, GraphTransform, LineInterpolation, StackingMode } from '@grafana/schema';
|
||||||
|
|
||||||
import { attachDebugger } from '../../utils';
|
import { attachDebugger } from '../../utils';
|
||||||
@ -392,6 +404,65 @@ function hasNegSample(data: unknown[], samples = 100) {
|
|||||||
return false;
|
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
|
// Dev helpers
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -2,15 +2,7 @@ import { css } from '@emotion/css';
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { usePrevious } from 'react-use';
|
import { usePrevious } from 'react-use';
|
||||||
|
|
||||||
import {
|
import { PanelProps } from '@grafana/data';
|
||||||
DisplayProcessor,
|
|
||||||
DisplayValue,
|
|
||||||
fieldReducers,
|
|
||||||
PanelProps,
|
|
||||||
reduceField,
|
|
||||||
ReducerID,
|
|
||||||
getDisplayProcessor,
|
|
||||||
} from '@grafana/data';
|
|
||||||
import { alpha } from '@grafana/data/src/themes/colorManipulator';
|
import { alpha } from '@grafana/data/src/themes/colorManipulator';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
@ -18,12 +10,14 @@ import {
|
|||||||
TooltipPlugin2,
|
TooltipPlugin2,
|
||||||
UPlotChart,
|
UPlotChart,
|
||||||
UPlotConfigBuilder,
|
UPlotConfigBuilder,
|
||||||
|
useTheme2,
|
||||||
VizLayout,
|
VizLayout,
|
||||||
VizLegend,
|
VizLegend,
|
||||||
VizLegendItem,
|
VizLegendItem,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
|
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
|
||||||
import { FacetedData } from '@grafana/ui/src/components/uPlot/types';
|
import { FacetedData } from '@grafana/ui/src/components/uPlot/types';
|
||||||
|
import { getDisplayValuesForCalcs } from '@grafana/ui/src/components/uPlot/utils';
|
||||||
|
|
||||||
import { XYChartTooltip } from './XYChartTooltip';
|
import { XYChartTooltip } from './XYChartTooltip';
|
||||||
import { Options, SeriesMapping } from './panelcfg.gen';
|
import { Options, SeriesMapping } from './panelcfg.gen';
|
||||||
@ -33,6 +27,8 @@ import { ScatterSeries } from './types';
|
|||||||
type Props = PanelProps<Options>;
|
type Props = PanelProps<Options>;
|
||||||
|
|
||||||
export const XYChartPanel = (props: Props) => {
|
export const XYChartPanel = (props: Props) => {
|
||||||
|
const theme = useTheme2();
|
||||||
|
|
||||||
const [error, setError] = useState<string | undefined>();
|
const [error, setError] = useState<string | undefined>();
|
||||||
const [series, setSeries] = useState<ScatterSeries[]>([]);
|
const [series, setSeries] = useState<ScatterSeries[]>([]);
|
||||||
const [builder, setBuilder] = useState<UPlotConfigBuilder | undefined>();
|
const [builder, setBuilder] = useState<UPlotConfigBuilder | undefined>();
|
||||||
@ -70,76 +66,14 @@ export const XYChartPanel = (props: Props) => {
|
|||||||
|
|
||||||
const renderLegend = () => {
|
const renderLegend = () => {
|
||||||
const items: VizLegendItem[] = [];
|
const items: VizLegendItem[] = [];
|
||||||
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
|
|
||||||
const theme = config.theme2;
|
|
||||||
|
|
||||||
for (let si = 0; si < series.length; si++) {
|
for (let si = 0; si < series.length; si++) {
|
||||||
const s = series[si];
|
const s = series[si];
|
||||||
const frame = s.frame(props.data.series);
|
const frame = s.frame(props.data.series);
|
||||||
if (frame) {
|
if (frame) {
|
||||||
for (const item of s.legend()) {
|
for (const item of s.legend()) {
|
||||||
item.getDisplayValues = () => {
|
const field = s.y(frame);
|
||||||
const calcs = props.options.legend.calcs;
|
item.getDisplayValues = () => getDisplayValuesForCalcs(props.options.legend.calcs, field, theme);
|
||||||
|
|
||||||
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.disabled = !(s.show ?? true);
|
item.disabled = !(s.show ?? true);
|
||||||
|
|
||||||
if (props.options.seriesMapping === SeriesMapping.Manual) {
|
if (props.options.seriesMapping === SeriesMapping.Manual) {
|
||||||
|
@ -12,8 +12,10 @@ import {
|
|||||||
VizLegend,
|
VizLegend,
|
||||||
VizLegendItem,
|
VizLegendItem,
|
||||||
useStyles2,
|
useStyles2,
|
||||||
|
useTheme2,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
|
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
|
||||||
|
import { getDisplayValuesForCalcs } from '@grafana/ui/src/components/uPlot/utils';
|
||||||
|
|
||||||
import { XYChartTooltip } from './XYChartTooltip';
|
import { XYChartTooltip } from './XYChartTooltip';
|
||||||
import { Options } from './panelcfg.gen';
|
import { Options } from './panelcfg.gen';
|
||||||
@ -24,6 +26,7 @@ type Props2 = PanelProps<Options>;
|
|||||||
|
|
||||||
export const XYChartPanel2 = (props: Props2) => {
|
export const XYChartPanel2 = (props: Props2) => {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
const theme = useTheme2();
|
||||||
|
|
||||||
let { mapping, series: mappedSeries } = props.options;
|
let { mapping, series: mappedSeries } = props.options;
|
||||||
|
|
||||||
@ -72,17 +75,24 @@ export const XYChartPanel2 = (props: Props2) => {
|
|||||||
getItemKey: () => `${idx}-${s.name.value}`,
|
getItemKey: () => `${idx}-${s.name.value}`,
|
||||||
fieldName: yField.state?.displayName ?? yField.name,
|
fieldName: yField.state?.displayName ?? yField.name,
|
||||||
disabled: yField.state?.hideFrom?.viz ?? false,
|
disabled: yField.state?.hideFrom?.viz ?? false,
|
||||||
|
getDisplayValues: () => getDisplayValuesForCalcs(props.options.legend.calcs, yField, theme),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// sort series by calcs? table mode?
|
const { placement, displayMode, width, sortBy, sortDesc } = props.options.legend;
|
||||||
|
|
||||||
const { placement, displayMode, width } = props.options.legend;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VizLayout.Legend placement={placement} width={width}>
|
<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>
|
</VizLayout.Legend>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user