From de5cd4a7d35a7becce00b1140777e820a6a36fc2 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Mon, 17 May 2021 13:00:04 -0700 Subject: [PATCH] Timeline: split "periodic" mode into its own panel (#34171) --- .../panel-timeline/timeline-demo.json | 10 +- .../panel-timeline/timeline-modes.json | 8 +- .../transformers/joinDataFrames.ts | 5 +- pkg/api/frontendsettings.go | 14 +-- pkg/plugins/manager/manager_test.go | 3 +- .../app/features/plugins/built_in_plugins.ts | 6 +- .../state-timeline/StateTimelinePanel.tsx | 53 +++++++++++ .../TimelineChart.tsx | 0 .../img/timeline.svg | 0 .../{timeline => state-timeline}/module.tsx | 40 +++----- .../{timeline => state-timeline}/plugin.json | 4 +- .../{timeline => state-timeline}/timeline.ts | 0 .../{timeline => state-timeline}/types.ts | 14 ++- .../panel/state-timeline/utils.test.ts | 60 ++++++++++++ .../{timeline => state-timeline}/utils.ts | 95 +++++++++++++++++++ .../StatusGridPanel.tsx} | 12 ++- .../plugins/panel/status-grid/img/status.svg | 42 ++++++++ .../app/plugins/panel/status-grid/module.tsx | 78 +++++++++++++++ .../app/plugins/panel/status-grid/plugin.json | 19 ++++ public/app/plugins/panel/status-grid/types.ts | 27 ++++++ 20 files changed, 434 insertions(+), 56 deletions(-) create mode 100755 public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx rename public/app/plugins/panel/{timeline => state-timeline}/TimelineChart.tsx (100%) rename public/app/plugins/panel/{timeline => state-timeline}/img/timeline.svg (100%) rename public/app/plugins/panel/{timeline => state-timeline}/module.tsx (67%) rename public/app/plugins/panel/{timeline => state-timeline}/plugin.json (83%) rename public/app/plugins/panel/{timeline => state-timeline}/timeline.ts (100%) rename public/app/plugins/panel/{timeline => state-timeline}/types.ts (73%) create mode 100644 public/app/plugins/panel/state-timeline/utils.test.ts rename public/app/plugins/panel/{timeline => state-timeline}/utils.ts (69%) rename public/app/plugins/panel/{timeline/TimelinePanel.tsx => status-grid/StatusGridPanel.tsx} (66%) create mode 100644 public/app/plugins/panel/status-grid/img/status.svg create mode 100755 public/app/plugins/panel/status-grid/module.tsx create mode 100755 public/app/plugins/panel/status-grid/plugin.json create mode 100644 public/app/plugins/panel/status-grid/types.ts diff --git a/devenv/dev-dashboards/panel-timeline/timeline-demo.json b/devenv/dev-dashboards/panel-timeline/timeline-demo.json index 81372febb15..0064cb80483 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-demo.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-demo.json @@ -110,7 +110,7 @@ } ], "title": "State changes strings", - "type": "timeline" + "type": "state-timeline" }, { "datasource": null, @@ -204,7 +204,7 @@ } ], "title": "State changes with boolean values", - "type": "timeline" + "type": "state-timeline" }, { "datasource": null, @@ -298,7 +298,7 @@ } ], "title": "State changes with nulls", - "type": "timeline" + "type": "state-timeline" }, { "datasource": null, @@ -388,8 +388,8 @@ "stringInput": "" } ], - "title": "\"Periodic samples\" Mode", - "type": "timeline" + "title": "Status map", + "type": "status-grid" } ], "refresh": false, diff --git a/devenv/dev-dashboards/panel-timeline/timeline-modes.json b/devenv/dev-dashboards/panel-timeline/timeline-modes.json index 28c0f186671..ecc1d988489 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-modes.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-modes.json @@ -188,7 +188,7 @@ } ], "title": "\"State changes\" Mode", - "type": "timeline" + "type": "state-timeline" }, { "datasource": null, @@ -253,7 +253,7 @@ } ], "title": "\"State changes\" Mode (strings & booleans)", - "type": "timeline" + "type": "state-timeline" }, { "datasource": null, @@ -331,8 +331,8 @@ "stringInput": "" } ], - "title": "\"Periodic samples\" Mode", - "type": "timeline" + "title": "Status map view", + "type": "status-grid" } ], "refresh": false, diff --git a/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts b/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts index be0a7647ad9..d263b522c41 100644 --- a/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts +++ b/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts @@ -155,7 +155,8 @@ export function outerJoinDataFrames(options: JoinOptions): DataFrame | undefined } // Support the standard graph span nulls field config - nullModesFrame.push(field.config.custom?.spanNulls === true ? NULL_REMOVE : NULL_EXPAND); + let spanNulls = field.config.custom?.spanNulls; + nullModesFrame.push(spanNulls === true ? NULL_REMOVE : spanNulls === -1 ? NULL_RETAIN : NULL_EXPAND); let labels = field.labels ?? {}; if (frame.name) { @@ -285,7 +286,7 @@ export function join(tables: AlignedData[], nullModes?: number[][]) { let yVal = ys[i]; let alignedIdx = xIdxs.get(xs[i]); - if (yVal == null) { + if (yVal === null) { if (nullMode !== NULL_REMOVE) { yVals[alignedIdx] = yVal; diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 077534ff9e0..1f89920e535 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -274,20 +274,22 @@ func getPanelSort(id string) int { sort = 7 case "piechart": sort = 8 - case "timeline": + case "state-timeline": sort = 9 case "heatmap": sort = 10 - case "graph": + case "status-grid": sort = 11 - case "text": + case "graph": sort = 12 - case "alertlist": + case "text": sort = 13 - case "dashlist": + case "alertlist": sort = 14 - case "news": + case "dashlist": sort = 15 + case "news": + sort = 16 } return sort } diff --git a/pkg/plugins/manager/manager_test.go b/pkg/plugins/manager/manager_test.go index a6fc235e7aa..80595e8bfea 100644 --- a/pkg/plugins/manager/manager_test.go +++ b/pkg/plugins/manager/manager_test.go @@ -431,7 +431,8 @@ func verifyCorePluginCatalogue(t *testing.T, pm *PluginManager) { "table", "table-old", "text", - "timeline", + "state-timeline", + "status-grid", "timeseries", "welcome", "xychart", diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 2b68e190131..04973256170 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -42,7 +42,8 @@ const alertmanagerPlugin = async () => import * as textPanel from 'app/plugins/panel/text/module'; import * as timeseriesPanel from 'app/plugins/panel/timeseries/module'; -import * as timelinePanel from 'app/plugins/panel/timeline/module'; +import * as stateTimelinePanel from 'app/plugins/panel/state-timeline/module'; +import * as statusGridPanel from 'app/plugins/panel/status-grid/module'; import * as graphPanel from 'app/plugins/panel/graph/module'; import * as xyChartPanel from 'app/plugins/panel/xychart/module'; import * as dashListPanel from 'app/plugins/panel/dashlist/module'; @@ -90,7 +91,8 @@ const builtInPlugins: any = { 'app/plugins/panel/text/module': textPanel, 'app/plugins/panel/timeseries/module': timeseriesPanel, - 'app/plugins/panel/timeline/module': timelinePanel, + 'app/plugins/panel/state-timeline/module': stateTimelinePanel, + 'app/plugins/panel/status-grid/module': statusGridPanel, 'app/plugins/panel/graph/module': graphPanel, 'app/plugins/panel/xychart/module': xyChartPanel, 'app/plugins/panel/dashlist/module': dashListPanel, diff --git a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx new file mode 100755 index 00000000000..6989cdc6d0d --- /dev/null +++ b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx @@ -0,0 +1,53 @@ +import React, { useMemo } from 'react'; +import { PanelProps } from '@grafana/data'; +import { useTheme2, ZoomPlugin } from '@grafana/ui'; +import { TimelineMode, TimelineOptions } from './types'; +import { TimelineChart } from './TimelineChart'; +import { prepareTimelineFields } from './utils'; + +interface TimelinePanelProps extends PanelProps {} + +/** + * @alpha + */ +export const StateTimelinePanel: React.FC = ({ + data, + timeRange, + timeZone, + options, + width, + height, + onChangeTimeRange, +}) => { + const theme = useTheme2(); + + const { frames, warn } = useMemo(() => prepareTimelineFields(data?.series, options.mergeValues ?? true), [ + data, + options.mergeValues, + ]); + + if (!frames || warn) { + return ( +
+

{warn ?? 'No data found in response'}

+
+ ); + } + + return ( + + {(config) => } + + ); +}; diff --git a/public/app/plugins/panel/timeline/TimelineChart.tsx b/public/app/plugins/panel/state-timeline/TimelineChart.tsx similarity index 100% rename from public/app/plugins/panel/timeline/TimelineChart.tsx rename to public/app/plugins/panel/state-timeline/TimelineChart.tsx diff --git a/public/app/plugins/panel/timeline/img/timeline.svg b/public/app/plugins/panel/state-timeline/img/timeline.svg similarity index 100% rename from public/app/plugins/panel/timeline/img/timeline.svg rename to public/app/plugins/panel/state-timeline/img/timeline.svg diff --git a/public/app/plugins/panel/timeline/module.tsx b/public/app/plugins/panel/state-timeline/module.tsx similarity index 67% rename from public/app/plugins/panel/timeline/module.tsx rename to public/app/plugins/panel/state-timeline/module.tsx index 23d8fccc398..aea825b3fb9 100755 --- a/public/app/plugins/panel/timeline/module.tsx +++ b/public/app/plugins/panel/state-timeline/module.tsx @@ -1,9 +1,9 @@ import { FieldColorModeId, FieldConfigProperty, PanelPlugin } from '@grafana/data'; -import { TimelinePanel } from './TimelinePanel'; -import { TimelineOptions, TimelineFieldConfig, TimelineMode, defaultTimelineFieldConfig } from './types'; +import { StateTimelinePanel } from './StateTimelinePanel'; +import { TimelineOptions, TimelineFieldConfig, defaultPanelOptions, defaultTimelineFieldConfig } from './types'; import { BarValueVisibility } from '@grafana/ui'; -export const plugin = new PanelPlugin(TimelinePanel) +export const plugin = new PanelPlugin(StateTimelinePanel) .useFieldConfig({ standardOptions: { [FieldConfigProperty.Color]: { @@ -41,17 +41,6 @@ export const plugin = new PanelPlugin(Time }) .setPanelOptions((builder) => { builder - .addRadio({ - path: 'mode', - name: 'Mode', - defaultValue: TimelineMode.Changes, - settings: { - options: [ - { label: 'State changes', value: TimelineMode.Changes }, - { label: 'Periodic samples', value: TimelineMode.Samples }, - ], - }, - }) .addRadio({ path: 'showValue', name: 'Show values', @@ -62,7 +51,7 @@ export const plugin = new PanelPlugin(Time { value: BarValueVisibility.Never, label: 'Never' }, ], }, - defaultValue: BarValueVisibility.Always, + defaultValue: defaultPanelOptions.showValue, }) .addRadio({ path: 'alignValue', @@ -74,29 +63,22 @@ export const plugin = new PanelPlugin(Time { value: 'right', label: 'Right' }, ], }, - defaultValue: 'center', - showIf: ({ mode }) => mode === TimelineMode.Changes, + defaultValue: defaultPanelOptions.alignValue, + }) + .addBooleanSwitch({ + path: 'mergeValues', + name: 'Merge equal consecutive values', + defaultValue: defaultPanelOptions.mergeValues, }) .addSliderInput({ path: 'rowHeight', name: 'Row height', - defaultValue: 0.9, settings: { min: 0, max: 1, step: 0.01, }, - }) - .addSliderInput({ - path: 'colWidth', - name: 'Column width', - defaultValue: 0.9, - settings: { - min: 0, - max: 1, - step: 0.01, - }, - showIf: ({ mode }) => mode === TimelineMode.Samples, + defaultValue: defaultPanelOptions.rowHeight, }); //addLegendOptions(builder); diff --git a/public/app/plugins/panel/timeline/plugin.json b/public/app/plugins/panel/state-timeline/plugin.json similarity index 83% rename from public/app/plugins/panel/timeline/plugin.json rename to public/app/plugins/panel/state-timeline/plugin.json index d5e86638368..9239ef48875 100755 --- a/public/app/plugins/panel/timeline/plugin.json +++ b/public/app/plugins/panel/state-timeline/plugin.json @@ -1,7 +1,7 @@ { "type": "panel", - "name": "Timeline", - "id": "timeline", + "name": "State timeline", + "id": "state-timeline", "state": "alpha", diff --git a/public/app/plugins/panel/timeline/timeline.ts b/public/app/plugins/panel/state-timeline/timeline.ts similarity index 100% rename from public/app/plugins/panel/timeline/timeline.ts rename to public/app/plugins/panel/state-timeline/timeline.ts diff --git a/public/app/plugins/panel/timeline/types.ts b/public/app/plugins/panel/state-timeline/types.ts similarity index 73% rename from public/app/plugins/panel/timeline/types.ts rename to public/app/plugins/panel/state-timeline/types.ts index f4dcf1b7fe5..b0a449177f6 100644 --- a/public/app/plugins/panel/timeline/types.ts +++ b/public/app/plugins/panel/state-timeline/types.ts @@ -4,12 +4,14 @@ import { VizLegendOptions, HideableFieldConfig, BarValueVisibility } from '@graf * @alpha */ export interface TimelineOptions { - mode: TimelineMode; + mode: TimelineMode; // not in the saved model! + legend: VizLegendOptions; showValue: BarValueVisibility; rowHeight: number; colWidth?: number; alignValue: TimelineValueAlignment; + mergeValues?: boolean; } export type TimelineValueAlignment = 'center' | 'left' | 'right'; @@ -22,6 +24,16 @@ export interface TimelineFieldConfig extends HideableFieldConfig { fillOpacity?: number; // 100 } +/** + * @alpha + */ +export const defaultPanelOptions: Partial = { + showValue: BarValueVisibility.Always, + mergeValues: true, + alignValue: 'left', + rowHeight: 0.9, +}; + /** * @alpha */ diff --git a/public/app/plugins/panel/state-timeline/utils.test.ts b/public/app/plugins/panel/state-timeline/utils.test.ts new file mode 100644 index 00000000000..fac08d5fed7 --- /dev/null +++ b/public/app/plugins/panel/state-timeline/utils.test.ts @@ -0,0 +1,60 @@ +import { FieldType, toDataFrame } from '@grafana/data'; +import { prepareTimelineFields } from './utils'; + +describe('prepare timeline graph', () => { + it('errors with no time fields', () => { + const frames = [ + toDataFrame({ + fields: [ + { name: 'a', values: [1, 2, 3] }, + { name: 'b', values: ['a', 'b', 'c'] }, + ], + }), + ]; + const info = prepareTimelineFields(frames, true); + expect(info.warn).toEqual('Data does not have a time field'); + }); + + it('requires a number, string, or boolean value', () => { + const frames = [ + toDataFrame({ + fields: [ + { name: 'a', type: FieldType.time, values: [1, 2, 3] }, + { name: 'b', type: FieldType.other, values: [{}, {}, {}] }, + ], + }), + ]; + const info = prepareTimelineFields(frames, true); + expect(info.warn).toEqual('No graphable fields'); + }); + + it('will merge duplicate values', () => { + const frames = [ + toDataFrame({ + fields: [ + { name: 'a', type: FieldType.time, values: [1, 2, 3, 4, 5, 6, 7] }, + { name: 'b', values: [1, 1, undefined, 1, 2, 2, null, 2, 3] }, + ], + }), + ]; + const info = prepareTimelineFields(frames, true); + expect(info.warn).toBeUndefined(); + + const out = info.frames![0]; + + const field = out.fields.find((f) => f.name === 'b'); + expect(field?.values.toArray()).toMatchInlineSnapshot(` + Array [ + 1, + undefined, + undefined, + undefined, + 2, + undefined, + null, + 2, + 3, + ] + `); + }); +}); diff --git a/public/app/plugins/panel/timeline/utils.ts b/public/app/plugins/panel/state-timeline/utils.ts similarity index 69% rename from public/app/plugins/panel/timeline/utils.ts rename to public/app/plugins/panel/state-timeline/utils.ts index d7ed865dd9b..7c1ada74def 100644 --- a/public/app/plugins/panel/timeline/utils.ts +++ b/public/app/plugins/panel/state-timeline/utils.ts @@ -8,6 +8,8 @@ import { outerJoinDataFrames, Field, FALLBACK_COLOR, + FieldType, + ArrayVector, } from '@grafana/data'; import { UPlotConfigBuilder, FIXED_UNIT, SeriesVisibilityChangeMode, UPlotConfigPrepFn } from '@grafana/ui'; import { TimelineCoreOptions, getConfig } from './timeline'; @@ -193,3 +195,96 @@ export function getNamesToFieldIndex(frame: DataFrame): Map { } return names; } + +/** + * If any sequential duplicate values exist, this will return a new array + * with the future values set to undefined. + * + * in: 1, 1,undefined, 1,2, 2,null,2,3 + * out: 1,undefined,undefined,undefined,2,undefined,null,2,3 + */ +export function unsetSameFutureValues(values: any[]): any[] | undefined { + let prevVal = values[0]; + let clone: any[] | undefined = undefined; + + for (let i = 1; i < values.length; i++) { + let value = values[i]; + + if (value === null) { + prevVal = null; + } else { + if (value === prevVal) { + if (!clone) { + clone = [...values]; + } + clone[i] = undefined; + } else if (value != null) { + prevVal = value; + } + } + } + return clone; +} + +// This will return a set of frames with only graphable values included +export function prepareTimelineFields( + series: DataFrame[] | undefined, + mergeValues: boolean +): { frames?: DataFrame[]; warn?: string } { + if (!series?.length) { + return { warn: 'No data in response' }; + } + let hasTimeseries = false; + const frames: DataFrame[] = []; + for (let frame of series) { + let isTimeseries = false; + let changed = false; + const fields: Field[] = []; + for (const field of frame.fields) { + switch (field.type) { + case FieldType.time: + isTimeseries = true; + hasTimeseries = true; + fields.push(field); + break; + case FieldType.number: + case FieldType.boolean: + case FieldType.string: + if (mergeValues) { + let merged = unsetSameFutureValues(field.values.toArray()); + if (merged) { + fields.push({ + ...field, + values: new ArrayVector(merged), + }); + changed = true; + continue; + } + } + fields.push(field); + break; + default: + changed = true; + } + } + if (isTimeseries && fields.length > 1) { + hasTimeseries = true; + if (changed) { + frames.push({ + ...frame, + fields, + }); + } else { + frames.push(frame); + } + } + } + + if (!hasTimeseries) { + return { warn: 'Data does not have a time field' }; + } + if (!frames.length) { + return { warn: 'No graphable fields' }; + } + return { frames }; +} diff --git a/public/app/plugins/panel/timeline/TimelinePanel.tsx b/public/app/plugins/panel/status-grid/StatusGridPanel.tsx similarity index 66% rename from public/app/plugins/panel/timeline/TimelinePanel.tsx rename to public/app/plugins/panel/status-grid/StatusGridPanel.tsx index c81a5113947..e9f837914b7 100755 --- a/public/app/plugins/panel/timeline/TimelinePanel.tsx +++ b/public/app/plugins/panel/status-grid/StatusGridPanel.tsx @@ -1,15 +1,16 @@ import React from 'react'; import { PanelProps } from '@grafana/data'; import { useTheme2, ZoomPlugin } from '@grafana/ui'; -import { TimelineOptions } from './types'; -import { TimelineChart } from './TimelineChart'; +import { StatusPanelOptions } from './types'; +import { TimelineChart } from '../state-timeline/TimelineChart'; +import { TimelineMode } from '../state-timeline/types'; -interface TimelinePanelProps extends PanelProps {} +interface TimelinePanelProps extends PanelProps {} /** * @alpha */ -export const TimelinePanel: React.FC = ({ +export const StatusGridPanel: React.FC = ({ data, timeRange, timeZone, @@ -38,6 +39,9 @@ export const TimelinePanel: React.FC = ({ width={width} height={height} {...options} + // hardcoded + mode={TimelineMode.Samples} + alignValue="center" > {(config) => } diff --git a/public/app/plugins/panel/status-grid/img/status.svg b/public/app/plugins/panel/status-grid/img/status.svg new file mode 100644 index 00000000000..8020fddfb0e --- /dev/null +++ b/public/app/plugins/panel/status-grid/img/status.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/app/plugins/panel/status-grid/module.tsx b/public/app/plugins/panel/status-grid/module.tsx new file mode 100755 index 00000000000..0be8f914145 --- /dev/null +++ b/public/app/plugins/panel/status-grid/module.tsx @@ -0,0 +1,78 @@ +import { FieldColorModeId, FieldConfigProperty, PanelPlugin } from '@grafana/data'; +import { StatusGridPanel } from './StatusGridPanel'; +import { StatusPanelOptions, StatusFieldConfig, defaultStatusFieldConfig } from './types'; +import { BarValueVisibility } from '@grafana/ui'; + +export const plugin = new PanelPlugin(StatusGridPanel) + .useFieldConfig({ + standardOptions: { + [FieldConfigProperty.Color]: { + settings: { + byValueSupport: true, + }, + defaultValue: { + mode: FieldColorModeId.PaletteClassic, + }, + }, + }, + useCustomConfig: (builder) => { + builder + .addSliderInput({ + path: 'lineWidth', + name: 'Line width', + defaultValue: defaultStatusFieldConfig.lineWidth, + settings: { + min: 0, + max: 10, + step: 1, + }, + }) + .addSliderInput({ + path: 'fillOpacity', + name: 'Fill opacity', + defaultValue: defaultStatusFieldConfig.fillOpacity, + settings: { + min: 0, + max: 100, + step: 1, + }, + }); + }, + }) + .setPanelOptions((builder) => { + builder + .addRadio({ + path: 'showValue', + name: 'Show values', + settings: { + options: [ + //{ value: BarValueVisibility.Auto, label: 'Auto' }, + { value: BarValueVisibility.Always, label: 'Always' }, + { value: BarValueVisibility.Never, label: 'Never' }, + ], + }, + defaultValue: BarValueVisibility.Always, + }) + .addSliderInput({ + path: 'rowHeight', + name: 'Row height', + defaultValue: 0.9, + settings: { + min: 0, + max: 1, + step: 0.01, + }, + }) + .addSliderInput({ + path: 'colWidth', + name: 'Column width', + defaultValue: 0.9, + settings: { + min: 0, + max: 1, + step: 0.01, + }, + }); + + //addLegendOptions(builder); + }); diff --git a/public/app/plugins/panel/status-grid/plugin.json b/public/app/plugins/panel/status-grid/plugin.json new file mode 100755 index 00000000000..7e260e54863 --- /dev/null +++ b/public/app/plugins/panel/status-grid/plugin.json @@ -0,0 +1,19 @@ +{ + "type": "panel", + "name": "Status grid", + "id": "status-grid", + + "state": "alpha", + + "info": { + "description": "System status map", + "author": { + "name": "Grafana Labs", + "url": "https://grafana.com" + }, + "logos": { + "small": "img/status.svg", + "large": "img/status.svg" + } + } +} diff --git a/public/app/plugins/panel/status-grid/types.ts b/public/app/plugins/panel/status-grid/types.ts new file mode 100644 index 00000000000..8c06f64bc79 --- /dev/null +++ b/public/app/plugins/panel/status-grid/types.ts @@ -0,0 +1,27 @@ +import { VizLegendOptions, HideableFieldConfig, BarValueVisibility } from '@grafana/ui'; + +/** + * @alpha + */ +export interface StatusPanelOptions { + legend: VizLegendOptions; + showValue: BarValueVisibility; + rowHeight: number; + colWidth?: number; +} + +/** + * @alpha + */ +export interface StatusFieldConfig extends HideableFieldConfig { + lineWidth?: number; // 0 + fillOpacity?: number; // 100 +} + +/** + * @alpha + */ +export const defaultStatusFieldConfig: StatusFieldConfig = { + lineWidth: 1, + fillOpacity: 70, +};