Commit 8c1a79f2 by Ryan McKinley Committed by GitHub

XY Chart: share legend config with timeseries (#30559)

parent e818bdd7
...@@ -259,6 +259,10 @@ export const GraphNG: React.FC<GraphNGProps> = ({ ...@@ -259,6 +259,10 @@ export const GraphNG: React.FC<GraphNGProps> = ({
label: seriesConfig.fieldName, label: seriesConfig.fieldName,
yAxis: axisPlacement === AxisPlacement.Left ? 1 : 2, yAxis: axisPlacement === AxisPlacement.Left ? 1 : 2,
getDisplayValues: () => { getDisplayValues: () => {
if (!legend.calcs?.length) {
return [];
}
const fmt = field.display ?? defaultFormatter; const fmt = field.display ?? defaultFormatter;
const fieldCalcs = reduceField({ const fieldCalcs = reduceField({
field, field,
......
...@@ -3,7 +3,10 @@ import { ...@@ -3,7 +3,10 @@ import {
FieldConfigProperty, FieldConfigProperty,
FieldType, FieldType,
identityOverrideProcessor, identityOverrideProcessor,
PanelOptionsEditorBuilder,
SetFieldConfigOptionsArgs, SetFieldConfigOptionsArgs,
standardEditorsRegistry,
StatsPickerConfigSettings,
stringOverrideProcessor, stringOverrideProcessor,
} from '@grafana/data'; } from '@grafana/data';
import { import {
...@@ -17,11 +20,13 @@ import { ...@@ -17,11 +20,13 @@ import {
ScaleDistribution, ScaleDistribution,
ScaleDistributionConfig, ScaleDistributionConfig,
GraphGradientMode, GraphGradientMode,
LegendDisplayMode,
} from '@grafana/ui'; } from '@grafana/ui';
import { SeriesConfigEditor } from './HideSeriesConfigEditor'; import { SeriesConfigEditor } from './HideSeriesConfigEditor';
import { ScaleDistributionEditor } from './ScaleDistributionEditor'; import { ScaleDistributionEditor } from './ScaleDistributionEditor';
import { LineStyleEditor } from './LineStyleEditor'; import { LineStyleEditor } from './LineStyleEditor';
import { FillBellowToEditor } from './FillBelowToEditor'; import { FillBellowToEditor } from './FillBelowToEditor';
import { OptionsWithLegend } from './types';
export const defaultGraphConfig: GraphFieldConfig = { export const defaultGraphConfig: GraphFieldConfig = {
drawStyle: DrawStyle.Line, drawStyle: DrawStyle.Line,
...@@ -224,3 +229,45 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption ...@@ -224,3 +229,45 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
}, },
}; };
} }
export function addLegendOptions(builder: PanelOptionsEditorBuilder<OptionsWithLegend>) {
builder
.addRadio({
path: 'legend.displayMode',
name: 'Legend mode',
description: '',
defaultValue: LegendDisplayMode.List,
settings: {
options: [
{ value: LegendDisplayMode.List, label: 'List' },
{ value: LegendDisplayMode.Table, label: 'Table' },
{ value: LegendDisplayMode.Hidden, label: 'Hidden' },
],
},
})
.addRadio({
path: 'legend.placement',
name: 'Legend placement',
description: '',
defaultValue: 'bottom',
settings: {
options: [
{ value: 'bottom', label: 'Bottom' },
{ value: 'right', label: 'Right' },
],
},
showIf: (c) => c.legend.displayMode !== LegendDisplayMode.Hidden,
})
.addCustomEditor<StatsPickerConfigSettings, string[]>({
id: 'legend.calcs',
path: 'legend.calcs',
name: 'Legend calculations',
description: 'Choose a reducer functions / calculations to include in legend',
editor: standardEditorsRegistry.get('stats-picker').editor as any,
defaultValue: [],
settings: {
allowMultiple: true,
},
showIf: (currentConfig) => currentConfig.legend.displayMode !== LegendDisplayMode.Hidden,
});
}
import { PanelPlugin, standardEditorsRegistry, StatsPickerConfigSettings } from '@grafana/data'; import { PanelPlugin } from '@grafana/data';
import { GraphFieldConfig, LegendDisplayMode } from '@grafana/ui'; import { GraphFieldConfig } from '@grafana/ui';
import { TimeSeriesPanel } from './TimeSeriesPanel'; import { TimeSeriesPanel } from './TimeSeriesPanel';
import { graphPanelChangedHandler } from './migrations'; import { graphPanelChangedHandler } from './migrations';
import { Options } from './types'; import { Options } from './types';
import { defaultGraphConfig, getGraphFieldConfig } from './config'; import { addLegendOptions, defaultGraphConfig, getGraphFieldConfig } from './config';
export const plugin = new PanelPlugin<Options, GraphFieldConfig>(TimeSeriesPanel) export const plugin = new PanelPlugin<Options, GraphFieldConfig>(TimeSeriesPanel)
.setPanelChangeHandler(graphPanelChangedHandler) .setPanelChangeHandler(graphPanelChangedHandler)
.useFieldConfig(getGraphFieldConfig(defaultGraphConfig)) .useFieldConfig(getGraphFieldConfig(defaultGraphConfig))
.setPanelOptions((builder) => { .setPanelOptions((builder) => {
builder builder.addRadio({
.addRadio({ path: 'tooltipOptions.mode',
path: 'tooltipOptions.mode', name: 'Tooltip mode',
name: 'Tooltip mode', description: '',
description: '', defaultValue: 'single',
defaultValue: 'single', settings: {
settings: { options: [
options: [ { value: 'single', label: 'Single' },
{ value: 'single', label: 'Single' }, { value: 'multi', label: 'All' },
{ value: 'multi', label: 'All' }, { value: 'none', label: 'Hidden' },
{ value: 'none', label: 'Hidden' }, ],
], },
}, });
}) addLegendOptions(builder);
.addRadio({
path: 'legend.displayMode',
name: 'Legend mode',
description: '',
defaultValue: LegendDisplayMode.List,
settings: {
options: [
{ value: LegendDisplayMode.List, label: 'List' },
{ value: LegendDisplayMode.Table, label: 'Table' },
{ value: LegendDisplayMode.Hidden, label: 'Hidden' },
],
},
})
.addRadio({
path: 'legend.placement',
name: 'Legend placement',
description: '',
defaultValue: 'bottom',
settings: {
options: [
{ value: 'bottom', label: 'Bottom' },
{ value: 'right', label: 'Right' },
],
},
showIf: (c) => c.legend.displayMode !== LegendDisplayMode.Hidden,
})
.addCustomEditor<StatsPickerConfigSettings, string[]>({
id: 'legend.calcs',
path: 'legend.calcs',
name: 'Legend calculations',
description: 'Choose a reducer functions / calculations to include in legend',
editor: standardEditorsRegistry.get('stats-picker').editor as any,
defaultValue: [],
settings: {
allowMultiple: true,
},
showIf: (currentConfig) => currentConfig.legend.displayMode !== LegendDisplayMode.Hidden,
});
}); });
import { VizLegendOptions, GraphTooltipOptions } from '@grafana/ui'; import { VizLegendOptions, GraphTooltipOptions } from '@grafana/ui';
export interface GraphOptions { export interface GraphOptions {
// Redraw as time passes // nothing for now
realTimeUpdates?: boolean;
} }
export interface Options { export interface OptionsWithLegend {
graph: GraphOptions;
legend: VizLegendOptions; legend: VizLegendOptions;
}
export interface Options extends OptionsWithLegend {
graph: GraphOptions;
tooltipOptions: GraphTooltipOptions; tooltipOptions: GraphTooltipOptions;
} }
import React, { FC, useCallback, useMemo } from 'react'; import React, { FC, useMemo } from 'react';
import { css } from 'emotion'; import { css } from 'emotion';
import { IconButton, Label, Select, stylesFactory, Switch, useTheme } from '@grafana/ui'; import { IconButton, Label, Select, stylesFactory, useTheme } from '@grafana/ui';
import { import {
SelectableValue, SelectableValue,
getFrameDisplayName, getFrameDisplayName,
...@@ -84,13 +84,6 @@ export const XYDimsEditor: FC<StandardEditorProps<XYDimensionConfig, any, Option ...@@ -84,13 +84,6 @@ export const XYDimsEditor: FC<StandardEditorProps<XYDimensionConfig, any, Option
return v; return v;
}, [dims, context.data, value]); }, [dims, context.data, value]);
const toggleSort = useCallback(() => {
onChange({
...value,
sort: !value?.sort,
});
}, [value, onChange]);
const theme = useTheme(); const theme = useTheme();
const styles = getStyles(theme); const styles = getStyles(theme);
...@@ -118,10 +111,6 @@ export const XYDimsEditor: FC<StandardEditorProps<XYDimensionConfig, any, Option ...@@ -118,10 +111,6 @@ export const XYDimsEditor: FC<StandardEditorProps<XYDimensionConfig, any, Option
}); });
}} }}
/> />
<div className={styles.sorter}>
<Switch value={value?.sort ?? false} onClick={toggleSort} />
<div onClick={toggleSort}>&nbsp; Sort field</div>
</div>
<br /> <br />
<Label>Y Fields</Label> <Label>Y Fields</Label>
<div> <div>
......
import { DataFrame, Field, FieldMatcher, FieldType, getFieldDisplayName, sortDataFrame } from '@grafana/data'; import { DataFrame, Field, FieldMatcher, FieldType, getFieldDisplayName } from '@grafana/data';
import { XYFieldMatchers } from '@grafana/ui/src/components/GraphNG/GraphNG'; import { XYFieldMatchers } from '@grafana/ui/src/components/GraphNG/GraphNG';
import { XYDimensionConfig } from './types'; import { XYDimensionConfig } from './types';
...@@ -49,11 +49,6 @@ export function getXYDimensions(cfg: XYDimensionConfig, data?: DataFrame[]): XYD ...@@ -49,11 +49,6 @@ export function getXYDimensions(cfg: XYDimensionConfig, data?: DataFrame[]): XYD
} }
} }
// Optionally sort
if (cfg.sort) {
frame = sortDataFrame(frame, xIndex);
}
let hasTime = false; let hasTime = false;
const x = frame.fields[xIndex]; const x = frame.fields[xIndex];
const fields: Field[] = [x]; const fields: Field[] = [x];
...@@ -92,12 +87,14 @@ function getSimpleFieldMatcher(f: Field): FieldMatcher { ...@@ -92,12 +87,14 @@ function getSimpleFieldMatcher(f: Field): FieldMatcher {
if (!f) { if (!f) {
return () => false; return () => false;
} }
return (field) => f === field; // the field may change if sorted
return (field) => f === field || !!(f.state && f.state === field.state);
} }
function getSimpleFieldNotMatcher(f: Field): FieldMatcher { function getSimpleFieldNotMatcher(f: Field): FieldMatcher {
if (!f) { if (!f) {
return () => false; return () => false;
} }
return (field) => f !== field; const m = getSimpleFieldMatcher(f);
return (field) => !m(field, undefined as any, undefined as any);
} }
import { PanelPlugin } from '@grafana/data'; import { PanelPlugin } from '@grafana/data';
import { DrawStyle, GraphFieldConfig, LegendDisplayMode } from '@grafana/ui'; import { DrawStyle, GraphFieldConfig } from '@grafana/ui';
import { XYChartPanel } from './XYChartPanel'; import { XYChartPanel } from './XYChartPanel';
import { Options } from './types'; import { Options } from './types';
import { XYDimsEditor } from './XYDimsEditor'; import { XYDimsEditor } from './XYDimsEditor';
import { getGraphFieldConfig, defaultGraphConfig } from '../timeseries/config'; import { getGraphFieldConfig, defaultGraphConfig, addLegendOptions } from '../timeseries/config';
export const plugin = new PanelPlugin<Options, GraphFieldConfig>(XYChartPanel) export const plugin = new PanelPlugin<Options, GraphFieldConfig>(XYChartPanel)
.useFieldConfig( .useFieldConfig(
...@@ -32,31 +32,7 @@ export const plugin = new PanelPlugin<Options, GraphFieldConfig>(XYChartPanel) ...@@ -32,31 +32,7 @@ export const plugin = new PanelPlugin<Options, GraphFieldConfig>(XYChartPanel)
{ value: 'none', label: 'Hidden' }, { value: 'none', label: 'Hidden' },
], ],
}, },
})
.addRadio({
path: 'legend.displayMode',
name: 'Legend mode',
description: '',
defaultValue: LegendDisplayMode.List,
settings: {
options: [
{ value: LegendDisplayMode.List, label: 'List' },
{ value: LegendDisplayMode.Table, label: 'Table' },
{ value: LegendDisplayMode.Hidden, label: 'Hidden' },
],
},
})
.addRadio({
path: 'legend.placement',
name: 'Legend placement',
description: '',
defaultValue: 'bottom',
settings: {
options: [
{ value: 'bottom', label: 'Bottom' },
{ value: 'right', label: 'Right' },
],
},
showIf: (c) => c.legend.displayMode !== LegendDisplayMode.Hidden,
}); });
addLegendOptions(builder);
}); });
import { VizLegendOptions, GraphTooltipOptions } from '@grafana/ui'; import { GraphTooltipOptions } from '@grafana/ui';
import { OptionsWithLegend } from '../timeseries/types';
export interface XYDimensionConfig { export interface XYDimensionConfig {
frame: number; frame: number;
sort?: boolean;
x?: string; // name | first x?: string; // name | first
exclude?: string[]; // all other numbers except exclude?: string[]; // all other numbers except
} }
export interface Options { export interface Options extends OptionsWithLegend {
dims: XYDimensionConfig; dims: XYDimensionConfig;
legend: VizLegendOptions;
tooltipOptions: GraphTooltipOptions; tooltipOptions: GraphTooltipOptions;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment