Procházet zdrojové kódy

Merge branch 'gauge-multi-series' into bar-gauge-poc

Peter Holmberg před 6 roky
rodič
revize
0de9c03879

+ 296 - 0
devenv/dev-dashboards/panel_tests_multiseries_gauge.json

@@ -0,0 +1,296 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": "-- Grafana --",
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "type": "dashboard"
+      }
+    ]
+  },
+  "editable": true,
+  "gnetId": null,
+  "graphTooltip": 0,
+  "links": [],
+  "panels": [
+    {
+      "datasource": "gdev-testdata",
+      "gridPos": {
+        "h": 8,
+        "w": 24,
+        "x": 0,
+        "y": 0
+      },
+      "id": 6,
+      "links": [],
+      "options-gauge": {
+        "decimals": 0,
+        "maxValue": 100,
+        "minValue": 0,
+        "options": {
+          "decimals": 0,
+          "maxValue": 100,
+          "minValue": 0,
+          "prefix": "",
+          "showThresholdLabels": false,
+          "showThresholdMarkers": true,
+          "stat": "avg",
+          "suffix": "",
+          "thresholds": [],
+          "unit": "none",
+          "valueMappings": []
+        },
+        "prefix": "",
+        "showThresholdLabels": false,
+        "showThresholdMarkers": true,
+        "stat": "avg",
+        "suffix": "",
+        "thresholds": [
+          {
+            "color": "#1F78C1",
+            "index": 5,
+            "value": 96.875
+          },
+          {
+            "color": "#E24D42",
+            "index": 4,
+            "value": 93.75
+          },
+          {
+            "color": "#EF843C",
+            "index": 3,
+            "value": 87.5
+          },
+          {
+            "color": "#6ED0E0",
+            "index": 2,
+            "value": 75
+          },
+          {
+            "color": "#EAB839",
+            "index": 1,
+            "value": 50
+          },
+          {
+            "color": "#7EB26D",
+            "index": 0,
+            "value": null
+          }
+        ],
+        "unit": "none",
+        "valueMappings": [
+          {
+            "from": "50",
+            "id": 1,
+            "operator": "",
+            "text": "Hello :) ",
+            "to": "90",
+            "type": 2,
+            "value": ""
+          }
+        ]
+      },
+      "targets": [
+        {
+          "refId": "A",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "B",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "C",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "D",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "E",
+          "scenarioId": "random_walk"
+        }
+      ],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Horizontal with range variable",
+      "type": "gauge"
+    },
+    {
+      "datasource": "gdev-testdata",
+      "gridPos": {
+        "h": 8,
+        "w": 24,
+        "x": 0,
+        "y": 8
+      },
+      "id": 2,
+      "links": [],
+      "options-gauge": {
+        "decimals": 0,
+        "maxValue": 100,
+        "minValue": 0,
+        "options": {
+          "decimals": 0,
+          "maxValue": 100,
+          "minValue": 0,
+          "prefix": "",
+          "showThresholdLabels": false,
+          "showThresholdMarkers": true,
+          "stat": "avg",
+          "suffix": "",
+          "thresholds": [],
+          "unit": "none",
+          "valueMappings": []
+        },
+        "prefix": "",
+        "showThresholdLabels": false,
+        "showThresholdMarkers": true,
+        "stat": "avg",
+        "suffix": "",
+        "thresholds": [
+          {
+            "color": "#EAB839",
+            "index": 1,
+            "value": 50
+          },
+          {
+            "color": "#7EB26D",
+            "index": 0,
+            "value": null
+          }
+        ],
+        "unit": "none",
+        "valueMappings": []
+      },
+      "targets": [
+        {
+          "refId": "A",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "B",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "C",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "D",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "E",
+          "scenarioId": "random_walk"
+        }
+      ],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Repeat horizontal",
+      "type": "gauge"
+    },
+    {
+      "datasource": "gdev-testdata",
+      "gridPos": {
+        "h": 14,
+        "w": 5,
+        "x": 0,
+        "y": 16
+      },
+      "id": 4,
+      "links": [],
+      "options-gauge": {
+        "decimals": 0,
+        "maxValue": "200",
+        "minValue": 0,
+        "options": {
+          "decimals": 0,
+          "maxValue": 100,
+          "minValue": 0,
+          "prefix": "",
+          "showThresholdLabels": false,
+          "showThresholdMarkers": true,
+          "stat": "avg",
+          "suffix": "",
+          "thresholds": [],
+          "unit": "none",
+          "valueMappings": []
+        },
+        "prefix": "",
+        "showThresholdLabels": false,
+        "showThresholdMarkers": true,
+        "stat": "max",
+        "suffix": "",
+        "thresholds": [
+          {
+            "color": "#6ED0E0",
+            "index": 2,
+            "value": 75
+          },
+          {
+            "color": "#EAB839",
+            "index": 1,
+            "value": 50
+          },
+          {
+            "color": "#7EB26D",
+            "index": 0,
+            "value": null
+          }
+        ],
+        "unit": "none",
+        "valueMappings": []
+      },
+      "targets": [
+        {
+          "refId": "A",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "B",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "C",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "D",
+          "scenarioId": "random_walk"
+        },
+        {
+          "refId": "E",
+          "scenarioId": "random_walk"
+        }
+      ],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Vertical",
+      "type": "gauge"
+    }
+  ],
+  "schemaVersion": 17,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": []
+  },
+  "time": {
+    "from": "now-6h",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
+    "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+  },
+  "timezone": "",
+  "title": "Multi series gauges",
+  "uid": "szkuR1umk",
+  "version": 7
+}

