diff --git a/packages/grafana-data/src/field/overrides/processors.ts b/packages/grafana-data/src/field/overrides/processors.ts index feff3993a09..b620471163a 100644 --- a/packages/grafana-data/src/field/overrides/processors.ts +++ b/packages/grafana-data/src/field/overrides/processors.ts @@ -132,3 +132,14 @@ export interface FieldColorConfigSettings { */ preferThresholdsMode?: boolean; } + +export interface StatsPickerConfigSettings { + /** + * Enable multi-selection in the stats picker + */ + allowMultiple: boolean; + /** + * Default stats to be use in the stats picker + */ + defaultStat?: string; +} diff --git a/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx b/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx index e7051af3079..30514107fab 100644 --- a/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx +++ b/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx @@ -81,7 +81,7 @@ export const GraphWithLegend: React.FunctionComponent = (p color: s.color || '', disabled: !s.isVisible, yAxis: s.yAxis.index, - displayValues: s.info || [], + getDisplayValues: () => s.info || [], }, ]); }, []); diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx index 65b72fed0ac..d1b1462380d 100644 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx @@ -63,8 +63,8 @@ export const Lines: React.FC = () => { to: dateTime(1546380000000), }, }} - legend={{ displayMode: LegendDisplayMode.List, placement: legendPlacement }} + legend={{ displayMode: LegendDisplayMode.List, placement: legendPlacement, calcs: [] }} timeZone="browser" - > + /> ); }; diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx index d995c5a4dbd..ebace9dae6a 100755 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx @@ -2,19 +2,22 @@ import React, { useCallback, useLayoutEffect, useMemo, useRef } from 'react'; import { compareDataFrameStructures, DataFrame, + DisplayValue, FieldConfig, FieldMatcher, + fieldReducers, FieldType, formattedValueToString, getFieldColorModeForField, getFieldDisplayName, + reduceField, TimeRange, } from '@grafana/data'; import { alignDataFrames } from './utils'; +import { useTheme } from '../../themes'; import { UPlotChart } from '../uPlot/Plot'; import { PlotProps } from '../uPlot/types'; import { AxisPlacement, DrawStyle, GraphFieldConfig, PointVisibility } from '../uPlot/config'; -import { useTheme } from '../../themes'; import { VizLayout } from '../VizLayout/VizLayout'; import { LegendDisplayMode, VizLegendItem, VizLegendOptions } from '../VizLegend/types'; import { VizLegend } from '../VizLegend/VizLegend'; @@ -30,7 +33,7 @@ export interface XYFieldMatchers { } export interface GraphNGProps extends Omit { data: DataFrame[]; - legend?: VizLegendOptions; + legend: VizLegendOptions; fields?: XYFieldMatchers; // default will assume timeseries data onLegendClick?: (event: GraphNGLegendEvent) => void; onSeriesColorChange?: (label: string, color: string) => void; @@ -55,10 +58,10 @@ export const GraphNG: React.FC = ({ onSeriesColorChange, ...plotProps }) => { - const alignedFrameWithGapTest = useMemo(() => alignDataFrames(data, fields), [data, fields]); const theme = useTheme(); - const legendItemsRef = useRef([]); const hasLegend = useRef(legend && legend.displayMode !== LegendDisplayMode.Hidden); + + const alignedFrameWithGapTest = useMemo(() => alignDataFrames(data, fields), [data, fields]); const alignedFrame = alignedFrameWithGapTest?.frame; const getDataFrameFieldIndex = alignedFrameWithGapTest?.getDataFrameFieldIndex; @@ -87,6 +90,7 @@ export const GraphNG: React.FC = ({ // reference change will not triger re-render const currentTimeRange = useRef(timeRange); + useLayoutEffect(() => { currentTimeRange.current = timeRange; }, [timeRange]); @@ -130,8 +134,6 @@ export const GraphNG: React.FC = ({ }); } - const legendItems: VizLegendItem[] = []; - for (let i = 0; i < alignedFrame.fields.length; i++) { const field = alignedFrame.fields[i]; const config = field.config as FieldConfig; @@ -170,6 +172,7 @@ export const GraphNG: React.FC = ({ } const showPoints = customConfig.drawStyle === DrawStyle.Points ? PointVisibility.Always : customConfig.showPoints; + const dataFrameFieldIndex = getDataFrameFieldIndex ? getDataFrameFieldIndex(i) : undefined; builder.addSeries({ scaleKey, @@ -185,24 +188,13 @@ export const GraphNG: React.FC = ({ spanNulls: customConfig.spanNulls || false, show: !customConfig.hideFrom?.graph, fillGradient: customConfig.fillGradient, + + // The following properties are not used in the uPlot config, but are utilized as transport for legend config + dataFrameFieldIndex, + fieldName: getFieldDisplayName(field, alignedFrame), + hideInLegend: customConfig.hideFrom?.legend, }); - - if (hasLegend.current && !customConfig.hideFrom?.legend) { - const axisPlacement = builder.getAxisPlacement(scaleKey); - // we need to add this as dep or move it to be done outside. - const dataFrameFieldIndex = getDataFrameFieldIndex ? getDataFrameFieldIndex(i) : undefined; - - legendItems.push({ - disabled: field.config.custom?.hideFrom?.graph ?? false, - fieldIndex: dataFrameFieldIndex, - color: seriesColor, - label: getFieldDisplayName(field, alignedFrame), - yAxis: axisPlacement === AxisPlacement.Left ? 1 : 2, - }); - } } - - legendItemsRef.current = legendItems; return builder; }, [configRev, timeZone]); @@ -214,16 +206,58 @@ export const GraphNG: React.FC = ({ ); } + const legendItems = configBuilder + .getSeries() + .map(s => { + const seriesConfig = s.props; + const fieldIndex = seriesConfig.dataFrameFieldIndex; + const axisPlacement = configBuilder.getAxisPlacement(s.props.scaleKey); + + if (seriesConfig.hideInLegend || !fieldIndex) { + return undefined; + } + + const field = data[fieldIndex.frameIndex]?.fields[fieldIndex.fieldIndex]; + + // Hackish: when the data prop and config builder are not in sync yet + if (!field) { + return undefined; + } + + return { + disabled: !seriesConfig.show ?? false, + fieldIndex, + color: seriesConfig.lineColor!, + label: seriesConfig.fieldName, + yAxis: axisPlacement === AxisPlacement.Left ? 1 : 2, + getDisplayValues: () => { + const fmt = field.display ?? defaultFormatter; + const fieldCalcs = reduceField({ + field, + reducers: legend.calcs, + }); + + return legend.calcs.map(reducer => { + return { + ...fmt(fieldCalcs[reducer]), + title: fieldReducers.get(reducer).name, + }; + }); + }, + }; + }) + .filter(i => i !== undefined) as VizLegendItem[]; + let legendElement: React.ReactElement | undefined; - if (hasLegend && legendItemsRef.current.length > 0) { + if (hasLegend && legendItems.length > 0) { legendElement = ( - + diff --git a/packages/grafana-ui/src/components/OptionsUI/stats.tsx b/packages/grafana-ui/src/components/OptionsUI/stats.tsx index 569e054afc0..453da4dc12e 100644 --- a/packages/grafana-ui/src/components/OptionsUI/stats.tsx +++ b/packages/grafana-ui/src/components/OptionsUI/stats.tsx @@ -1,7 +1,18 @@ import React from 'react'; -import { FieldConfigEditorProps, ReducerID } from '@grafana/data'; +import { FieldConfigEditorProps, StatsPickerConfigSettings } from '@grafana/data'; import { StatsPicker } from '../StatsPicker/StatsPicker'; -export const StatsPickerEditor: React.FC> = ({ value, onChange }) => { - return ; +export const StatsPickerEditor: React.FC> = ({ + value, + onChange, + item, +}) => { + return ( + + ); }; diff --git a/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx b/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx index 64eeb917c54..9cba4f1e366 100755 --- a/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx +++ b/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx @@ -8,6 +8,7 @@ import { FieldType, getFieldColorModeForField, FieldConfig, + getFieldDisplayName, } from '@grafana/data'; import { AxisPlacement, DrawStyle, GraphFieldConfig, PointVisibility } from '../uPlot/config'; import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder'; @@ -136,6 +137,7 @@ export class Sparkline extends PureComponent { builder.addSeries({ scaleKey, + fieldName: getFieldDisplayName(field, data), drawStyle: customConfig.drawStyle!, lineColor: customConfig.lineColor ?? seriesColor, lineWidth: customConfig.lineWidth, diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx index ef82fcd26bc..4c65d1e33c0 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx @@ -40,7 +40,7 @@ export const VizLegendListItem: React.FunctionComponent = ({ item, onSeri {item.label} - {item.displayValues && } + {item.getDisplayValues && } ); }; diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx index 7f92396fd76..c2474113e51 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx @@ -22,8 +22,8 @@ export const VizLegendTable: FC = ({ const columns = items .map(item => { - if (item.displayValues) { - return item.displayValues.map(i => i.title); + if (item.getDisplayValues) { + return item.getDisplayValues().map(i => i.title); } return []; }) @@ -39,8 +39,8 @@ export const VizLegendTable: FC = ({ const sortedItems = sortKey ? sortBy(items, item => { - if (item.displayValues) { - const stat = item.displayValues.filter(stat => stat.title === sortKey)[0]; + if (item.getDisplayValues) { + const stat = item.getDisplayValues().filter(stat => stat.title === sortKey)[0]; return stat && stat.numeric; } return undefined; @@ -67,7 +67,7 @@ export const VizLegendTable: FC = ({ return ( { if (onToggleSort) { onToggleSort(columnHeader); @@ -99,6 +99,8 @@ const getStyles = (theme: GrafanaTheme) => ({ border-bottom: 1px solid ${theme.colors.border1}; padding: ${theme.spacing.xxs} ${theme.spacing.sm}; text-align: right; + `, + headerSortable: css` cursor: pointer; `, sortIcon: css` diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx index 52e31ab81f4..24b5dbd41ef 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx @@ -49,8 +49,8 @@ export const LegendTableItem: React.FunctionComponent = ({ - {item.displayValues && - item.displayValues.map((stat, index) => { + {item.getDisplayValues && + item.getDisplayValues().map((stat, index) => { return ( {formattedValueToString(stat)} diff --git a/packages/grafana-ui/src/components/VizLegend/types.ts b/packages/grafana-ui/src/components/VizLegend/types.ts index 25ca758fb46..a078b0c8236 100644 --- a/packages/grafana-ui/src/components/VizLegend/types.ts +++ b/packages/grafana-ui/src/components/VizLegend/types.ts @@ -24,7 +24,8 @@ export interface VizLegendItem { color: string; yAxis: number; disabled?: boolean; - displayValues?: DisplayValue[]; + // displayValues?: DisplayValue[]; + getDisplayValues?: () => DisplayValue[]; fieldIndex?: DataFrameFieldIndex; } @@ -39,6 +40,7 @@ export type LegendPlacement = 'bottom' | 'right'; export interface VizLegendOptions { displayMode: LegendDisplayMode; placement: LegendPlacement; + calcs: string[]; } export type SeriesOptionChangeHandler = (label: string, option: TOption) => void; diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts index cc9da9bb155..d09ef0e5a81 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts @@ -325,6 +325,7 @@ describe('UPlotConfigBuilder', () => { builder.addSeries({ drawStyle: DrawStyle.Line, scaleKey: 'scale-x', + fieldName: 'A-series', lineColor: '#0000ff', }); @@ -336,6 +337,7 @@ describe('UPlotConfigBuilder', () => { builder.addSeries({ drawStyle: DrawStyle.Line, scaleKey: 'scale-x', + fieldName: 'A-series', lineColor: '#FFAABB', fillOpacity: 50, }); @@ -348,6 +350,7 @@ describe('UPlotConfigBuilder', () => { builder.addSeries({ drawStyle: DrawStyle.Line, scaleKey: 'scale-x', + fieldName: 'A-series', lineColor: '#FFAABB', fillOpacity: 50, fillColor: '#FF0000', @@ -361,6 +364,7 @@ describe('UPlotConfigBuilder', () => { builder.addSeries({ drawStyle: DrawStyle.Line, scaleKey: 'scale-x', + fieldName: 'A-series', lineColor: '#FFAABB', fillOpacity: 50, fillGradient: FillGradientMode.Opacity, @@ -374,6 +378,7 @@ describe('UPlotConfigBuilder', () => { builder.addSeries({ drawStyle: DrawStyle.Line, scaleKey: 'scale-x', + fieldName: 'A-series', fillOpacity: 50, fillGradient: FillGradientMode.Opacity, showPoints: PointVisibility.Auto, diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts index 9815d115ff4..a3fb4ccf8b9 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts @@ -57,6 +57,10 @@ export class UPlotConfigBuilder { this.series.push(new UPlotSeriesBuilder(props)); } + getSeries() { + return this.series; + } + /** Add or update the scale with the scale key */ addScale(props: ScaleProps) { const current = this.scales.find(v => v.props.scaleKey === props.scaleKey); diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts index 5636d6c78b5..9da09029701 100755 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts @@ -11,11 +11,15 @@ import { FillGradientMode, } from '../config'; import { PlotConfigBuilder } from '../types'; +import { DataFrameFieldIndex } from '@grafana/data'; export interface SeriesProps extends LineConfig, FillConfig, PointsConfig { - drawStyle: DrawStyle; scaleKey: string; + fieldName: string; + drawStyle: DrawStyle; show?: boolean; + dataFrameFieldIndex?: DataFrameFieldIndex; + hideInLegend?: boolean; } export class UPlotSeriesBuilder extends PlotConfigBuilder { diff --git a/packages/grafana-ui/src/utils/standardEditors.tsx b/packages/grafana-ui/src/utils/standardEditors.tsx index e22df9c2972..b44e25bbb8e 100644 --- a/packages/grafana-ui/src/utils/standardEditors.tsx +++ b/packages/grafana-ui/src/utils/standardEditors.tsx @@ -21,6 +21,7 @@ import { TimeZone, FieldColor, FieldColorConfigSettings, + StatsPickerConfigSettings, } from '@grafana/data'; import { Switch } from '../components/Switch/Switch'; @@ -323,7 +324,7 @@ export const getStandardOptionEditors = () => { editor: DataLinksValueEditor as any, }; - const statsPicker: StandardEditorsRegistryItem = { + const statsPicker: StandardEditorsRegistryItem = { id: 'stats-picker', name: 'Stats Picker', editor: StatsPickerEditor as any, diff --git a/public/app/features/sandbox/TestStuffPage.tsx b/public/app/features/sandbox/TestStuffPage.tsx index ecb8184013b..9a536856703 100644 --- a/public/app/features/sandbox/TestStuffPage.tsx +++ b/public/app/features/sandbox/TestStuffPage.tsx @@ -1,5 +1,5 @@ import { ApplyFieldOverrideOptions, DataTransformerConfig, dateMath, FieldColorModeId, PanelData } from '@grafana/data'; -import { GraphNG, Table } from '@grafana/ui'; +import { GraphNG, LegendDisplayMode, Table } from '@grafana/ui'; import { config } from 'app/core/config'; import React, { FC, useMemo, useState } from 'react'; import { useObservable } from 'react-use'; @@ -54,7 +54,14 @@ export const TestStuffPage: FC = () => { {data && (
- +
diff --git a/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap b/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap index 9f5ac96da84..2fa2dfa64b6 100644 --- a/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap +++ b/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap @@ -1,5 +1,60 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Graph Migrations legend 1`] = ` +Object { + "fieldConfig": Object { + "defaults": Object { + "custom": Object { + "axisPlacement": "auto", + "drawStyle": "line", + "fillOpacity": 50, + "lineInterpolation": "stepAfter", + "lineWidth": 5, + "pointSize": 6, + "showPoints": "never", + "spanNulls": true, + }, + "nullValueMode": "null", + "unit": "short", + }, + "overrides": Array [ + Object { + "matcher": Object { + "id": "byName", + "options": "A-series", + }, + "properties": Array [ + Object { + "id": "color", + "value": Object { + "fixedColor": "red", + "mode": "fixed", + }, + }, + ], + }, + ], + }, + "options": Object { + "graph": Object {}, + "legend": Object { + "calcs": Array [ + "mean", + "lastNotNull", + "max", + "min", + "sum", + ], + "displayMode": "table", + "placement": "bottom", + }, + "tooltipOptions": Object { + "mode": "single", + }, + }, +} +`; + exports[`Graph Migrations simple bars 1`] = ` Object { "fieldConfig": Object { @@ -16,6 +71,7 @@ Object { "options": Object { "graph": Object {}, "legend": Object { + "calcs": Array [], "displayMode": "list", "placement": "bottom", }, @@ -50,7 +106,14 @@ Object { "options": Object { "graph": Object {}, "legend": Object { - "displayMode": "list", + "calcs": Array [ + "mean", + "lastNotNull", + "max", + "min", + "sum", + ], + "displayMode": "table", "placement": "bottom", }, "tooltipOptions": Object { @@ -98,6 +161,7 @@ Object { "options": Object { "graph": Object {}, "legend": Object { + "calcs": Array [], "displayMode": "list", "placement": "bottom", }, @@ -179,6 +243,7 @@ Object { "options": Object { "graph": Object {}, "legend": Object { + "calcs": Array [], "displayMode": "list", "placement": "bottom", }, diff --git a/public/app/plugins/panel/timeseries/migrations.test.ts b/public/app/plugins/panel/timeseries/migrations.test.ts index 8d849895673..6fbd9d26248 100644 --- a/public/app/plugins/panel/timeseries/migrations.test.ts +++ b/public/app/plugins/panel/timeseries/migrations.test.ts @@ -39,6 +39,15 @@ describe('Graph Migrations', () => { panel.options = graphPanelChangedHandler(panel, 'graph', old); expect(panel).toMatchSnapshot(); }); + + it('legend', () => { + const old: any = { + angular: legend, + }; + const panel = {} as PanelModel; + panel.options = graphPanelChangedHandler(panel, 'graph', old); + expect(panel).toMatchSnapshot(); + }); }); const stairscase = { @@ -306,3 +315,97 @@ const stepedColordLine = { timeShift: null, datasource: null, }; + +const legend = { + aliasColors: { + 'A-series': 'red', + }, + dashLength: 10, + fieldConfig: { + defaults: { + custom: {}, + }, + overrides: [], + }, + fill: 5, + gridPos: { + h: 9, + w: 12, + x: 0, + y: 0, + }, + id: 2, + legend: { + avg: true, + current: true, + max: false, + min: false, + show: true, + total: true, + values: true, + alignAsTable: true, + }, + lines: true, + linewidth: 5, + maxDataPoints: 20, + nullPointMode: 'null', + options: { + alertThreshold: true, + }, + pluginVersion: '7.4.0-pre', + pointradius: 2, + renderer: 'flot', + seriesOverrides: [], + spaceLength: 10, + steppedLine: true, + thresholds: [], + timeRegions: [], + title: 'Panel Title', + tooltip: { + shared: true, + sort: 0, + value_type: 'individual', + }, + type: 'graph', + xaxis: { + buckets: null, + mode: 'time', + name: null, + show: true, + values: [], + }, + yaxes: [ + { + $$hashKey: 'object:38', + format: 'short', + label: null, + logBase: 1, + max: null, + min: null, + show: true, + }, + { + $$hashKey: 'object:39', + format: 'short', + label: null, + logBase: 1, + max: null, + min: null, + show: true, + }, + ], + yaxis: { + align: false, + alignLevel: null, + }, + bars: false, + dashes: false, + fillGradient: 0, + hiddenSeries: false, + percentage: false, + points: false, + stack: false, + timeFrom: null, + timeShift: null, + datasource: null, +}; diff --git a/public/app/plugins/panel/timeseries/migrations.ts b/public/app/plugins/panel/timeseries/migrations.ts index b9d6af16683..c2a84aa134c 100644 --- a/public/app/plugins/panel/timeseries/migrations.ts +++ b/public/app/plugins/panel/timeseries/migrations.ts @@ -1,20 +1,20 @@ import { + ConfigOverrideRule, + DynamicConfigValue, + FieldColorModeId, FieldConfig, + FieldConfigProperty, FieldConfigSource, + FieldMatcherID, + fieldReducers, NullValueMode, PanelModel, - fieldReducers, - ConfigOverrideRule, - FieldMatcherID, - DynamicConfigValue, - FieldConfigProperty, - FieldColorModeId, } from '@grafana/data'; import { GraphFieldConfig, LegendDisplayMode } from '@grafana/ui'; import { - FillGradientMode, AxisPlacement, DrawStyle, + FillGradientMode, LineInterpolation, LineStyle, PointVisibility, @@ -256,15 +256,29 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour legend: { displayMode: LegendDisplayMode.List, placement: 'bottom', + calcs: [], }, tooltipOptions: { mode: 'single', }, }; - if (angular.legend?.values) { - const show = getReducersFromLegend(angular.legend?.values); - console.log('Migrate Legend', show); + // Legend config migration + const legendConfig = angular.legend; + if (legendConfig) { + if (legendConfig.show) { + options.legend.displayMode = legendConfig.alignAsTable ? LegendDisplayMode.Table : LegendDisplayMode.List; + } else { + options.legend.displayMode = LegendDisplayMode.Hidden; + } + + if (legendConfig.rightSide) { + options.legend.placement = 'right'; + } + + if (angular.legend.values) { + options.legend.calcs = getReducersFromLegend(angular.legend); + } } return { diff --git a/public/app/plugins/panel/timeseries/module.tsx b/public/app/plugins/panel/timeseries/module.tsx index 217fb9c04cf..719bf74be9d 100644 --- a/public/app/plugins/panel/timeseries/module.tsx +++ b/public/app/plugins/panel/timeseries/module.tsx @@ -1,9 +1,9 @@ -import { PanelPlugin } from '@grafana/data'; +import { PanelPlugin, standardEditorsRegistry, StatsPickerConfigSettings } from '@grafana/data'; import { GraphFieldConfig, LegendDisplayMode } from '@grafana/ui'; import { TimeSeriesPanel } from './TimeSeriesPanel'; import { graphPanelChangedHandler } from './migrations'; import { Options } from './types'; -import { getGraphFieldConfig, defaultGraphConfig } from './config'; +import { defaultGraphConfig, getGraphFieldConfig } from './config'; export const plugin = new PanelPlugin(TimeSeriesPanel) .setPanelChangeHandler(graphPanelChangedHandler) @@ -48,5 +48,17 @@ export const plugin = new PanelPlugin(TimeSeriesPanel ], }, showIf: c => c.legend.displayMode !== LegendDisplayMode.Hidden, + }) + .addCustomEditor({ + 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, }); });