mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Began experimenting with a bar gauge
This commit is contained in:
parent
2d5fd7fdfd
commit
83d1eb87e5
59
packages/grafana-ui/src/components/BarGauge/BarGauge.tsx
Normal file
59
packages/grafana-ui/src/components/BarGauge/BarGauge.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
.bar-gauge {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-gauge__value {
|
||||||
|
text-align: center;
|
||||||
|
}
|
@ -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';
|
|||||||
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 @@
|
|||||||
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> {
|
|||||||
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> {
|
|||||||
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';
|
|||||||
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 = {
|
|||||||
'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;
|
||||||
|
56
public/app/plugins/panel/bargauge/BarGaugePanel.tsx
Normal file
56
public/app/plugins/panel/bargauge/BarGaugePanel.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
4
public/app/plugins/panel/bargauge/module.tsx
Normal file
4
public/app/plugins/panel/bargauge/module.tsx
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { BarGaugePanel } from './BarGaugePanel';
|
||||||
|
import { PanelDefaults } from './types';
|
||||||
|
|
||||||
|
export { BarGaugePanel as Panel, PanelDefaults };
|
15
public/app/plugins/panel/bargauge/plugin.json
Normal file
15
public/app/plugins/panel/bargauge/plugin.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"type": "panel",
|
||||||
|
"name": "Bar Gauge",
|
||||||
|
"id": "bargauge",
|
||||||
|
|
||||||
|
"dataFormats": ["time_series"],
|
||||||
|
|
||||||
|
"info": {
|
||||||
|
"author": {
|
||||||
|
"name": "Grafana Project",
|
||||||
|
"url": "https://grafana.com"
|
||||||
|
},
|
||||||
|
"logos": {}
|
||||||
|
}
|
||||||
|
}
|
17
public/app/plugins/panel/bargauge/types.ts
Normal file
17
public/app/plugins/panel/bargauge/types.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
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',
|
||||||
|
};
|
@ -1,9 +1,8 @@
|
|||||||
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 });
|
||||||
|
|
||||||
|
@ -1,36 +1,16 @@
|
|||||||
|
// 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 { GaugeOptionsEditor } from './GaugeOptionsEditor';
|
||||||
|
import { ThresholdsEditor, ValueMappingsEditor } from '@grafana/ui';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { PanelOptionsProps, Threshold, PanelOptionsGrid, ValueMapping } from '@grafana/ui';
|
||||||
import { GaugeOptions } from './types';
|
import { GaugeOptions } from './types';
|
||||||
|
|
||||||
export const defaultProps = {
|
export class GaugePanelOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
|
||||||
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>> {
|
|
||||||
static defaultProps = defaultProps;
|
|
||||||
|
|
||||||
onThresholdsChanged = (thresholds: Threshold[]) =>
|
onThresholdsChanged = (thresholds: Threshold[]) =>
|
||||||
this.props.onChange({
|
this.props.onChange({
|
||||||
...this.props.options,
|
...this.props.options,
|
||||||
|
@ -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 });
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
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 {
|
|||||||
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: [],
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user