diff --git a/packages/grafana-ui/src/utils/singlestat.ts b/packages/grafana-ui/src/utils/singlestat.ts index 95938069b40..5f5fbb8f247 100644 --- a/packages/grafana-ui/src/utils/singlestat.ts +++ b/packages/grafana-ui/src/utils/singlestat.ts @@ -1,17 +1,11 @@ import { PanelData, NullValueMode, SingleStatValueInfo } from '../types'; import { processTimeSeries } from './processTimeSeries'; -import { DisplayValueOptions } from './displayValue'; export interface SingleStatProcessingOptions { panelData: PanelData; stat: string; } -export interface SingleStatOptions { - stat: string; - display: DisplayValueOptions; -} - // // This is a temporary thing, waiting for a better data model and maybe unification between time series & table data // diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 9a156652a65..ab9d9aba08a 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -25,6 +25,7 @@ import * as heatmapPanel from 'app/plugins/panel/heatmap/module'; import * as tablePanel from 'app/plugins/panel/table/module'; import * as table2Panel from 'app/plugins/panel/table2/module'; import * as singlestatPanel from 'app/plugins/panel/singlestat/module'; +import * as singlestatPanel2 from 'app/plugins/panel/singlestat2/module'; import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module'; import * as gaugePanel from 'app/plugins/panel/gauge/module'; import * as barGaugePanel from 'app/plugins/panel/bargauge/module'; @@ -57,6 +58,7 @@ const builtInPlugins = { 'app/plugins/panel/table/module': tablePanel, 'app/plugins/panel/table2/module': table2Panel, 'app/plugins/panel/singlestat/module': singlestatPanel, + 'app/plugins/panel/singlestat2/module': singlestatPanel2, 'app/plugins/panel/gettingstarted/module': gettingStartedPanel, 'app/plugins/panel/gauge/module': gaugePanel, 'app/plugins/panel/bargauge/module': barGaugePanel, diff --git a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx index 8102d16de40..2fa48b4572a 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -2,7 +2,7 @@ import React from 'react'; // Services & Utils -import { DisplayValue, VizOrientation } from '@grafana/ui'; +import { DisplayValue } from '@grafana/ui'; import { config } from 'app/core/config'; // Components @@ -10,17 +10,11 @@ import { BarGauge } from '@grafana/ui'; // Types import { BarGaugeOptions } from './types'; -import { SingleStatPanel } from '../gauge/SingleStatPanel'; - -export class BarGaugePanel extends SingleStatPanel { - getOrientation(): VizOrientation { - const { options } = this.props; - return options.orientation; - } +import { SingleStatBase } from '../singlestat2/SingleStatBase'; +export class BarGaugePanel extends SingleStatBase { renderStat(value: DisplayValue, width: number, height: number) { const { options } = this.props; - const { display } = options; return ( { width={width} height={height} orientation={options.orientation} - thresholds={display.thresholds} + thresholds={options.thresholds} theme={config.theme} /> ); diff --git a/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx b/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx index 2420a4c1ec2..cccd4e88b8e 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx @@ -2,38 +2,31 @@ import React, { PureComponent } from 'react'; // Components -import { SingleStatValueEditor } from 'app/plugins/panel/gauge/SingleStatValueEditor'; -import { - PanelOptionsGrid, - PanelOptionsGroup, - FormField, - DisplayValueOptions, - ThresholdsEditor, - Threshold, -} from '@grafana/ui'; +import { ThresholdsEditor, ValueMappingsEditor, PanelOptionsGrid, PanelOptionsGroup, FormField } from '@grafana/ui'; // Types -import { FormLabel, PanelEditorProps, Select, ValueMappingsEditor, ValueMapping } from '@grafana/ui'; +import { FormLabel, PanelEditorProps, Threshold, Select, ValueMapping } from '@grafana/ui'; import { BarGaugeOptions, orientationOptions } from './types'; -import { DisplayValueEditor } from '../gauge/DisplayValueEditor'; +import { SingleStatValueEditor } from '../singlestat2/SingleStatValueEditor'; +import { SingleStatValueOptions } from '../singlestat2/types'; export class BarGaugePanelEditor extends PureComponent> { - onDisplayOptionsChanged = (displayOptions: DisplayValueOptions) => + onThresholdsChanged = (thresholds: Threshold[]) => this.props.onOptionsChange({ ...this.props.options, - display: displayOptions, - }); - - onThresholdsChanged = (thresholds: Threshold[]) => - this.onDisplayOptionsChanged({ - ...this.props.options.display, thresholds, }); onValueMappingsChanged = (valueMappings: ValueMapping[]) => - this.onDisplayOptionsChanged({ - ...this.props.options.display, - mappings: valueMappings, + this.props.onOptionsChange({ + ...this.props.options, + valueMappings, + }); + + onValueOptionsChanged = (valueOptions: SingleStatValueOptions) => + this.props.onOptionsChange({ + ...this.props.options, + valueOptions, }); onMinValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, minValue: target.value }); @@ -41,17 +34,12 @@ export class BarGaugePanelEditor extends PureComponent this.props.onOptionsChange({ ...this.props.options, orientation: value }); render() { - const { onOptionsChange, options } = this.props; - const { display } = options; + const { options } = this.props; return ( <> - {/* This just sets the 'stats', that should be moved to somethign more general */} - - - - + @@ -66,9 +54,10 @@ export class BarGaugePanelEditor extends PureComponent - - + + + ); } diff --git a/public/app/plugins/panel/bargauge/module.tsx b/public/app/plugins/panel/bargauge/module.tsx index f3dab902dd1..5ca355b3110 100644 --- a/public/app/plugins/panel/bargauge/module.tsx +++ b/public/app/plugins/panel/bargauge/module.tsx @@ -3,10 +3,10 @@ import { ReactPanelPlugin } from '@grafana/ui'; import { BarGaugePanel } from './BarGaugePanel'; import { BarGaugePanelEditor } from './BarGaugePanelEditor'; import { BarGaugeOptions, defaults } from './types'; -import { gaugePanelTypeChangedHook } from '../gauge/module'; +import { singleStatOptionsCheck } from '../singlestat2/module'; export const reactPanel = new ReactPanelPlugin(BarGaugePanel); reactPanel.setEditor(BarGaugePanelEditor); reactPanel.setDefaults(defaults); -reactPanel.setPanelTypeChangedHook(gaugePanelTypeChangedHook); +reactPanel.setPanelTypeChangedHook(singleStatOptionsCheck); diff --git a/public/app/plugins/panel/bargauge/types.ts b/public/app/plugins/panel/bargauge/types.ts index 562d4b03323..ea6f1887501 100644 --- a/public/app/plugins/panel/bargauge/types.ts +++ b/public/app/plugins/panel/bargauge/types.ts @@ -1,17 +1,28 @@ -import { SelectOptionItem, VizOrientation } from '@grafana/ui'; +import { VizOrientation, SelectOptionItem } from '@grafana/ui'; -import { GaugeOptions, defaults as gaugeDefaults } from '../gauge/types'; - -export interface BarGaugeOptions extends GaugeOptions { - orientation: VizOrientation; -} +import { SingleStatBaseOptions } from '../singlestat2/types'; export const orientationOptions: SelectOptionItem[] = [ { value: VizOrientation.Horizontal, label: 'Horizontal' }, { value: VizOrientation.Vertical, label: 'Vertical' }, ]; +export interface BarGaugeOptions extends SingleStatBaseOptions { + minValue: number; + maxValue: number; +} + export const defaults: BarGaugeOptions = { - ...gaugeDefaults, + minValue: 0, + maxValue: 100, orientation: VizOrientation.Horizontal, + valueOptions: { + unit: 'none', + stat: 'avg', + prefix: '', + suffix: '', + decimals: null, + }, + thresholds: [{ index: 0, value: -Infinity, color: 'green' }, { index: 1, value: 80, color: 'red' }], + valueMappings: [], }; diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index 387e0116239..72b4756c0b6 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -10,19 +10,18 @@ import { Gauge } from '@grafana/ui'; // Types import { GaugeOptions } from './types'; import { DisplayValue } from '@grafana/ui/src/utils/displayValue'; -import { SingleStatPanel } from './SingleStatPanel'; +import { SingleStatBase } from '../singlestat2/SingleStatBase'; -export class GaugePanel extends SingleStatPanel { +export class GaugePanel extends SingleStatBase { renderStat(value: DisplayValue, width: number, height: number) { const { options } = this.props; - const { display } = options; return ( > { - onDisplayOptionsChanged = (displayOptions: DisplayValueOptions) => + onThresholdsChanged = (thresholds: Threshold[]) => this.props.onOptionsChange({ ...this.props.options, - display: displayOptions, - }); - - onThresholdsChanged = (thresholds: Threshold[]) => - this.onDisplayOptionsChanged({ - ...this.props.options.display, thresholds, }); onValueMappingsChanged = (valueMappings: ValueMapping[]) => - this.onDisplayOptionsChanged({ - ...this.props.options.display, - mappings: valueMappings, + this.props.onOptionsChange({ + ...this.props.options, + valueMappings, + }); + + onValueOptionsChanged = (valueOptions: SingleStatValueOptions) => + this.props.onOptionsChange({ + ...this.props.options, + valueOptions, }); render() { const { onOptionsChange, options } = this.props; - const { display } = options; return ( <> - {/* This just sets the 'stats', that should be moved to somethign more general */} - - + - + - + ); } diff --git a/public/app/plugins/panel/gauge/SingleStatValueEditor.tsx b/public/app/plugins/panel/gauge/SingleStatValueEditor.tsx deleted file mode 100644 index 414a606b108..00000000000 --- a/public/app/plugins/panel/gauge/SingleStatValueEditor.tsx +++ /dev/null @@ -1,51 +0,0 @@ -// Libraries -import React, { PureComponent } from 'react'; - -// Components -import { FormLabel, PanelOptionsGroup, Select } from '@grafana/ui'; - -// Types -import { GaugeOptions } from './types'; - -const statOptions = [ - { value: 'min', label: 'Min' }, - { value: 'max', label: 'Max' }, - { value: 'avg', label: 'Average' }, - { value: 'current', label: 'Current' }, - { value: 'total', label: 'Total' }, - { value: 'name', label: 'Name' }, - { value: 'first', label: 'First' }, - { value: 'delta', label: 'Delta' }, - { value: 'diff', label: 'Difference' }, - { value: 'range', label: 'Range' }, - { value: 'last_time', label: 'Time of last point' }, -]; - -const labelWidth = 6; - -export interface Props { - options: GaugeOptions; - onChange: (options: GaugeOptions) => void; -} - -export class SingleStatValueEditor extends PureComponent { - onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value }); - - render() { - const { stat } = this.props.options; - - return ( - -
- Stat - option.value === stat)} + /> +
Unit diff --git a/public/app/plugins/panel/singlestat2/img/icn-singlestat-panel.svg b/public/app/plugins/panel/singlestat2/img/icn-singlestat-panel.svg new file mode 100644 index 00000000000..746687d360f --- /dev/null +++ b/public/app/plugins/panel/singlestat2/img/icn-singlestat-panel.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/app/plugins/panel/singlestat2/module.tsx b/public/app/plugins/panel/singlestat2/module.tsx new file mode 100644 index 00000000000..c07e24e5198 --- /dev/null +++ b/public/app/plugins/panel/singlestat2/module.tsx @@ -0,0 +1,29 @@ +import { ReactPanelPlugin } from '@grafana/ui'; +import { SingleStatOptions, defaults } from './types'; +import { SingleStatPanel } from './SingleStatPanel'; +import cloneDeep from 'lodash/cloneDeep'; +import { SingleStatEditor } from './SingleStatEditor'; + +export const reactPanel = new ReactPanelPlugin(SingleStatPanel); + +const optionsToKeep = ['valueOptions', 'stat', 'maxValue', 'maxValue', 'thresholds', 'valueMappings']; + +export const singleStatOptionsCheck = ( + options: Partial, + prevPluginId?: string, + prevOptions?: any +) => { + if (prevOptions) { + optionsToKeep.forEach(v => { + if (prevOptions.hasOwnProperty(v)) { + options[v] = cloneDeep(prevOptions.display); + } + }); + } + + return options; +}; + +reactPanel.setEditor(SingleStatEditor); +reactPanel.setDefaults(defaults); +reactPanel.setPanelTypeChangedHook(singleStatOptionsCheck); diff --git a/public/app/plugins/panel/singlestat2/plugin.json b/public/app/plugins/panel/singlestat2/plugin.json new file mode 100644 index 00000000000..6828399ec2b --- /dev/null +++ b/public/app/plugins/panel/singlestat2/plugin.json @@ -0,0 +1,20 @@ +{ + "type": "panel", + "name": "Singlestat (react)", + "id": "singlestat2", + "state": "alpha", + + "dataFormats": ["time_series", "table"], + + "info": { + "description": "Singlestat Panel for Grafana", + "author": { + "name": "Grafana Project", + "url": "https://grafana.com" + }, + "logos": { + "small": "img/icn-singlestat-panel.svg", + "large": "img/icn-singlestat-panel.svg" + } + } +} diff --git a/public/app/plugins/panel/singlestat2/types.ts b/public/app/plugins/panel/singlestat2/types.ts new file mode 100644 index 00000000000..1f31783e814 --- /dev/null +++ b/public/app/plugins/panel/singlestat2/types.ts @@ -0,0 +1,33 @@ +import { VizOrientation, ValueMapping, Threshold } from '@grafana/ui'; + +export interface SingleStatBaseOptions { + valueMappings: ValueMapping[]; + thresholds: Threshold[]; + valueOptions: SingleStatValueOptions; + orientation: VizOrientation; +} + +export interface SingleStatValueOptions { + unit: string; + suffix: string; + stat: string; + prefix: string; + decimals?: number | null; +} + +export interface SingleStatOptions extends SingleStatBaseOptions { + // TODO, fill in with options from angular +} + +export const defaults: SingleStatOptions = { + valueOptions: { + prefix: '', + suffix: '', + decimals: null, + stat: 'avg', + unit: 'none', + }, + valueMappings: [], + thresholds: [{ index: 0, value: -Infinity, color: 'green' }, { index: 1, value: 80, color: 'red' }], + orientation: VizOrientation.Auto, +};