mirror of https://github.com/grafana/grafana
parent
8299c95459
commit
985f057ab3
@ -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: [], |
||||
}; |
||||
|
||||
@ -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<Props> { |
||||
onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value }); |
||||
|
||||
render() { |
||||
const { stat } = this.props.options; |
||||
|
||||
return ( |
||||
<PanelOptionsGroup title="Show Value"> |
||||
<div className="gf-form"> |
||||
<FormLabel width={labelWidth}>Stat</FormLabel> |
||||
<Select |
||||
width={12} |
||||
options={statOptions} |
||||
onChange={this.onStatChange} |
||||
value={statOptions.find(option => option.value === stat)} |
||||
/> |
||||
</div> |
||||
</PanelOptionsGroup> |
||||
); |
||||
} |
||||
} |
||||
@ -1,31 +0,0 @@ |
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
||||
|
||||
exports[`Gauge Module migrations should migrate from 6.0 settings to 6.1 1`] = ` |
||||
Object { |
||||
"display": Object { |
||||
"decimals": 4, |
||||
"mappings": Array [], |
||||
"prefix": "a", |
||||
"stat": "avg", |
||||
"suffix": "z", |
||||
"thresholds": Array [ |
||||
Object { |
||||
"color": "green", |
||||
"index": 0, |
||||
"value": -Infinity, |
||||
}, |
||||
Object { |
||||
"color": "red", |
||||
"index": 1, |
||||
"value": 80, |
||||
}, |
||||
], |
||||
"unit": "ms", |
||||
}, |
||||
"maxValue": 60, |
||||
"minValue": 50, |
||||
"showThresholdLabels": false, |
||||
"showThresholdMarkers": true, |
||||
"stat": "avg", |
||||
} |
||||
`; |
||||
@ -1,27 +0,0 @@ |
||||
import { gaugePanelTypeChangedHook } from './module'; |
||||
|
||||
describe('Gauge Module', () => { |
||||
describe('migrations', () => { |
||||
it('should migrate from 6.0 settings to 6.1', () => { |
||||
const v60 = { |
||||
minValue: 50, |
||||
maxValue: 60, |
||||
showThresholdMarkers: true, |
||||
showThresholdLabels: false, |
||||
valueOptions: { |
||||
prefix: 'a', |
||||
suffix: 'z', |
||||
decimals: 4, |
||||
stat: 'avg', |
||||
unit: 'ms', |
||||
}, |
||||
valueMappings: [], |
||||
thresholds: [{ index: 0, value: -Infinity, color: 'green' }, { index: 1, value: 80, color: 'red' }], |
||||
}; |
||||
|
||||
const after = gaugePanelTypeChangedHook(v60); |
||||
expect((after.stat = 'avg')); |
||||
expect(after).toMatchSnapshot(); |
||||
}); |
||||
}); |
||||
}); |
||||
@ -1,58 +1,12 @@ |
||||
import { ReactPanelPlugin, DisplayValueOptions } from '@grafana/ui'; |
||||
import cloneDeep from 'lodash/cloneDeep'; |
||||
import { ReactPanelPlugin } from '@grafana/ui'; |
||||
|
||||
import { GaugePanelEditor } from './GaugePanelEditor'; |
||||
import { GaugePanel } from './GaugePanel'; |
||||
import { GaugeOptions, defaults } from './types'; |
||||
import { singleStatOptionsCheck } from '../singlestat2/module'; |
||||
|
||||
export const reactPanel = new ReactPanelPlugin<GaugeOptions>(GaugePanel); |
||||
|
||||
// Bar Gauge uses the same handler
|
||||
|
||||
const optionsToCheck = ['display', 'stat', 'maxValue', 'maxValue']; |
||||
|
||||
export const gaugePanelTypeChangedHook = (options: Partial<GaugeOptions>, prevPluginId?: string, prevOptions?: any) => { |
||||
// TODO! migrate to new settings format
|
||||
//
|
||||
// thresholds?: Threshold[];
|
||||
// valueMappings?: ValueMapping[];
|
||||
// valueOptions?: SingleStatValueOptions;
|
||||
//
|
||||
// if (props.options.valueOptions) {
|
||||
// console.warn('TODO!! how do we best migration options?');
|
||||
// }
|
||||
|
||||
// 6.0 -> 6.1, settings were stored on the root, now moved to display
|
||||
if (!options.display && !prevOptions && options.hasOwnProperty('thresholds')) { |
||||
console.log('Migrating old gauge settings format', options); |
||||
const migrate = options as any; |
||||
const display = (migrate.valueOptions || {}) as DisplayValueOptions; |
||||
|
||||
display.thresholds = migrate.thresholds; |
||||
display.mappings = migrate.valueMappings; |
||||
if (migrate.valueMappings) { |
||||
options.stat = migrate.valueMappings.stat; |
||||
delete migrate.valueMappings.stat; |
||||
} |
||||
|
||||
delete migrate.valueOptions; |
||||
delete migrate.thresholds; |
||||
delete migrate.valueMappings; |
||||
|
||||
options.display = display; |
||||
} |
||||
|
||||
if (prevOptions) { |
||||
optionsToCheck.forEach(v => { |
||||
if (prevOptions.hasOwnProperty(v)) { |
||||
options[v] = cloneDeep(prevOptions.display); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
return options; |
||||
}; |
||||
|
||||
reactPanel.setEditor(GaugePanelEditor); |
||||
reactPanel.setDefaults(defaults); |
||||
reactPanel.setPanelTypeChangedHook(gaugePanelTypeChangedHook); |
||||
reactPanel.setPanelTypeChangedHook(singleStatOptionsCheck); |
||||
|
||||
@ -0,0 +1,9 @@ |
||||
# Singlestat Panel - Native Plugin |
||||
|
||||
The Singlestat Panel is **included** with Grafana. |
||||
|
||||
The Singlestat Panel allows you to show the one main summary stat of a SINGLE series. It reduces the series into a single number (by looking at the max, min, average, or sum of values in the series). Singlestat also provides thresholds to color the stat or the Panel background. It can also translate the single number into a text value, and show a sparkline summary of the series. |
||||
|
||||
Read more about it here: |
||||
|
||||
[http://docs.grafana.org/reference/singlestat/](http://docs.grafana.org/reference/singlestat/) |
||||
@ -0,0 +1,48 @@ |
||||
// Libraries
|
||||
import React, { PureComponent } from 'react'; |
||||
import { |
||||
PanelEditorProps, |
||||
ThresholdsEditor, |
||||
Threshold, |
||||
PanelOptionsGrid, |
||||
ValueMappingsEditor, |
||||
ValueMapping, |
||||
} from '@grafana/ui'; |
||||
|
||||
import { SingleStatOptions, SingleStatValueOptions } from './types'; |
||||
import { SingleStatValueEditor } from './SingleStatValueEditor'; |
||||
|
||||
export class SingleStatEditor extends PureComponent<PanelEditorProps<SingleStatOptions>> { |
||||
onThresholdsChanged = (thresholds: Threshold[]) => |
||||
this.props.onOptionsChange({ |
||||
...this.props.options, |
||||
thresholds, |
||||
}); |
||||
|
||||
onValueMappingsChanged = (valueMappings: ValueMapping[]) => |
||||
this.props.onOptionsChange({ |
||||
...this.props.options, |
||||
valueMappings, |
||||
}); |
||||
|
||||
onValueOptionsChanged = (valueOptions: SingleStatValueOptions) => |
||||
this.props.onOptionsChange({ |
||||
...this.props.options, |
||||
valueOptions, |
||||
}); |
||||
|
||||
render() { |
||||
const { options } = this.props; |
||||
|
||||
return ( |
||||
<> |
||||
<PanelOptionsGrid> |
||||
<SingleStatValueEditor onChange={this.onValueOptionsChanged} options={options.valueOptions} /> |
||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} /> |
||||
</PanelOptionsGrid> |
||||
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={options.valueMappings} /> |
||||
</> |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,17 @@ |
||||
// Libraries
|
||||
import React from 'react'; |
||||
|
||||
// Types
|
||||
import { SingleStatOptions } from './types'; |
||||
import { DisplayValue } from '@grafana/ui/src/utils/displayValue'; |
||||
import { SingleStatBase } from './SingleStatBase'; |
||||
|
||||
export class SingleStatPanel extends SingleStatBase<SingleStatOptions> { |
||||
renderStat(value: DisplayValue, width: number, height: number) { |
||||
return ( |
||||
<div style={{ width, height }}> |
||||
<b>{value.text}</b> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 4.9 KiB |
@ -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<SingleStatOptions>(SingleStatPanel); |
||||
|
||||
const optionsToKeep = ['valueOptions', 'stat', 'maxValue', 'maxValue', 'thresholds', 'valueMappings']; |
||||
|
||||
export const singleStatOptionsCheck = ( |
||||
options: Partial<SingleStatOptions>, |
||||
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); |
||||
@ -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" |
||||
} |
||||
} |
||||
} |
||||
@ -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, |
||||
}; |
||||
Loading…
Reference in new issue