Commit 843f8b04 by Torkel Ödegaard

Began experimenting with a bar gauge

parent 3d3db08b
// Library
import React, { PureComponent } from 'react';
// Utils
import { getValueFormat } from '../../utils';
// Types
import { Themeable, TimeSeriesValue } from '../../types';
export interface Props extends Themeable {
height: number;
unit: string;
width: number;
value: TimeSeriesValue;
prefix: string;
suffix: string;
maxValue: number;
minValue: number;
}
export class BarGauge extends PureComponent<Props> {
static defaultProps = {
maxValue: 100,
minValue: 0,
unit: 'none',
};
getNumericValue(): number {
if (Number.isFinite(this.props.value as number)) {
return this.props.value as number;
}
return 0;
}
render() {
const { height, width, maxValue, minValue, unit } = this.props;
const numericValue = this.getNumericValue();
const barMaxHeight = height * 0.8; // 20% for value & name
const valuePercent = numericValue / (maxValue - minValue);
const barHeight = valuePercent * barMaxHeight;
const formatFunc = getValueFormat(unit);
const valueFormatted = formatFunc(numericValue);
return (
<div className="bar-gauge" style={{ width: `${width}px`, height: `${height}px` }}>
<div className="bar-gauge__value">{valueFormatted}</div>
<div
style={{
height: `${barHeight}px`,
width: `${width}px`,
backgroundColor: 'rgba(200,0,0,0.3)',
}}
/>
</div>
);
}
}
.bar-gauge {
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.bar-gauge__value {
text-align: center;
}
...@@ -9,3 +9,4 @@ ...@@ -9,3 +9,4 @@
@import 'ValueMappingsEditor/ValueMappingsEditor'; @import 'ValueMappingsEditor/ValueMappingsEditor';
@import 'EmptySearchResult/EmptySearchResult'; @import 'EmptySearchResult/EmptySearchResult';
@import 'FormField/FormField'; @import 'FormField/FormField';
@import 'BarGauge/BarGauge';
...@@ -17,10 +17,13 @@ export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder'; ...@@ -17,10 +17,13 @@ export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder';
export { ColorPicker, SeriesColorPicker } from './ColorPicker/ColorPicker'; export { ColorPicker, SeriesColorPicker } from './ColorPicker/ColorPicker';
export { SeriesColorPickerPopover, SeriesColorPickerPopoverWithTheme } from './ColorPicker/SeriesColorPickerPopover'; export { SeriesColorPickerPopover, SeriesColorPickerPopoverWithTheme } from './ColorPicker/SeriesColorPickerPopover';
export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor'; export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor';
export { Graph } from './Graph/Graph';
export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup'; export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup';
export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid'; export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid';
export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor'; export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor';
export { Gauge } from './Gauge/Gauge';
export { Switch } from './Switch/Switch'; export { Switch } from './Switch/Switch';
export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult'; export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
// Visualizations
export { Gauge } from './Gauge/Gauge';
export { Graph } from './Graph/Graph';
export { BarGauge } from './BarGauge/BarGauge';
...@@ -2,9 +2,12 @@ ...@@ -2,9 +2,12 @@
import _ from 'lodash'; import _ from 'lodash';
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
// Components
import { AlertBox } from 'app/core/components/AlertBox/AlertBox';
// Types // Types
import { PanelProps } from '@grafana/ui'; import { PanelProps } from '@grafana/ui';
import { PanelPlugin } from 'app/types'; import { PanelPlugin, AppNotificationSeverity } from 'app/types';
interface Props { interface Props {
pluginId: string; pluginId: string;
...@@ -19,15 +22,13 @@ class PanelPluginNotFound extends PureComponent<Props> { ...@@ -19,15 +22,13 @@ class PanelPluginNotFound extends PureComponent<Props> {
const style = { const style = {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
textAlign: 'center' as 'center', justifyContent: 'center',
height: '100%', height: '100%',
}; };
return ( return (
<div style={style}> <div style={style}>
<div className="alert alert-error" style={{ margin: '0 auto' }}> <AlertBox severity={AppNotificationSeverity.Error} title={`Panel plugin not found: ${this.props.pluginId})`} />
Panel plugin with id {this.props.pluginId} could not be found
</div>
</div> </div>
); );
} }
......
...@@ -54,10 +54,10 @@ export class VisualizationTab extends PureComponent<Props, State> { ...@@ -54,10 +54,10 @@ export class VisualizationTab extends PureComponent<Props, State> {
const { panel, plugin } = this.props; const { panel, plugin } = this.props;
if (plugin.exports.PanelDefaults) { if (plugin.exports.PanelDefaults) {
return panel.getOptions(plugin.exports.PanelDefaults.options); return panel.getOptions(plugin.exports.PanelDefaults);
} }
return panel.getOptions(plugin.exports.PanelDefaults); return panel.getOptions({});
}; };
renderPanelOptions() { renderPanelOptions() {
......
...@@ -26,6 +26,7 @@ import * as tablePanel from 'app/plugins/panel/table/module'; ...@@ -26,6 +26,7 @@ import * as tablePanel from 'app/plugins/panel/table/module';
import * as singlestatPanel from 'app/plugins/panel/singlestat/module'; import * as singlestatPanel from 'app/plugins/panel/singlestat/module';
import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module'; import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module';
import * as gaugePanel from 'app/plugins/panel/gauge/module'; import * as gaugePanel from 'app/plugins/panel/gauge/module';
import * as barGaugePanel from 'app/plugins/panel/bargauge/module';
const builtInPlugins = { const builtInPlugins = {
'app/plugins/datasource/graphite/module': graphitePlugin, 'app/plugins/datasource/graphite/module': graphitePlugin,
...@@ -56,6 +57,7 @@ const builtInPlugins = { ...@@ -56,6 +57,7 @@ const builtInPlugins = {
'app/plugins/panel/singlestat/module': singlestatPanel, 'app/plugins/panel/singlestat/module': singlestatPanel,
'app/plugins/panel/gettingstarted/module': gettingStartedPanel, 'app/plugins/panel/gettingstarted/module': gettingStartedPanel,
'app/plugins/panel/gauge/module': gaugePanel, 'app/plugins/panel/gauge/module': gaugePanel,
'app/plugins/panel/bargauge/module': barGaugePanel,
}; };
export default builtInPlugins; export default builtInPlugins;
// Libraries
import React, { PureComponent } from 'react';
// Services & Utils
import { processTimeSeries, ThemeContext } from '@grafana/ui';
// Components
import { BarGauge } from '@grafana/ui';
// Types
import { BarGaugeOptions } from './types';
import { PanelProps, NullValueMode, TimeSeriesValue } from '@grafana/ui/src/types';
interface Props extends PanelProps<BarGaugeOptions> {}
export class BarGaugePanel extends PureComponent<Props> {
render() {
const { panelData, width, height, onInterpolate, options } = this.props;
const prefix = onInterpolate(options.prefix);
const suffix = onInterpolate(options.suffix);
let value: TimeSeriesValue;
if (panelData.timeSeries) {
const vmSeries = processTimeSeries({
timeSeries: panelData.timeSeries,
nullValueMode: NullValueMode.Null,
});
if (vmSeries[0]) {
value = vmSeries[0].stats[options.stat];
} else {
value = null;
}
} else if (panelData.tableData) {
value = panelData.tableData.rows[0].find(prop => prop > 0);
}
return (
<ThemeContext.Consumer>
{theme => (
<BarGauge
value={value}
{...this.props.options}
width={width}
height={height}
prefix={prefix}
suffix={suffix}
theme={theme}
/>
)}
</ThemeContext.Consumer>
);
}
}
import { BarGaugePanel } from './BarGaugePanel';
import { PanelDefaults } from './types';
export { BarGaugePanel as Panel, PanelDefaults };
{
"type": "panel",
"name": "Bar Gauge",
"id": "bargauge",
"dataFormats": ["time_series"],
"info": {
"author": {
"name": "Grafana Project",
"url": "https://grafana.com"
},
"logos": {}
}
}
export interface BarGaugeOptions {
minValue: number;
maxValue: number;
prefix: string;
stat: string;
suffix: string;
unit: string;
}
export const PanelDefaults: BarGaugeOptions = {
minValue: 0,
maxValue: 100,
prefix: '',
suffix: '',
stat: 'avg',
unit: 'none',
};
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { FormField, PanelOptionsProps, PanelOptionsGroup, Switch } from '@grafana/ui'; import { FormField, PanelOptionsProps, PanelOptionsGroup, Switch } from '@grafana/ui';
import { GaugeOptions } from './types'; import { GaugeOptions } from './types';
export default class GaugeOptionsEditor extends PureComponent<PanelOptionsProps<GaugeOptions>> { export class GaugeOptionsEditor extends PureComponent<PanelOptionsProps<GaugeOptions>> {
onToggleThresholdLabels = () => onToggleThresholdLabels = () =>
this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels });
......
// Libraries
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import {
PanelOptionsProps,
ThresholdsEditor,
Threshold,
PanelOptionsGrid,
ValueMappingsEditor,
ValueMapping,
} from '@grafana/ui';
import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; // Components
import GaugeOptionsEditor from './GaugeOptionsEditor'; import { ValueOptions } from 'app/plugins/panel/gauge/ValueOptions';
import { GaugeOptions } from './types'; import { GaugeOptionsEditor } from './GaugeOptionsEditor';
import { ThresholdsEditor, ValueMappingsEditor } from '@grafana/ui';
export const defaultProps = {
options: {
minValue: 0,
maxValue: 100,
prefix: '',
showThresholdMarkers: true,
showThresholdLabels: false,
suffix: '',
decimals: 0,
stat: 'avg',
unit: 'none',
valueMappings: [],
thresholds: [],
},
};
export default class GaugePanelOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> { // Types
static defaultProps = defaultProps; import { PanelOptionsProps, Threshold, PanelOptionsGrid, ValueMapping } from '@grafana/ui';
import { GaugeOptions } from './types';
export class GaugePanelOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
onThresholdsChanged = (thresholds: Threshold[]) => onThresholdsChanged = (thresholds: Threshold[]) =>
this.props.onChange({ this.props.onChange({
...this.props.options, ...this.props.options,
......
...@@ -19,7 +19,7 @@ const statOptions = [ ...@@ -19,7 +19,7 @@ const statOptions = [
const labelWidth = 6; const labelWidth = 6;
export default class ValueOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> { export class ValueOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value }); onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value });
onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value }); onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value });
......
import GaugePanelOptions, { defaultProps } from './GaugePanelOptions'; import { GaugePanelOptions } from './GaugePanelOptions';
import { GaugePanel } from './GaugePanel'; import { GaugePanel } from './GaugePanel';
import { PanelDefaults } from './types';
export { GaugePanel as Panel, GaugePanelOptions as PanelOptions, defaultProps as PanelDefaults }; export { GaugePanel as Panel, GaugePanelOptions as PanelOptions, PanelDefaults };
...@@ -13,3 +13,17 @@ export interface GaugeOptions { ...@@ -13,3 +13,17 @@ export interface GaugeOptions {
thresholds: Threshold[]; thresholds: Threshold[];
unit: string; unit: string;
} }
export const PanelDefaults: GaugeOptions = {
minValue: 0,
maxValue: 100,
prefix: '',
showThresholdMarkers: true,
showThresholdLabels: false,
suffix: '',
decimals: 0,
stat: 'avg',
unit: 'none',
valueMappings: [],
thresholds: [],
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment