diff --git a/devenv/dev-dashboards/panel_tests_multiseries_gauge.json b/devenv/dev-dashboards/panel_tests_multiseries_gauge.json new file mode 100644 index 00000000000..91313b7d439 --- /dev/null +++ b/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 +} diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx index d46ddff8be3..174a4c0a7ca 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx @@ -173,17 +173,15 @@ export class Gauge extends PureComponent { const { height, width } = this.props; return ( -
-
(this.canvasElement = element)} - /> -
+
(this.canvasElement = element)} + /> ); } } diff --git a/public/app/core/utils/ConfigProvider.tsx b/public/app/core/utils/ConfigProvider.tsx index cb3ad88b191..2162abae29b 100644 --- a/public/app/core/utils/ConfigProvider.tsx +++ b/public/app/core/utils/ConfigProvider.tsx @@ -15,6 +15,7 @@ export const provideConfig = (component: React.ComponentType) => { export const getCurrentThemeName = () => config.bootData.user.lightTheme ? GrafanaThemeType.Light : GrafanaThemeType.Dark; + export const getCurrentTheme = () => getTheme(getCurrentThemeName()); export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { diff --git a/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx b/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx index b5d6acca806..43ccd56a304 100644 --- a/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx +++ b/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> { + labelWidth = 8; + onToggleThresholdLabels = () => this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); @@ -25,17 +27,17 @@ export class GaugeOptionsBox extends PureComponent - - + + diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index e7e60a7c417..7be347563fd 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/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 {} export class GaugePanel extends PureComponent { - 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 ( +
+ {this.renderGauge(value, gaugeWidth, gaugeHeight)} +
{series.label}
+
+ ); + }); + } + + 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 ( + + ); + } + + renderSingleGauge(timeSeries) { + const { options, width, height } = this.props; + const value = timeSeries[0].stats[options.valueOptions.stat]; + + return
{this.renderGauge(value, width, height)}
; + } + + renderGaugeWithTableData(panelData) { + const { width, height } = this.props; + const firstTableDataValue = panelData.tableData.rows[0].find(prop => prop > 0); + + return
{this.renderGauge(firstTableDataValue, width, height)}
; + } + + 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
No time series data available
; } - - return ( - - {theme => ( - - )} - - ); } }