diff --git a/.betterer.results b/.betterer.results
index e6260144960..e4df49386e2 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -1,5 +1,5 @@
// BETTERER RESULTS V2.
-//
+//
// If this file contains merge conflicts, use `betterer merge` to automatically resolve them:
// https://phenomnomnominal.github.io/betterer/docs/results-file/#merge
//
@@ -11,9 +11,6 @@ exports[`no enzyme tests`] = {
"packages/grafana-ui/src/components/Forms/Legacy/Input/Input.test.tsx:3129955645": [
[0, 19, 13, "RegExp match", "2409514259"]
],
- "packages/grafana-ui/src/components/Gauge/Gauge.test.tsx:2525939157": [
- [0, 19, 13, "RegExp match", "2409514259"]
- ],
"packages/grafana-ui/src/components/Graph/Graph.test.tsx:1664091255": [
[0, 17, 13, "RegExp match", "2409514259"]
],
diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.test.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.test.tsx
index 9a1e0ebdd80..19a0ede4b44 100644
--- a/packages/grafana-ui/src/components/Gauge/Gauge.test.tsx
+++ b/packages/grafana-ui/src/components/Gauge/Gauge.test.tsx
@@ -1,4 +1,4 @@
-import { shallow } from 'enzyme';
+import { render } from '@testing-library/react';
import React from 'react';
import { ThresholdsMode, FieldConfig, FieldColorModeId } from '@grafana/data';
@@ -11,71 +11,33 @@ jest.mock('jquery', () => ({
plot: jest.fn(),
}));
-const setup = (propOverrides?: FieldConfig) => {
- const field: FieldConfig = {
- min: 0,
- max: 100,
- color: {
- mode: FieldColorModeId.Thresholds,
- },
- thresholds: {
- mode: ThresholdsMode.Absolute,
- steps: [{ value: -Infinity, color: '#7EB26D' }],
- },
- };
- Object.assign(field, propOverrides);
-
- const props: Props = {
- showThresholdMarkers: true,
- showThresholdLabels: false,
- field,
- width: 300,
- height: 300,
- value: {
- text: '25',
- numeric: 25,
- },
- theme: getTheme(),
- };
-
- const wrapper = shallow();
- const instance = wrapper.instance() as Gauge;
-
- return {
- instance,
- wrapper,
- };
+const field: FieldConfig = {
+ min: 0,
+ max: 100,
+ color: {
+ mode: FieldColorModeId.Thresholds,
+ },
+ thresholds: {
+ mode: ThresholdsMode.Absolute,
+ steps: [{ value: -Infinity, color: '#7EB26D' }],
+ },
};
-describe('Get thresholds formatted', () => {
- it('should return first thresholds color for min and max', () => {
- const { instance } = setup({
- thresholds: { mode: ThresholdsMode.Absolute, steps: [{ value: -Infinity, color: '#7EB26D' }] },
- });
+const props: Props = {
+ showThresholdMarkers: true,
+ showThresholdLabels: false,
+ field,
+ width: 300,
+ height: 300,
+ value: {
+ text: '25',
+ numeric: 25,
+ },
+ theme: getTheme(),
+};
- expect(instance.getFormattedThresholds(2)).toEqual([
- { value: 0, color: '#7EB26D' },
- { value: 100, color: '#7EB26D' },
- ]);
- });
-
- it('should get the correct formatted values when thresholds are added', () => {
- const { instance } = setup({
- thresholds: {
- mode: ThresholdsMode.Absolute,
- steps: [
- { value: -Infinity, color: '#7EB26D' },
- { value: 50, color: '#EAB839' },
- { value: 75, color: '#6ED0E0' },
- ],
- },
- });
-
- expect(instance.getFormattedThresholds(2)).toEqual([
- { value: 0, color: '#7EB26D' },
- { value: 50, color: '#7EB26D' },
- { value: 75, color: '#EAB839' },
- { value: 100, color: '#6ED0E0' },
- ]);
+describe('Gauge', () => {
+ it('should render without blowing up', () => {
+ expect(() => render()).not.toThrow();
});
});
diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx
index f35c53901f7..709cd81ea09 100644
--- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx
+++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx
@@ -8,17 +8,14 @@ import {
ThresholdsMode,
GAUGE_DEFAULT_MAXIMUM,
GAUGE_DEFAULT_MINIMUM,
- getActiveThreshold,
- Threshold,
- getColorForTheme,
- FieldColorModeId,
- FALLBACK_COLOR,
TextDisplayOptions,
} from '@grafana/data';
import { Themeable } from '../../types';
import { calculateFontSize } from '../../utils/measureText';
+import { calculateGaugeAutoProps, DEFAULT_THRESHOLDS, getFormattedThresholds } from './utils';
+
export interface Props extends Themeable {
height: number;
field: FieldConfig;
@@ -40,13 +37,7 @@ export class Gauge extends PureComponent {
field: {
min: 0,
max: 100,
- thresholds: {
- mode: ThresholdsMode.Absolute,
- steps: [
- { value: -Infinity, color: 'green' },
- { value: 80, color: 'red' },
- ],
- },
+ thresholds: DEFAULT_THRESHOLDS,
},
};
@@ -58,48 +49,6 @@ export class Gauge extends PureComponent {
this.draw();
}
- getFormattedThresholds(decimals: number): Threshold[] {
- const { field, theme, value } = this.props;
-
- if (field.color?.mode !== FieldColorModeId.Thresholds) {
- return [{ value: field.min ?? GAUGE_DEFAULT_MINIMUM, color: value.color ?? FALLBACK_COLOR }];
- }
-
- const thresholds = field.thresholds ?? Gauge.defaultProps.field?.thresholds!;
- const isPercent = thresholds.mode === ThresholdsMode.Percentage;
- const steps = thresholds.steps;
-
- let min = field.min ?? GAUGE_DEFAULT_MINIMUM;
- let max = field.max ?? GAUGE_DEFAULT_MAXIMUM;
-
- if (isPercent) {
- min = 0;
- max = 100;
- }
-
- const first = getActiveThreshold(min, steps);
- const last = getActiveThreshold(max, steps);
- const formatted: Threshold[] = [];
- formatted.push({ value: +min.toFixed(decimals), color: getColorForTheme(first.color, theme) });
- let skip = true;
- for (let i = 0; i < steps.length; i++) {
- const step = steps[i];
- if (skip) {
- if (first === step) {
- skip = false;
- }
- continue;
- }
- const prev = steps[i - 1];
- formatted.push({ value: step.value, color: getColorForTheme(prev!.color, theme) });
- if (step === last) {
- break;
- }
- }
- formatted.push({ value: +max.toFixed(decimals), color: getColorForTheme(last.color, theme) });
- return formatted;
- }
-
draw() {
const { field, showThresholdLabels, showThresholdMarkers, width, height, theme, value } = this.props;
@@ -157,7 +106,7 @@ export class Gauge extends PureComponent {
layout: { margin: 0, thresholdWidth: 0, vMargin: 0 },
cell: { border: { width: 0 } },
threshold: {
- values: this.getFormattedThresholds(decimals),
+ values: getFormattedThresholds(decimals, field, value, theme),
label: {
show: showThresholdLabels,
margin: thresholdMarkersWidth + 1,
@@ -240,23 +189,3 @@ export class Gauge extends PureComponent {
);
}
}
-
-interface GaugeAutoProps {
- titleFontSize: number;
- gaugeHeight: number;
- showLabel: boolean;
-}
-
-function calculateGaugeAutoProps(width: number, height: number, title: string | undefined): GaugeAutoProps {
- const showLabel = title !== null && title !== undefined;
- const titleFontSize = Math.min((width * 0.15) / 1.5, 20); // 20% of height * line-height, max 40px
- const titleHeight = titleFontSize * 1.5;
- const availableHeight = showLabel ? height - titleHeight : height;
- const gaugeHeight = Math.min(availableHeight, width);
-
- return {
- showLabel,
- gaugeHeight,
- titleFontSize,
- };
-}
diff --git a/packages/grafana-ui/src/components/Gauge/utils.test.ts b/packages/grafana-ui/src/components/Gauge/utils.test.ts
new file mode 100644
index 00000000000..45720c01a3c
--- /dev/null
+++ b/packages/grafana-ui/src/components/Gauge/utils.test.ts
@@ -0,0 +1,55 @@
+import { FieldColorModeId, FieldConfig, ThresholdsMode } from '@grafana/data';
+
+import { getTheme } from '../../themes';
+
+import { getFormattedThresholds } from './utils';
+
+describe('getFormattedThresholds', () => {
+ const value = {
+ text: '25',
+ numeric: 25,
+ };
+ const theme = getTheme();
+ let field: FieldConfig;
+
+ beforeEach(() => {
+ field = {
+ min: 0,
+ max: 100,
+ color: {
+ mode: FieldColorModeId.Thresholds,
+ },
+ thresholds: {
+ mode: ThresholdsMode.Absolute,
+ steps: [{ value: -Infinity, color: '#7EB26D' }],
+ },
+ };
+ });
+
+ it('should return first thresholds color for min and max', () => {
+ field.thresholds = { mode: ThresholdsMode.Absolute, steps: [{ value: -Infinity, color: '#7EB26D' }] };
+
+ expect(getFormattedThresholds(2, field, value, theme)).toEqual([
+ { value: 0, color: '#7EB26D' },
+ { value: 100, color: '#7EB26D' },
+ ]);
+ });
+
+ it('should get the correct formatted values when thresholds are added', () => {
+ field.thresholds = {
+ mode: ThresholdsMode.Absolute,
+ steps: [
+ { value: -Infinity, color: '#7EB26D' },
+ { value: 50, color: '#EAB839' },
+ { value: 75, color: '#6ED0E0' },
+ ],
+ };
+
+ expect(getFormattedThresholds(2, field, value, theme)).toEqual([
+ { value: 0, color: '#7EB26D' },
+ { value: 50, color: '#7EB26D' },
+ { value: 75, color: '#EAB839' },
+ { value: 100, color: '#6ED0E0' },
+ ]);
+ });
+});
diff --git a/packages/grafana-ui/src/components/Gauge/utils.ts b/packages/grafana-ui/src/components/Gauge/utils.ts
new file mode 100644
index 00000000000..84750c5274e
--- /dev/null
+++ b/packages/grafana-ui/src/components/Gauge/utils.ts
@@ -0,0 +1,86 @@
+import {
+ DisplayValue,
+ FALLBACK_COLOR,
+ FieldColorModeId,
+ FieldConfig,
+ GAUGE_DEFAULT_MAXIMUM,
+ GAUGE_DEFAULT_MINIMUM,
+ getActiveThreshold,
+ getColorForTheme,
+ GrafanaTheme,
+ Threshold,
+ ThresholdsConfig,
+ ThresholdsMode,
+} from '@grafana/data';
+
+interface GaugeAutoProps {
+ titleFontSize: number;
+ gaugeHeight: number;
+ showLabel: boolean;
+}
+
+export const DEFAULT_THRESHOLDS: ThresholdsConfig = {
+ mode: ThresholdsMode.Absolute,
+ steps: [
+ { value: -Infinity, color: 'green' },
+ { value: 80, color: 'red' },
+ ],
+};
+
+export function calculateGaugeAutoProps(width: number, height: number, title: string | undefined): GaugeAutoProps {
+ const showLabel = title !== null && title !== undefined;
+ const titleFontSize = Math.min((width * 0.15) / 1.5, 20); // 20% of height * line-height, max 40px
+ const titleHeight = titleFontSize * 1.5;
+ const availableHeight = showLabel ? height - titleHeight : height;
+ const gaugeHeight = Math.min(availableHeight, width);
+
+ return {
+ showLabel,
+ gaugeHeight,
+ titleFontSize,
+ };
+}
+
+export function getFormattedThresholds(
+ decimals: number,
+ field: FieldConfig,
+ value: DisplayValue,
+ theme: GrafanaTheme
+): Threshold[] {
+ if (field.color?.mode !== FieldColorModeId.Thresholds) {
+ return [{ value: field.min ?? GAUGE_DEFAULT_MINIMUM, color: value.color ?? FALLBACK_COLOR }];
+ }
+
+ const thresholds = field.thresholds ?? DEFAULT_THRESHOLDS;
+ const isPercent = thresholds.mode === ThresholdsMode.Percentage;
+ const steps = thresholds.steps;
+
+ let min = field.min ?? GAUGE_DEFAULT_MINIMUM;
+ let max = field.max ?? GAUGE_DEFAULT_MAXIMUM;
+
+ if (isPercent) {
+ min = 0;
+ max = 100;
+ }
+
+ const first = getActiveThreshold(min, steps);
+ const last = getActiveThreshold(max, steps);
+ const formatted: Threshold[] = [{ value: +min.toFixed(decimals), color: getColorForTheme(first.color, theme) }];
+ let skip = true;
+ for (let i = 0; i < steps.length; i++) {
+ const step = steps[i];
+ if (skip) {
+ if (first === step) {
+ skip = false;
+ }
+ continue;
+ }
+ const prev = steps[i - 1];
+ formatted.push({ value: step.value, color: getColorForTheme(prev!.color, theme) });
+ if (step === last) {
+ break;
+ }
+ }
+ formatted.push({ value: +max.toFixed(decimals), color: getColorForTheme(last.color, theme) });
+ return formatted;
+}