diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx new file mode 100644 index 00000000000..6b704148ee2 --- /dev/null +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx @@ -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 { + 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 ( +
+
{valueFormatted}
+
+
+ ); + } +} diff --git a/packages/grafana-ui/src/components/BarGauge/_BarGauge.scss b/packages/grafana-ui/src/components/BarGauge/_BarGauge.scss new file mode 100644 index 00000000000..9e43439f0a2 --- /dev/null +++ b/packages/grafana-ui/src/components/BarGauge/_BarGauge.scss @@ -0,0 +1,9 @@ +.bar-gauge { + display: flex; + flex-direction: column; + justify-content: flex-end; +} + +.bar-gauge__value { + text-align: center; +} diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index a475a7ccc1b..25d1b6f35af 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -9,3 +9,4 @@ @import 'ValueMappingsEditor/ValueMappingsEditor'; @import 'EmptySearchResult/EmptySearchResult'; @import 'FormField/FormField'; +@import 'BarGauge/BarGauge'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 86ce9347dad..2cdf3ffe6f6 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -17,10 +17,13 @@ export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder'; export { ColorPicker, SeriesColorPicker } from './ColorPicker/ColorPicker'; export { SeriesColorPickerPopover, SeriesColorPickerPopoverWithTheme } from './ColorPicker/SeriesColorPickerPopover'; export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor'; -export { Graph } from './Graph/Graph'; export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup'; export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid'; export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor'; -export { Gauge } from './Gauge/Gauge'; export { Switch } from './Switch/Switch'; export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult'; + +// Visualizations +export { Gauge } from './Gauge/Gauge'; +export { Graph } from './Graph/Graph'; +export { BarGauge } from './BarGauge/BarGauge'; diff --git a/public/app/features/dashboard/dashgrid/PanelPluginNotFound.tsx b/public/app/features/dashboard/dashgrid/PanelPluginNotFound.tsx index 3f835bdbac2..50d9e1afdae 100644 --- a/public/app/features/dashboard/dashgrid/PanelPluginNotFound.tsx +++ b/public/app/features/dashboard/dashgrid/PanelPluginNotFound.tsx @@ -2,9 +2,12 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; +// Components +import { AlertBox } from 'app/core/components/AlertBox/AlertBox'; + // Types import { PanelProps } from '@grafana/ui'; -import { PanelPlugin } from 'app/types'; +import { PanelPlugin, AppNotificationSeverity } from 'app/types'; interface Props { pluginId: string; @@ -19,15 +22,13 @@ class PanelPluginNotFound extends PureComponent { const style = { display: 'flex', alignItems: 'center', - textAlign: 'center' as 'center', + justifyContent: 'center', height: '100%', }; return (
-
- Panel plugin with id {this.props.pluginId} could not be found -
+
); } diff --git a/public/app/features/dashboard/panel_editor/VisualizationTab.tsx b/public/app/features/dashboard/panel_editor/VisualizationTab.tsx index f9d8b3df607..c40286beb21 100644 --- a/public/app/features/dashboard/panel_editor/VisualizationTab.tsx +++ b/public/app/features/dashboard/panel_editor/VisualizationTab.tsx @@ -54,10 +54,10 @@ export class VisualizationTab extends PureComponent { const { panel, plugin } = this.props; 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() { diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 078443b019a..7d123c24095 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -26,6 +26,7 @@ import * as tablePanel from 'app/plugins/panel/table/module'; import * as singlestatPanel from 'app/plugins/panel/singlestat/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'; const builtInPlugins = { 'app/plugins/datasource/graphite/module': graphitePlugin, @@ -56,6 +57,7 @@ const builtInPlugins = { 'app/plugins/panel/singlestat/module': singlestatPanel, 'app/plugins/panel/gettingstarted/module': gettingStartedPanel, 'app/plugins/panel/gauge/module': gaugePanel, + 'app/plugins/panel/bargauge/module': barGaugePanel, }; export default builtInPlugins; diff --git a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx new file mode 100644 index 00000000000..534e24f1e25 --- /dev/null +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -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 {} + +export class BarGaugePanel extends PureComponent { + 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 ( + + {theme => ( + + )} + + ); + } +} diff --git a/public/app/plugins/panel/bargauge/module.tsx b/public/app/plugins/panel/bargauge/module.tsx new file mode 100644 index 00000000000..ba074427c41 --- /dev/null +++ b/public/app/plugins/panel/bargauge/module.tsx @@ -0,0 +1,4 @@ +import { BarGaugePanel } from './BarGaugePanel'; +import { PanelDefaults } from './types'; + +export { BarGaugePanel as Panel, PanelDefaults }; diff --git a/public/app/plugins/panel/bargauge/plugin.json b/public/app/plugins/panel/bargauge/plugin.json new file mode 100644 index 00000000000..2c5cdc08a0b --- /dev/null +++ b/public/app/plugins/panel/bargauge/plugin.json @@ -0,0 +1,15 @@ +{ + "type": "panel", + "name": "Bar Gauge", + "id": "bargauge", + + "dataFormats": ["time_series"], + + "info": { + "author": { + "name": "Grafana Project", + "url": "https://grafana.com" + }, + "logos": {} + } +} diff --git a/public/app/plugins/panel/bargauge/types.ts b/public/app/plugins/panel/bargauge/types.ts new file mode 100644 index 00000000000..0e3ee4c25cd --- /dev/null +++ b/public/app/plugins/panel/bargauge/types.ts @@ -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', +}; diff --git a/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx index 50e2a344a9b..d89560806a8 100644 --- a/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx @@ -1,9 +1,8 @@ import React, { PureComponent } from 'react'; import { FormField, PanelOptionsProps, PanelOptionsGroup, Switch } from '@grafana/ui'; - import { GaugeOptions } from './types'; -export default class GaugeOptionsEditor extends PureComponent> { +export class GaugeOptionsEditor extends PureComponent> { onToggleThresholdLabels = () => this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index 84726ac88bf..457ba0865de 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -1,36 +1,16 @@ +// Libraries import React, { PureComponent } from 'react'; -import { - PanelOptionsProps, - ThresholdsEditor, - Threshold, - PanelOptionsGrid, - ValueMappingsEditor, - ValueMapping, -} from '@grafana/ui'; -import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; -import GaugeOptionsEditor from './GaugeOptionsEditor'; -import { GaugeOptions } from './types'; - -export const defaultProps = { - options: { - minValue: 0, - maxValue: 100, - prefix: '', - showThresholdMarkers: true, - showThresholdLabels: false, - suffix: '', - decimals: 0, - stat: 'avg', - unit: 'none', - valueMappings: [], - thresholds: [], - }, -}; +// Components +import { ValueOptions } from 'app/plugins/panel/gauge/ValueOptions'; +import { GaugeOptionsEditor } from './GaugeOptionsEditor'; +import { ThresholdsEditor, ValueMappingsEditor } from '@grafana/ui'; -export default class GaugePanelOptions extends PureComponent> { - static defaultProps = defaultProps; +// Types +import { PanelOptionsProps, Threshold, PanelOptionsGrid, ValueMapping } from '@grafana/ui'; +import { GaugeOptions } from './types'; +export class GaugePanelOptions extends PureComponent> { onThresholdsChanged = (thresholds: Threshold[]) => this.props.onChange({ ...this.props.options, diff --git a/public/app/plugins/panel/gauge/ValueOptions.tsx b/public/app/plugins/panel/gauge/ValueOptions.tsx index 1fdccadddf2..c778bbd8e36 100644 --- a/public/app/plugins/panel/gauge/ValueOptions.tsx +++ b/public/app/plugins/panel/gauge/ValueOptions.tsx @@ -19,7 +19,7 @@ const statOptions = [ const labelWidth = 6; -export default class ValueOptions extends PureComponent> { +export class ValueOptions extends PureComponent> { onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value }); onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value }); diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx index 783e4825657..3323fdfd5cb 100644 --- a/public/app/plugins/panel/gauge/module.tsx +++ b/public/app/plugins/panel/gauge/module.tsx @@ -1,4 +1,5 @@ -import GaugePanelOptions, { defaultProps } from './GaugePanelOptions'; +import { GaugePanelOptions } from './GaugePanelOptions'; 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 }; diff --git a/public/app/plugins/panel/gauge/types.ts b/public/app/plugins/panel/gauge/types.ts index 42262178dc8..4cdb4fcdd7b 100644 --- a/public/app/plugins/panel/gauge/types.ts +++ b/public/app/plugins/panel/gauge/types.ts @@ -13,3 +13,17 @@ export interface GaugeOptions { thresholds: Threshold[]; unit: string; } + +export const PanelDefaults: GaugeOptions = { + minValue: 0, + maxValue: 100, + prefix: '', + showThresholdMarkers: true, + showThresholdLabels: false, + suffix: '', + decimals: 0, + stat: 'avg', + unit: 'none', + valueMappings: [], + thresholds: [], +};