Merge branch 'gauge-multi-series' into bar-gauge-poc
This commit is contained in:
@@ -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;
|
const { height, width } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="singlestat-panel">
|
<div
|
||||||
<div
|
style={{
|
||||||
style={{
|
height: `${height * 0.9}px`,
|
||||||
height: `${height * 0.9}px`,
|
width: `${Math.min(width, height * 1.3)}px`,
|
||||||
width: `${Math.min(width, height * 1.3)}px`,
|
top: '10px',
|
||||||
top: '10px',
|
margin: 'auto',
|
||||||
margin: 'auto',
|
}}
|
||||||
}}
|
ref={element => (this.canvasElement = element)}
|
||||||
ref={element => (this.canvasElement = element)}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const provideConfig = (component: React.ComponentType<any>) => {
|
|||||||
|
|
||||||
export const getCurrentThemeName = () =>
|
export const getCurrentThemeName = () =>
|
||||||
config.bootData.user.lightTheme ? GrafanaThemeType.Light : GrafanaThemeType.Dark;
|
config.bootData.user.lightTheme ? GrafanaThemeType.Light : GrafanaThemeType.Dark;
|
||||||
|
|
||||||
export const getCurrentTheme = () => getTheme(getCurrentThemeName());
|
export const getCurrentTheme = () => getTheme(getCurrentThemeName());
|
||||||
|
|
||||||
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import { FormField, PanelEditorProps } from '@grafana/ui';
|
|||||||
import { GaugeOptions } from './types';
|
import { GaugeOptions } from './types';
|
||||||
|
|
||||||
export class GaugeOptionsBox extends PureComponent<PanelEditorProps<GaugeOptions>> {
|
export class GaugeOptionsBox extends PureComponent<PanelEditorProps<GaugeOptions>> {
|
||||||
|
labelWidth = 8;
|
||||||
|
|
||||||
onToggleThresholdLabels = () =>
|
onToggleThresholdLabels = () =>
|
||||||
this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels });
|
this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels });
|
||||||
|
|
||||||
@@ -25,17 +27,17 @@ export class GaugeOptionsBox extends PureComponent<PanelEditorProps<GaugeOptions
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelOptionsGroup title="Gauge">
|
<PanelOptionsGroup title="Gauge">
|
||||||
<FormField label="Min value" labelWidth={8} onChange={this.onMinValueChange} value={minValue} />
|
<FormField label="Min value" labelWidth={this.labelWidth} onChange={this.onMinValueChange} value={minValue} />
|
||||||
<FormField label="Max value" labelWidth={8} onChange={this.onMaxValueChange} value={maxValue} />
|
<FormField label="Max value" labelWidth={this.labelWidth} onChange={this.onMaxValueChange} value={maxValue} />
|
||||||
<Switch
|
<Switch
|
||||||
label="Show labels"
|
label="Show labels"
|
||||||
labelClass="width-8"
|
labelClass={`width-${this.labelWidth}`}
|
||||||
checked={showThresholdLabels}
|
checked={showThresholdLabels}
|
||||||
onChange={this.onToggleThresholdLabels}
|
onChange={this.onToggleThresholdLabels}
|
||||||
/>
|
/>
|
||||||
<Switch
|
<Switch
|
||||||
label="Show markers"
|
label="Show markers"
|
||||||
labelClass="width-8"
|
labelClass={`width-${this.labelWidth}`}
|
||||||
checked={showThresholdMarkers}
|
checked={showThresholdMarkers}
|
||||||
onChange={this.onToggleThresholdMarkers}
|
onChange={this.onToggleThresholdMarkers}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,62 +2,124 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
// Services & Utils
|
// Services & Utils
|
||||||
import { processTimeSeries, ThemeContext } from '@grafana/ui';
|
import { processTimeSeries } from '@grafana/ui';
|
||||||
|
import { config } from 'app/core/config';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Gauge } from '@grafana/ui';
|
import { Gauge } from '@grafana/ui';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { GaugeOptions } from './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> {}
|
interface Props extends PanelProps<GaugeOptions> {}
|
||||||
|
|
||||||
export class GaugePanel extends PureComponent<Props> {
|
export class GaugePanel extends PureComponent<Props> {
|
||||||
render() {
|
renderMultipleGauge(timeSeries) {
|
||||||
const { panelData, width, height, onInterpolate, options } = this.props;
|
const { options, height, width } = this.props;
|
||||||
const { valueOptions } = options;
|
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 prefix = onInterpolate(valueOptions.prefix);
|
||||||
const suffix = onInterpolate(valueOptions.suffix);
|
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) {
|
if (panelData.timeSeries) {
|
||||||
const vmSeries = processTimeSeries({
|
const timeSeries = processTimeSeries({
|
||||||
timeSeries: panelData.timeSeries,
|
timeSeries: panelData.timeSeries,
|
||||||
nullValueMode: NullValueMode.Null,
|
nullValueMode: NullValueMode.Null,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (vmSeries[0]) {
|
if (timeSeries.length > 1) {
|
||||||
value = vmSeries[0].stats[valueOptions.stat];
|
return this.renderMultipleGauge(timeSeries);
|
||||||
|
} else if (timeSeries.length > 0) {
|
||||||
|
return this.renderSingleGauge(timeSeries);
|
||||||
} else {
|
} else {
|
||||||
value = null;
|
return null;
|
||||||
}
|
}
|
||||||
} else if (panelData.tableData) {
|
} 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