revert most options sharing

This commit is contained in:
ryan
2019-03-14 13:20:24 -07:00
parent 8299c95459
commit 985f057ab3
22 changed files with 360 additions and 274 deletions

View File

@@ -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
//

View File

@@ -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,

View File

@@ -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<BarGaugeOptions> {
getOrientation(): VizOrientation {
const { options } = this.props;
return options.orientation;
}
import { SingleStatBase } from '../singlestat2/SingleStatBase';
export class BarGaugePanel extends SingleStatBase<BarGaugeOptions> {
renderStat(value: DisplayValue, width: number, height: number) {
const { options } = this.props;
const { display } = options;
return (
<BarGauge
@@ -28,7 +22,7 @@ export class BarGaugePanel extends SingleStatPanel<BarGaugeOptions> {
width={width}
height={height}
orientation={options.orientation}
thresholds={display.thresholds}
thresholds={options.thresholds}
theme={config.theme}
/>
);

View File

@@ -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<PanelEditorProps<BarGaugeOptions>> {
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<PanelEditorProps<BarGauge
onOrientationChange = ({ value }) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
render() {
const { onOptionsChange, options } = this.props;
const { display } = options;
const { options } = this.props;
return (
<>
<PanelOptionsGrid>
{/* This just sets the 'stats', that should be moved to somethign more general */}
<SingleStatValueEditor onChange={onOptionsChange} options={options} />
<DisplayValueEditor onChange={this.onDisplayOptionsChanged} options={display} />
<SingleStatValueEditor onChange={this.onValueOptionsChanged} options={options.valueOptions} />
<PanelOptionsGroup title="Gauge">
<FormField label="Min value" labelWidth={8} onChange={this.onMinValueChange} value={options.minValue} />
<FormField label="Max value" labelWidth={8} onChange={this.onMaxValueChange} value={options.maxValue} />
@@ -66,9 +54,10 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
/>
</div>
</PanelOptionsGroup>
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={display.thresholds} />
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={display.mappings} />
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
</PanelOptionsGrid>
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={options.valueMappings} />
</>
);
}

View File

@@ -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<BarGaugeOptions>(BarGaugePanel);
reactPanel.setEditor(BarGaugePanelEditor);
reactPanel.setDefaults(defaults);
reactPanel.setPanelTypeChangedHook(gaugePanelTypeChangedHook);
reactPanel.setPanelTypeChangedHook(singleStatOptionsCheck);

View File

@@ -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: [],
};

View File

@@ -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<GaugeOptions> {
export class GaugePanel extends SingleStatBase<GaugeOptions> {
renderStat(value: DisplayValue, width: number, height: number) {
const { options } = this.props;
const { display } = options;
return (
<Gauge
value={value}
width={width}
height={height}
thresholds={display.thresholds}
thresholds={options.thresholds}
showThresholdLabels={options.showThresholdLabels}
showThresholdMarkers={options.showThresholdMarkers}
minValue={options.minValue}

View File

@@ -9,46 +9,42 @@ import {
ValueMapping,
} from '@grafana/ui';
import { SingleStatValueEditor } from 'app/plugins/panel/gauge/SingleStatValueEditor';
import { GaugeOptionsBox } from './GaugeOptionsBox';
import { GaugeOptions } from './types';
import { DisplayValueEditor } from './DisplayValueEditor';
import { DisplayValueOptions } from '@grafana/ui';
import { SingleStatValueEditor } from '../singlestat2/SingleStatValueEditor';
import { SingleStatValueOptions } from '../singlestat2/types';
export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {
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 (
<>
<PanelOptionsGrid>
{/* This just sets the 'stats', that should be moved to somethign more general */}
<SingleStatValueEditor onChange={onOptionsChange} options={options} />
<DisplayValueEditor onChange={this.onDisplayOptionsChanged} options={display} />
<SingleStatValueEditor onChange={this.onValueOptionsChanged} options={options.valueOptions} />
<GaugeOptionsBox onOptionsChange={onOptionsChange} options={options} />
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={display.thresholds} />
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
</PanelOptionsGrid>
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={display.mappings} />
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={options.valueMappings} />
</>
);
}

View File

@@ -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>
);
}
}

View File

@@ -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",
}
`;

View File

@@ -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();
});
});
});

View File

@@ -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);

View File

@@ -1,6 +1,7 @@
import { SingleStatOptions } from '@grafana/ui';
import { SingleStatBaseOptions } from '../singlestat2/types';
import { VizOrientation } from '@grafana/ui';
export interface GaugeOptions extends SingleStatOptions {
export interface GaugeOptions extends SingleStatBaseOptions {
maxValue: number;
minValue: number;
showThresholdLabels: boolean;
@@ -12,14 +13,14 @@ export const defaults: GaugeOptions = {
maxValue: 100,
showThresholdMarkers: true,
showThresholdLabels: false,
stat: 'avg',
display: {
valueOptions: {
prefix: '',
suffix: '',
decimals: null,
stat: 'avg',
unit: 'none',
mappings: [],
thresholds: [{ index: 0, value: -Infinity, color: 'green' }, { index: 1, value: 80, color: 'red' }],
},
valueMappings: [],
thresholds: [{ index: 0, value: -Infinity, color: 'green' }, { index: 1, value: 80, color: 'red' }],
orientation: VizOrientation.Auto,
};

View File

@@ -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/)

View File

@@ -1,21 +1,16 @@
// Libraries
import React, { PureComponent } from 'react';
// Services & Utils
import { processSingleStatPanelData, SingleStatOptions, DisplayValue, PanelProps, VizOrientation } from '@grafana/ui';
import { processSingleStatPanelData, DisplayValue, PanelProps } from '@grafana/ui';
import { config } from 'app/core/config';
// Components
import { VizRepeater, getDisplayProcessor } from '@grafana/ui';
import { SingleStatBaseOptions } from './types';
interface State {
export interface State {
values: DisplayValue[];
}
export class SingleStatPanel<T extends SingleStatOptions> extends PureComponent<PanelProps<T>, State> {
export class SingleStatBase<T extends SingleStatBaseOptions> extends PureComponent<PanelProps<T>, State> {
constructor(props: PanelProps<T>) {
super(props);
this.state = {
values: this.findDisplayValues(props),
};
@@ -29,18 +24,20 @@ export class SingleStatPanel<T extends SingleStatOptions> extends PureComponent<
findDisplayValues(props: PanelProps<T>): DisplayValue[] {
const { panelData, replaceVariables, options } = this.props;
const { display } = options;
const { valueOptions, valueMappings } = options;
const processor = getDisplayProcessor({
...display,
prefix: replaceVariables(display.prefix),
suffix: replaceVariables(display.suffix),
unit: valueOptions.unit,
decimals: valueOptions.decimals,
mappings: valueMappings,
thresholds: options.thresholds,
prefix: replaceVariables(valueOptions.prefix),
suffix: replaceVariables(valueOptions.suffix),
theme: config.theme,
});
return processSingleStatPanelData({
panelData: panelData,
stat: options.stat,
stat: valueOptions.stat,
}).map(stat => processor(stat.value));
}
@@ -51,17 +48,12 @@ export class SingleStatPanel<T extends SingleStatOptions> extends PureComponent<
return <div style={{ width, height, border: '1px solid red' }}>{value.text}</div>;
}
// Or we could add this to single stat props?
getOrientation(): VizOrientation {
return VizOrientation.Auto;
}
render() {
const { height, width } = this.props;
const { height, width, options } = this.props;
const { orientation } = options;
const { values } = this.state;
return (
<VizRepeater height={height} width={width} values={values} orientation={this.getOrientation()}>
<VizRepeater height={height} width={width} values={values} orientation={orientation}>
{({ vizHeight, vizWidth, value }) => this.renderStat(value, vizWidth, vizHeight)}
</VizRepeater>
);

View File

@@ -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} />
</>
);
}
}

View File

@@ -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>
);
}
}

View File

@@ -2,20 +2,35 @@
import React, { PureComponent } from 'react';
// Components
import { FormField, FormLabel, PanelOptionsGroup, UnitPicker } from '@grafana/ui';
import { FormField, FormLabel, PanelOptionsGroup, Select, UnitPicker } from '@grafana/ui';
// Types
import { DisplayValueOptions } from '@grafana/ui';
import { SingleStatValueOptions } 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: DisplayValueOptions;
onChange: (options: DisplayValueOptions) => void;
options: SingleStatValueOptions;
onChange: (valueOptions: SingleStatValueOptions) => void;
}
export class DisplayValueEditor extends PureComponent<Props> {
export class SingleStatValueEditor extends PureComponent<Props> {
onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value });
onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value });
onDecimalChange = event => {
if (!isNaN(event.target.value)) {
@@ -35,7 +50,7 @@ export class DisplayValueEditor extends PureComponent<Props> {
onSuffixChange = event => this.props.onChange({ ...this.props.options, suffix: event.target.value });
render() {
const { unit, decimals, prefix, suffix } = this.props.options;
const { stat, unit, decimals, prefix, suffix } = this.props.options;
let decimalsString = '';
if (Number.isFinite(decimals)) {
@@ -43,7 +58,16 @@ export class DisplayValueEditor extends PureComponent<Props> {
}
return (
<PanelOptionsGroup title="Display Value">
<PanelOptionsGroup title="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>
<div className="gf-form">
<FormLabel width={labelWidth}>Unit</FormLabel>
<UnitPicker defaultValue={unit} onChange={this.onUnitChange} />

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100px" height="100px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.26;fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_2_);}
.st2{fill:url(#SVGID_3_);}
.st3{fill:url(#SVGID_4_);}
.st4{fill:url(#SVGID_5_);}
.st5{fill:none;stroke:url(#SVGID_6_);stroke-miterlimit:10;}
</style>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="50" y1="65.6698" x2="50" y2="93.5681">
<stop offset="0" style="stop-color:#FFF23A"/>
<stop offset="4.010540e-02" style="stop-color:#FEE62D"/>
<stop offset="0.1171" style="stop-color:#FED41A"/>
<stop offset="0.1964" style="stop-color:#FDC90F"/>
<stop offset="0.2809" style="stop-color:#FDC60B"/>
<stop offset="0.6685" style="stop-color:#F28F3F"/>
<stop offset="0.8876" style="stop-color:#ED693C"/>
<stop offset="1" style="stop-color:#E83E39"/>
</linearGradient>
<path class="st0" d="M97.6,83.8H2.4c-1.3,0-2.4-1.1-2.4-2.4v-1.8l17-1l19.2-4.3l16.3-1.6l16.5,0l15.8-4.7l15.1-3v16.3
C100,82.8,98.9,83.8,97.6,83.8z"/>
<g>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="19.098" y1="76.0776" x2="19.098" y2="27.8027">
<stop offset="0" style="stop-color:#FFF23A"/>
<stop offset="4.010540e-02" style="stop-color:#FEE62D"/>
<stop offset="0.1171" style="stop-color:#FED41A"/>
<stop offset="0.1964" style="stop-color:#FDC90F"/>
<stop offset="0.2809" style="stop-color:#FDC60B"/>
<stop offset="0.6685" style="stop-color:#F28F3F"/>
<stop offset="0.8876" style="stop-color:#ED693C"/>
<stop offset="1" style="stop-color:#E83E39"/>
</linearGradient>
<path class="st1" d="M19.6,64.3V38.9l-5.2,3.9l-3.5-6l9.4-6.9h6.8v34.4H19.6z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="42.412" y1="76.0776" x2="42.412" y2="27.8027">
<stop offset="0" style="stop-color:#FFF23A"/>
<stop offset="4.010540e-02" style="stop-color:#FEE62D"/>
<stop offset="0.1171" style="stop-color:#FED41A"/>
<stop offset="0.1964" style="stop-color:#FDC90F"/>
<stop offset="0.2809" style="stop-color:#FDC60B"/>
<stop offset="0.6685" style="stop-color:#F28F3F"/>
<stop offset="0.8876" style="stop-color:#ED693C"/>
<stop offset="1" style="stop-color:#E83E39"/>
</linearGradient>
<path class="st2" d="M53.1,39.4c0,1.1-0.1,2.2-0.4,3.2c-0.3,1-0.7,1.9-1.2,2.8c-0.5,0.9-1,1.7-1.7,2.5c-0.6,0.8-1.2,1.6-1.9,2.3
l-6.4,7.4h11.1v6.7H32.3v-6.9l10.5-12c0.8-1,1.5-2,2-3c0.5-1,0.7-2,0.7-2.9c0-1-0.2-1.9-0.7-2.6c-0.5-0.7-1.2-1.1-2.2-1.1
c-0.9,0-1.7,0.4-2.3,1.1c-0.6,0.8-1,1.9-1.1,3.3l-7.3-0.7c0.4-3.5,1.6-6.1,3.6-7.9c2-1.7,4.5-2.6,7.4-2.6c1.6,0,3,0.2,4.3,0.7
c1.3,0.5,2.3,1.2,3.2,2c0.9,0.9,1.6,1.9,2.1,3.2C52.8,36.4,53.1,37.8,53.1,39.4z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="60.3739" y1="76.0776" x2="60.3739" y2="27.8027">
<stop offset="0" style="stop-color:#FFF23A"/>
<stop offset="4.010540e-02" style="stop-color:#FEE62D"/>
<stop offset="0.1171" style="stop-color:#FED41A"/>
<stop offset="0.1964" style="stop-color:#FDC90F"/>
<stop offset="0.2809" style="stop-color:#FDC60B"/>
<stop offset="0.6685" style="stop-color:#F28F3F"/>
<stop offset="0.8876" style="stop-color:#ED693C"/>
<stop offset="1" style="stop-color:#E83E39"/>
</linearGradient>
<path class="st3" d="M64.5,60.4c0,1.2-0.4,2.3-1.2,3.1c-0.8,0.8-1.8,1.3-3,1.3c-1.2,0-2.2-0.4-3-1.3c-0.8-0.8-1.1-1.9-1.1-3.1
c0-1.2,0.4-2.2,1.1-3.1c0.8-0.9,1.8-1.3,3-1.3c1.2,0,2.2,0.4,3,1.3C64.1,58.1,64.5,59.2,64.5,60.4z"/>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="77.5234" y1="76.0776" x2="77.5234" y2="27.8027">
<stop offset="0" style="stop-color:#FFF23A"/>
<stop offset="4.010540e-02" style="stop-color:#FEE62D"/>
<stop offset="0.1171" style="stop-color:#FED41A"/>
<stop offset="0.1964" style="stop-color:#FDC90F"/>
<stop offset="0.2809" style="stop-color:#FDC60B"/>
<stop offset="0.6685" style="stop-color:#F28F3F"/>
<stop offset="0.8876" style="stop-color:#ED693C"/>
<stop offset="1" style="stop-color:#E83E39"/>
</linearGradient>
<path class="st4" d="M85.5,57.4v6.9h-6.9v-6.9H66v-6.6l10.1-20.9h9.4V51H89v6.4H85.5z M78.8,37.5L78.8,37.5l-6,13.5h6V37.5z"/>
</g>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="-2.852199e-02" y1="72.3985" x2="100.0976" y2="72.3985">
<stop offset="0" style="stop-color:#F28F3F"/>
<stop offset="1" style="stop-color:#F28F3F"/>
</linearGradient>
<polyline class="st5" points="0,79.7 17,78.7 36.2,74.4 52.5,72.8 69,72.9 84.9,68.1 100,65.1 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -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);

View File

@@ -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"
}
}
}

View File

@@ -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,
};