소스 검색

Began experimenting with a bar gauge

Torkel Ödegaard 6 년 전
부모
커밋
843f8b04d4

+ 59 - 0
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<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>
+    );
+  }
+}

+ 9 - 0
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;
+}

+ 1 - 0
packages/grafana-ui/src/components/index.scss

@@ -9,3 +9,4 @@
 @import 'ValueMappingsEditor/ValueMappingsEditor';
 @import 'EmptySearchResult/EmptySearchResult';
 @import 'FormField/FormField';
+@import 'BarGauge/BarGauge';

+ 5 - 2
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';

+ 6 - 5
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<Props> {
     const style = {
       display: 'flex',
       alignItems: 'center',
-      textAlign: 'center' as 'center',
+      justifyContent: 'center',
       height: '100%',
     };
 
     return (
       <div style={style}>
-        <div className="alert alert-error" style={{ margin: '0 auto' }}>
-          Panel plugin with id {this.props.pluginId} could not be found
-        </div>
+        <AlertBox severity={AppNotificationSeverity.Error} title={`Panel plugin not found: ${this.props.pluginId})`} />
       </div>
     );
   }

+ 2 - 2
public/app/features/dashboard/panel_editor/VisualizationTab.tsx

@@ -54,10 +54,10 @@ export class VisualizationTab extends PureComponent<Props, State> {
     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() {

+ 2 - 0
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;

+ 56 - 0
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<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 - 0
public/app/plugins/panel/bargauge/module.tsx

@@ -0,0 +1,4 @@
+import { BarGaugePanel } from './BarGaugePanel';
+import { PanelDefaults } from './types';
+
+export { BarGaugePanel as Panel, PanelDefaults };

+ 15 - 0
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": {}
+  }
+}

+ 17 - 0
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',
+};

+ 1 - 2
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<PanelOptionsProps<GaugeOptions>> {
+export class GaugeOptionsEditor extends PureComponent<PanelOptionsProps<GaugeOptions>> {
   onToggleThresholdLabels = () =>
     this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels });
 

+ 9 - 29
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<PanelOptionsProps<GaugeOptions>> {
-  static defaultProps = defaultProps;
+// Types
+import { PanelOptionsProps, Threshold, PanelOptionsGrid, ValueMapping } from '@grafana/ui';
+import { GaugeOptions } from './types';
 
+export class GaugePanelOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
   onThresholdsChanged = (thresholds: Threshold[]) =>
     this.props.onChange({
       ...this.props.options,

+ 1 - 1
public/app/plugins/panel/gauge/ValueOptions.tsx

@@ -19,7 +19,7 @@ const statOptions = [
 
 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 });
 
   onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value });

+ 3 - 2
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 };

+ 14 - 0
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: [],
+};