diff --git a/public/app/features/alerting/unified/components/expressions/Expression.tsx b/public/app/features/alerting/unified/components/expressions/Expression.tsx
index d710c3831f9..3a4e0ce2141 100644
--- a/public/app/features/alerting/unified/components/expressions/Expression.tsx
+++ b/public/app/features/alerting/unified/components/expressions/Expression.tsx
@@ -35,7 +35,7 @@ import { Spacer } from '../Spacer';
import { AlertStateTag } from '../rules/AlertStateTag';
import { ExpressionStatusIndicator } from './ExpressionStatusIndicator';
-import { formatLabels, getSeriesLabels, getSeriesName, getSeriesValue, isEmptySeries } from './util';
+import { formatLabels, formatSeriesValue, getSeriesLabels, getSeriesName, getSeriesValue, isEmptySeries } from './util';
interface ExpressionProps {
isAlertCondition?: boolean;
@@ -438,7 +438,7 @@ function FrameRow({ frame, index, isAlertCondition, isRecordingRule }: FrameProp
)}
-
{value}
+ {formatSeriesValue(value)}
{shouldRenderSumary && (
<>
{showFiring && }
diff --git a/public/app/features/alerting/unified/components/expressions/util.test.ts b/public/app/features/alerting/unified/components/expressions/util.test.ts
index f8cb2d39d23..9ceec0b4cc4 100644
--- a/public/app/features/alerting/unified/components/expressions/util.test.ts
+++ b/public/app/features/alerting/unified/components/expressions/util.test.ts
@@ -8,6 +8,7 @@ import {
decodeGrafanaNamespace,
encodeGrafanaNamespace,
formatLabels,
+ formatSeriesValue,
getSeriesLabels,
getSeriesName,
getSeriesValue,
@@ -24,10 +25,6 @@ const DATA_FRAME: DataFrame = toDataFrame({
fields: [{ name: 'value', type: FieldType.number, values: [1, 2, 3] }],
});
-const DATA_FRAME_LARGE_DECIMAL: DataFrame = toDataFrame({
- fields: [{ name: 'value', type: FieldType.number, values: [1.23456789] }],
-});
-
const DATA_FRAME_WITH_LABELS: DataFrame = toDataFrame({
fields: [{ name: 'value', type: FieldType.number, values: [1, 2, 3], labels: { __name__: 'my-series', foo: 'bar' } }],
});
@@ -222,10 +219,6 @@ describe('getSeriesValue', () => {
const name = getSeriesValue(DATA_FRAME);
expect(name).toBe(1);
});
-
- it('should round values', () => {
- expect(getSeriesValue(DATA_FRAME_LARGE_DECIMAL)).toBe(1.23457);
- });
});
describe('getSeriesLabels', () => {
@@ -237,3 +230,35 @@ describe('getSeriesLabels', () => {
expect(getSeriesLabels(EMPTY_FRAME)).toStrictEqual({});
});
});
+
+describe('formatSeriesValue', () => {
+ it('should convert non-numeric values to strings', () => {
+ expect(formatSeriesValue('string value')).toBe('string value');
+ expect(formatSeriesValue(null)).toBe('null');
+ expect(formatSeriesValue(undefined)).toBe('undefined');
+ expect(formatSeriesValue({})).toBe('[object Object]');
+ });
+
+ it('should return 5 significant digits in absolutely smaller than 1', () => {
+ expect(formatSeriesValue(0.00005123)).toBe('0.00005123');
+ expect(formatSeriesValue(0.7894)).toBe('0.7894');
+ expect(formatSeriesValue(-0.000051237676767)).toBe('-0.000051238');
+ expect(formatSeriesValue(-0.25)).toBe('-0.25');
+ });
+
+ it('should use standard notation for numbers absolutely larger than 1', () => {
+ expect(formatSeriesValue(1.0001)).toBe('1.0001');
+ expect(formatSeriesValue(1.234)).toBe('1.234');
+ expect(formatSeriesValue(76767345.9876)).toBe('76767345.9876');
+ });
+
+ it('should round regular numbers to 5 decimal places', () => {
+ expect(formatSeriesValue(1.23456789)).toBe('1.23457');
+ expect(formatSeriesValue(0.10000001)).toBe('0.1');
+ expect(formatSeriesValue(-42.98765432)).toBe('-42.98765');
+ });
+
+ it('should handle zero correctly', () => {
+ expect(formatSeriesValue(0)).toBe('0');
+ });
+});
diff --git a/public/app/features/alerting/unified/components/expressions/util.ts b/public/app/features/alerting/unified/components/expressions/util.ts
index 07d732eb27b..a74c54dd79f 100644
--- a/public/app/features/alerting/unified/components/expressions/util.ts
+++ b/public/app/features/alerting/unified/components/expressions/util.ts
@@ -15,20 +15,29 @@ import { isCloudRulesSource } from '../../utils/datasource';
*/
const getSeriesName = (frame: DataFrame): string | undefined => {
- const firstField = frame.fields[0];
+ const firstField = frame.fields.at(0);
const displayNameFromDS = firstField?.config?.displayNameFromDS;
return displayNameFromDS ?? frame.name ?? firstField?.labels?.__name__;
};
const getSeriesValue = (frame: DataFrame) => {
- const value = frame.fields[0]?.values[0];
+ return frame.fields.at(0)?.values.at(0);
+};
- if (Number.isFinite(value)) {
- return roundDecimals(value, 5);
+const smallNumberFormatter = new Intl.NumberFormat(undefined, {
+ maximumSignificantDigits: 5,
+});
+
+const formatSeriesValue = (value: unknown): string => {
+ if (Number.isFinite(value) && typeof value === 'number') {
+ const absValue = Math.abs(value);
+ if (absValue < 1) {
+ return smallNumberFormatter.format(value);
+ }
+ return roundDecimals(value, 5).toString(10);
}
-
- return value;
+ return String(value);
};
const getSeriesLabels = (frame: DataFrame): Record => {
@@ -103,5 +112,6 @@ export {
getSeriesLabels,
getSeriesName,
getSeriesValue,
+ formatSeriesValue,
isEmptySeries,
};