Skip to content

Commit bfa54d2

Browse files
authored
Merge pull request grafana#15925 from ryantxu/reusable-formatting-options
make value processing/formatting more reusable
2 parents fff6e0a + 0bb772a commit bfa54d2

25 files changed

+735
-285
lines changed

packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,14 @@ jest.mock('jquery', () => ({
1111
const setup = (propOverrides?: object) => {
1212
const props: Props = {
1313
maxValue: 100,
14-
valueMappings: [],
1514
minValue: 0,
16-
prefix: '',
17-
suffix: '',
1815
thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }],
19-
unit: 'none',
2016
height: 300,
2117
width: 300,
22-
value: 25,
23-
decimals: 0,
18+
value: {
19+
text: '25',
20+
numeric: 25,
21+
},
2422
theme: getTheme(),
2523
orientation: VizOrientation.Horizontal,
2624
};

packages/grafana-ui/src/components/BarGauge/BarGauge.tsx

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,21 @@ import React, { PureComponent, CSSProperties } from 'react';
33
import tinycolor from 'tinycolor2';
44

55
// Utils
6-
import { getColorFromHexRgbOrName, getValueFormat, getThresholdForValue } from '../../utils';
6+
import { getColorFromHexRgbOrName, getThresholdForValue, DisplayValue } from '../../utils';
77

88
// Types
9-
import { Themeable, TimeSeriesValue, Threshold, ValueMapping, VizOrientation } from '../../types';
9+
import { Themeable, TimeSeriesValue, Threshold, VizOrientation } from '../../types';
1010

1111
const BAR_SIZE_RATIO = 0.8;
1212

1313
export interface Props extends Themeable {
1414
height: number;
15-
unit: string;
1615
width: number;
1716
thresholds: Threshold[];
18-
valueMappings: ValueMapping[];
19-
value: TimeSeriesValue;
17+
value: DisplayValue;
2018
maxValue: number;
2119
minValue: number;
2220
orientation: VizOrientation;
23-
prefix?: string;
24-
suffix?: string;
25-
decimals?: number;
2621
}
2722

2823
/*
@@ -32,24 +27,18 @@ export class BarGauge extends PureComponent<Props> {
3227
static defaultProps: Partial<Props> = {
3328
maxValue: 100,
3429
minValue: 0,
35-
value: 100,
36-
unit: 'none',
30+
value: {
31+
text: '100',
32+
numeric: 100,
33+
},
3734
orientation: VizOrientation.Horizontal,
3835
thresholds: [],
39-
valueMappings: [],
4036
};
4137

42-
getNumericValue(): number {
43-
if (Number.isFinite(this.props.value as number)) {
44-
return this.props.value as number;
45-
}
46-
return 0;
47-
}
48-
4938
getValueColors(): BarColors {
5039
const { thresholds, theme, value } = this.props;
5140

52-
const activeThreshold = getThresholdForValue(thresholds, value);
41+
const activeThreshold = getThresholdForValue(thresholds, value.numeric);
5342

5443
if (activeThreshold !== null) {
5544
const color = getColorFromHexRgbOrName(activeThreshold.color, theme.type);
@@ -78,7 +67,7 @@ export class BarGauge extends PureComponent<Props> {
7867
const color = getColorFromHexRgbOrName(activeThreshold.color, theme.type);
7968

8069
// if we are past real value the cell is not "on"
81-
if (value === null || (positionValue !== null && positionValue > value)) {
70+
if (value === null || (positionValue !== null && positionValue > value.numeric)) {
8271
return tinycolor(color)
8372
.setAlpha(0.15)
8473
.toRgbString();
@@ -217,18 +206,14 @@ export class BarGauge extends PureComponent<Props> {
217206
}
218207

219208
render() {
220-
const { maxValue, minValue, orientation, unit, decimals } = this.props;
221-
222-
const numericValue = this.getNumericValue();
223-
const valuePercent = Math.min(numericValue / (maxValue - minValue), 1);
209+
const { maxValue, minValue, orientation, value } = this.props;
224210

225-
const formatFunc = getValueFormat(unit);
226-
const valueFormatted = formatFunc(numericValue, decimals);
211+
const valuePercent = Math.min(value.numeric / (maxValue - minValue), 1);
227212
const vertical = orientation === 'vertical';
228213

229214
return vertical
230-
? this.renderVerticalBar(valueFormatted, valuePercent)
231-
: this.renderHorizontalLCD(valueFormatted, valuePercent);
215+
? this.renderVerticalBar(value.text, valuePercent)
216+
: this.renderHorizontalLCD(value.text, valuePercent);
232217
}
233218
}
234219

packages/grafana-ui/src/components/Gauge/Gauge.test.tsx

Lines changed: 4 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from 'react';
22
import { shallow } from 'enzyme';
33

44
import { Gauge, Props } from './Gauge';
5-
import { ValueMapping, MappingType } from '../../types';
65
import { getTheme } from '../../themes';
76

87
jest.mock('jquery', () => ({
@@ -12,19 +11,16 @@ jest.mock('jquery', () => ({
1211
const setup = (propOverrides?: object) => {
1312
const props: Props = {
1413
maxValue: 100,
15-
valueMappings: [],
1614
minValue: 0,
17-
prefix: '',
1815
showThresholdMarkers: true,
1916
showThresholdLabels: false,
20-
suffix: '',
2117
thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }],
22-
unit: 'none',
23-
stat: 'avg',
2418
height: 300,
2519
width: 300,
26-
value: 25,
27-
decimals: 0,
20+
value: {
21+
text: '25',
22+
numeric: 25,
23+
},
2824
theme: getTheme(),
2925
};
3026

@@ -39,38 +35,6 @@ const setup = (propOverrides?: object) => {
3935
};
4036
};
4137

42-
describe('Get font color', () => {
43-
it('should get first threshold color when only one threshold', () => {
44-
const { instance } = setup({ thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }] });
45-
46-
expect(instance.getFontColor(49)).toEqual('#7EB26D');
47-
});
48-
49-
it('should get the threshold color if value is same as a threshold', () => {
50-
const { instance } = setup({
51-
thresholds: [
52-
{ index: 2, value: 75, color: '#6ED0E0' },
53-
{ index: 1, value: 50, color: '#EAB839' },
54-
{ index: 0, value: -Infinity, color: '#7EB26D' },
55-
],
56-
});
57-
58-
expect(instance.getFontColor(50)).toEqual('#EAB839');
59-
});
60-
61-
it('should get the nearest threshold color between thresholds', () => {
62-
const { instance } = setup({
63-
thresholds: [
64-
{ index: 2, value: 75, color: '#6ED0E0' },
65-
{ index: 1, value: 50, color: '#EAB839' },
66-
{ index: 0, value: -Infinity, color: '#7EB26D' },
67-
],
68-
});
69-
70-
expect(instance.getFontColor(55)).toEqual('#EAB839');
71-
});
72-
});
73-
7438
describe('Get thresholds formatted', () => {
7539
it('should return first thresholds color for min and max', () => {
7640
const { instance } = setup({ thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }] });
@@ -98,51 +62,3 @@ describe('Get thresholds formatted', () => {
9862
]);
9963
});
10064
});
101-
102-
describe('Format value', () => {
103-
it('should return if value isNaN', () => {
104-
const valueMappings: ValueMapping[] = [];
105-
const value = 'N/A';
106-
const { instance } = setup({ valueMappings });
107-
108-
const result = instance.formatValue(value);
109-
110-
expect(result).toEqual('N/A');
111-
});
112-
113-
it('should return formatted value if there are no value mappings', () => {
114-
const valueMappings: ValueMapping[] = [];
115-
const value = '6';
116-
const { instance } = setup({ valueMappings, decimals: 1 });
117-
118-
const result = instance.formatValue(value);
119-
120-
expect(result).toEqual('6.0');
121-
});
122-
123-
it('should return formatted value if there are no matching value mappings', () => {
124-
const valueMappings: ValueMapping[] = [
125-
{ id: 0, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
126-
{ id: 1, operator: '', text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
127-
];
128-
const value = '10';
129-
const { instance } = setup({ valueMappings, decimals: 1 });
130-
131-
const result = instance.formatValue(value);
132-
133-
expect(result).toEqual('10.0');
134-
});
135-
136-
it('should return mapped value if there are matching value mappings', () => {
137-
const valueMappings: ValueMapping[] = [
138-
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
139-
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
140-
];
141-
const value = '11';
142-
const { instance } = setup({ valueMappings, decimals: 1 });
143-
144-
const result = instance.formatValue(value);
145-
146-
expect(result).toEqual('1-20');
147-
});
148-
});

packages/grafana-ui/src/components/Gauge/Gauge.tsx

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
import React, { PureComponent } from 'react';
22
import $ from 'jquery';
33

4-
import { ValueMapping, Threshold, GrafanaThemeType } from '../../types';
5-
import { getMappedValue } from '../../utils/valueMappings';
6-
import { getColorFromHexRgbOrName, getValueFormat, getThresholdForValue } from '../../utils';
4+
import { Threshold, GrafanaThemeType } from '../../types';
5+
import { getColorFromHexRgbOrName } from '../../utils';
76
import { Themeable } from '../../index';
8-
9-
type GaugeValue = string | number | null;
7+
import { DisplayValue } from '../../utils/displayValue';
108

119
export interface Props extends Themeable {
12-
decimals?: number | null;
1310
height: number;
14-
valueMappings: ValueMapping[];
1511
maxValue: number;
1612
minValue: number;
17-
prefix: string;
1813
thresholds: Threshold[];
1914
showThresholdMarkers: boolean;
2015
showThresholdLabels: boolean;
21-
stat: string;
22-
suffix: string;
23-
unit: string;
2416
width: number;
25-
value: number;
17+
value: DisplayValue;
2618
}
2719

2820
const FONT_SCALE = 1;
@@ -32,15 +24,10 @@ export class Gauge extends PureComponent<Props> {
3224

3325
static defaultProps: Partial<Props> = {
3426
maxValue: 100,
35-
valueMappings: [],
3627
minValue: 0,
37-
prefix: '',
3828
showThresholdMarkers: true,
3929
showThresholdLabels: false,
40-
suffix: '',
4130
thresholds: [],
42-
unit: 'none',
43-
stat: 'avg',
4431
};
4532

4633
componentDidMount() {
@@ -51,39 +38,6 @@ export class Gauge extends PureComponent<Props> {
5138
this.draw();
5239
}
5340

54-
formatValue(value: GaugeValue) {
55-
const { decimals, valueMappings, prefix, suffix, unit } = this.props;
56-
57-
if (isNaN(value as number)) {
58-
return value;
59-
}
60-
61-
if (valueMappings.length > 0) {
62-
const valueMappedValue = getMappedValue(valueMappings, value);
63-
if (valueMappedValue) {
64-
return `${prefix && prefix + ' '}${valueMappedValue.text}${suffix && ' ' + suffix}`;
65-
}
66-
}
67-
68-
const formatFunc = getValueFormat(unit);
69-
const formattedValue = formatFunc(value as number, decimals);
70-
const handleNoValueValue = formattedValue || 'no value';
71-
72-
return `${prefix && prefix + ' '}${handleNoValueValue}${suffix && ' ' + suffix}`;
73-
}
74-
75-
getFontColor(value: GaugeValue): string {
76-
const { thresholds, theme } = this.props;
77-
78-
const activeThreshold = getThresholdForValue(thresholds, value);
79-
80-
if (activeThreshold !== null) {
81-
return getColorFromHexRgbOrName(activeThreshold.color, theme.type);
82-
}
83-
84-
return '';
85-
}
86-
8741
getFormattedThresholds() {
8842
const { maxValue, minValue, thresholds, theme } = this.props;
8943

@@ -112,15 +66,13 @@ export class Gauge extends PureComponent<Props> {
11266
draw() {
11367
const { maxValue, minValue, showThresholdLabels, showThresholdMarkers, width, height, theme, value } = this.props;
11468

115-
const formattedValue = this.formatValue(value) as string;
11669
const dimension = Math.min(width, height * 1.3);
11770
const backgroundColor = theme.type === GrafanaThemeType.Light ? 'rgb(230,230,230)' : theme.colors.dark3;
11871

11972
const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1;
12073
const gaugeWidth = Math.min(dimension / 6, 60) / gaugeWidthReduceRatio;
12174
const thresholdMarkersWidth = gaugeWidth / 5;
122-
const fontSize =
123-
Math.min(dimension / 5, 100) * (formattedValue !== null ? this.getFontScale(formattedValue.length) : 1);
75+
const fontSize = Math.min(dimension / 5, 100) * (value.text !== null ? this.getFontScale(value.text.length) : 1);
12476
const thresholdLabelFontSize = fontSize / 2.5;
12577

12678
const options: any = {
@@ -149,9 +101,9 @@ export class Gauge extends PureComponent<Props> {
149101
width: thresholdMarkersWidth,
150102
},
151103
value: {
152-
color: this.getFontColor(value),
104+
color: value.color,
153105
formatter: () => {
154-
return formattedValue;
106+
return value.text;
155107
},
156108
font: { size: fontSize, family: '"Helvetica Neue", Helvetica, Arial, sans-serif' },
157109
},
@@ -160,7 +112,7 @@ export class Gauge extends PureComponent<Props> {
160112
},
161113
};
162114

163-
const plotSeries = { data: [[0, value]] };
115+
const plotSeries = { data: [[0, value.numeric]] };
164116

165117
try {
166118
$.plot(this.canvasElement, [plotSeries], options);

0 commit comments

Comments
 (0)