+ 9 - 11
packages/grafana-ui/src/components/Gauge/Gauge.tsx

@@ -173,17 +173,15 @@ export class Gauge extends PureComponent<Props> {
     const { height, width } = this.props;
 
     return (
-      <div className="singlestat-panel">
-        <div
-          style={{
-            height: `${height * 0.9}px`,
-            width: `${Math.min(width, height * 1.3)}px`,
-            top: '10px',
-            margin: 'auto',
-          }}
-          ref={element => (this.canvasElement = element)}
-        />
-      </div>
+      <div
+        style={{
+          height: `${height * 0.9}px`,
+          width: `${Math.min(width, height * 1.3)}px`,
+          top: '10px',
+          margin: 'auto',
+        }}
+        ref={element => (this.canvasElement = element)}
+      />
     );
   }
 }

+ 1 - 0
public/app/core/utils/ConfigProvider.tsx

@@ -15,6 +15,7 @@ export const provideConfig = (component: React.ComponentType<any>) => {
 
 export const getCurrentThemeName = () =>
   config.bootData.user.lightTheme ? GrafanaThemeType.Light : GrafanaThemeType.Dark;
+
 export const getCurrentTheme = () => getTheme(getCurrentThemeName());
 
 export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {

+ 6 - 4
public/app/plugins/panel/gauge/GaugeOptionsBox.tsx

@@ -9,6 +9,8 @@ import { FormField, PanelEditorProps } from '@grafana/ui';
 import { GaugeOptions } from './types';
 
 export class GaugeOptionsBox extends PureComponent<PanelEditorProps<GaugeOptions>> {
+  labelWidth = 8;
+
   onToggleThresholdLabels = () =>
     this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels });
 
@@ -25,17 +27,17 @@ export class GaugeOptionsBox extends PureComponent<PanelEditorProps<GaugeOptions
 
     return (
       <PanelOptionsGroup title="Gauge">
-        <FormField label="Min value" labelWidth={8} onChange={this.onMinValueChange} value={minValue} />
-        <FormField label="Max value" labelWidth={8} onChange={this.onMaxValueChange} value={maxValue} />
+        <FormField label="Min value" labelWidth={this.labelWidth} onChange={this.onMinValueChange} value={minValue} />
+        <FormField label="Max value" labelWidth={this.labelWidth} onChange={this.onMaxValueChange} value={maxValue} />
         <Switch
           label="Show labels"
-          labelClass="width-8"
+          labelClass={`width-${this.labelWidth}`}
           checked={showThresholdLabels}
           onChange={this.onToggleThresholdLabels}
         />
         <Switch
           label="Show markers"
-          labelClass="width-8"
+          labelClass={`width-${this.labelWidth}`}
           checked={showThresholdMarkers}
           onChange={this.onToggleThresholdMarkers}
         />

+ 96 - 34
public/app/plugins/panel/gauge/GaugePanel.tsx

@@ -2,62 +2,124 @@
 import React, { PureComponent } from 'react';
 
 // Services & Utils
-import { processTimeSeries, ThemeContext } from '@grafana/ui';
+import { processTimeSeries } from '@grafana/ui';
+import { config } from 'app/core/config';
 
 // Components
 import { Gauge } from '@grafana/ui';
 
 // Types
 import { GaugeOptions } from './types';
-import { PanelProps, NullValueMode, TimeSeriesValue } from '@grafana/ui/src/types';
+import { PanelProps, NullValueMode } from '@grafana/ui/src/types';
 
 interface Props extends PanelProps<GaugeOptions> {}
 
 export class GaugePanel extends PureComponent<Props> {
-  render() {
-    const { panelData, width, height, onInterpolate, options } = this.props;
-    const { valueOptions } = options;
+  renderMultipleGauge(timeSeries) {
+    const { options, height, width } = this.props;
+    const { stat } = options.valueOptions;
+
+    return timeSeries.map((series, index) => {
+      const singleStatWidth = 1 / timeSeries.length * 100;
+      const singleStatHeight = 1 / timeSeries.length * 100;
+      const repeatingGaugeWidth = Math.floor(width / timeSeries.length) - 10; // make Gauge slightly smaller than panel.
+      const repeatingGaugeHeight = Math.floor(height / timeSeries.length) - 10;
+
+      const horizontalPanels = {
+        display: 'inline-block',
+        height: height,
+        width: `${singleStatWidth}%`,
+      };
+
+      const verticalPanels = {
+        display: 'block',
+        width: width,
+        height: `${singleStatHeight}%`,
+      };
+
+      let style = {};
+      let gaugeWidth = width;
+      let gaugeHeight = height;
+
+      if (width > height) {
+        style = horizontalPanels;
+        gaugeWidth = repeatingGaugeWidth;
+      } else if (height > width) {
+        style = verticalPanels;
+        gaugeHeight = repeatingGaugeHeight;
+      }
+
+      const value = stat !== 'name' ? series.stats[stat] : series.label;
+
+      return (
+        <div className="singlestat-panel" key={`${timeSeries.label}-${index}`} style={style}>
+          {this.renderGauge(value, gaugeWidth, gaugeHeight)}
+          <div style={{ textAlign: 'center' }}>{series.label}</div>
+        </div>
+      );
+    });
+  }
 
+  renderGauge(value, width, height) {
+    const { onInterpolate, options } = this.props;
+    const { valueOptions } = options;
     const prefix = onInterpolate(valueOptions.prefix);
     const suffix = onInterpolate(valueOptions.suffix);
-    let value: TimeSeriesValue;
+
+    return (
+      <Gauge
+        value={value}
+        width={width}
+        height={height}
+        prefix={prefix}
+        suffix={suffix}
+        unit={valueOptions.unit}
+        decimals={valueOptions.decimals}
+        thresholds={options.thresholds}
+        valueMappings={options.valueMappings}
+        showThresholdLabels={options.showThresholdLabels}
+        showThresholdMarkers={options.showThresholdMarkers}
+        minValue={options.minValue}
+        maxValue={options.maxValue}
+        theme={config.theme}
+      />
+    );
+  }
+
+  renderSingleGauge(timeSeries) {
+    const { options, width, height } = this.props;
+    const value = timeSeries[0].stats[options.valueOptions.stat];
+
+    return <div className="singlestat-panel">{this.renderGauge(value, width, height)}</div>;
+  }
+
+  renderGaugeWithTableData(panelData) {
+    const { width, height } = this.props;
+    const firstTableDataValue = panelData.tableData.rows[0].find(prop => prop > 0);
+
+    return <div className="singlestat-panel">{this.renderGauge(firstTableDataValue, width, height)}</div>;
+  }
+
+  render() {
+    const { panelData } = this.props;
 
     if (panelData.timeSeries) {
-      const vmSeries = processTimeSeries({
+      const timeSeries = processTimeSeries({
         timeSeries: panelData.timeSeries,
         nullValueMode: NullValueMode.Null,
       });
 
-      if (vmSeries[0]) {
-        value = vmSeries[0].stats[valueOptions.stat];
+      if (timeSeries.length > 1) {
+        return this.renderMultipleGauge(timeSeries);
+      } else if (timeSeries.length > 0) {
+        return this.renderSingleGauge(timeSeries);
       } else {
-        value = null;
+        return null;
       }
     } else if (panelData.tableData) {
-      value = panelData.tableData.rows[0].find(prop => prop > 0);
+      return this.renderGaugeWithTableData(panelData);
+    } else {
+      return <div className="singlestat-panel">No time series data available</div>;
     }
-
-    return (
-      <ThemeContext.Consumer>
-        {theme => (
-          <Gauge
-            value={value}
-            width={width}
-            height={height}
-            prefix={prefix}
-            suffix={suffix}
-            unit={valueOptions.unit}
-            decimals={valueOptions.decimals}
-            thresholds={options.thresholds}
-            valueMappings={options.valueMappings}
-            showThresholdLabels={options.showThresholdLabels}
-            showThresholdMarkers={options.showThresholdMarkers}
-            minValue={options.minValue}
-            maxValue={options.maxValue}
-            theme={theme}
-          />
-        )}
-      </ThemeContext.Consumer>
-    );
   }
 }