mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'gauge-multi-series' into bar-gauge-poc
This commit is contained in:
296
devenv/dev-dashboards/panel_tests_multiseries_gauge.json
Normal file
296
devenv/dev-dashboards/panel_tests_multiseries_gauge.json
Normal file
@@ -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
|
||||
}
|
||||
@@ -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)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user