Table: Fix units showing in footer after reductions without units (#82081)

* Add preservesUnits property to reducer registry items

* Hide units when appropriate

* Prettier

* some code cleanup

* Prevent error when no stat is selected.

---------

Co-authored-by: nmarrs <nathanielmarrs@gmail.com>
This commit is contained in:
Kyle Cunningham
2024-02-22 22:28:15 -06:00
committed by GitHub
co-authored by nmarrs
parent 49299ebd9c
commit a4cc4179c8
2 changed files with 56 additions and 7 deletions
@@ -41,6 +41,7 @@ export interface FieldReducerInfo extends RegistryItem {
// Internal details
emptyInputResult?: unknown; // typically null, but some things like 'count' & 'sum' should be zero
standard: boolean; // The most common stats can all be calculated in a single pass
preservesUnits: boolean; // Whether this reducer preserves units, certain ones don't e.g. count, distinct count, etc,
reduce?: FieldReducer;
}
@@ -141,6 +142,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
standard: true,
aliasIds: ['current'],
reduce: calculateLastNotNull,
preservesUnits: true,
},
{
id: ReducerID.last,
@@ -148,6 +150,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'Last value',
standard: true,
reduce: calculateLast,
preservesUnits: true,
},
{
id: ReducerID.firstNotNull,
@@ -155,17 +158,33 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'First non-null value (also excludes NaNs)',
standard: true,
reduce: calculateFirstNotNull,
preservesUnits: true,
},
{
id: ReducerID.first,
name: 'First',
description: 'First Value',
standard: true,
reduce: calculateFirst,
preservesUnits: true,
},
{ id: ReducerID.min, name: 'Min', description: 'Minimum Value', standard: true, preservesUnits: true },
{ id: ReducerID.max, name: 'Max', description: 'Maximum Value', standard: true, preservesUnits: true },
{
id: ReducerID.mean,
name: 'Mean',
description: 'Average Value',
standard: true,
aliasIds: ['avg'],
preservesUnits: true,
},
{ id: ReducerID.first, name: 'First', description: 'First Value', standard: true, reduce: calculateFirst },
{ id: ReducerID.min, name: 'Min', description: 'Minimum Value', standard: true },
{ id: ReducerID.max, name: 'Max', description: 'Maximum Value', standard: true },
{ id: ReducerID.mean, name: 'Mean', description: 'Average Value', standard: true, aliasIds: ['avg'] },
{
id: ReducerID.variance,
name: 'Variance',
description: 'Variance of all values in a field',
standard: false,
reduce: calculateStdDev,
preservesUnits: true,
},
{
id: ReducerID.stdDev,
@@ -173,6 +192,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'Standard deviation of all values in a field',
standard: false,
reduce: calculateStdDev,
preservesUnits: true,
},
{
id: ReducerID.sum,
@@ -181,6 +201,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
emptyInputResult: 0,
standard: true,
aliasIds: ['total'],
preservesUnits: true,
},
{
id: ReducerID.count,
@@ -188,36 +209,42 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'Number of values in response',
emptyInputResult: 0,
standard: true,
preservesUnits: false,
},
{
id: ReducerID.range,
name: 'Range',
description: 'Difference between minimum and maximum values',
standard: true,
preservesUnits: true,
},
{
id: ReducerID.delta,
name: 'Delta',
description: 'Cumulative change in value',
standard: true,
preservesUnits: true,
},
{
id: ReducerID.step,
name: 'Step',
description: 'Minimum interval between values',
standard: true,
preservesUnits: true,
},
{
id: ReducerID.diff,
name: 'Difference',
description: 'Difference between first and last values',
standard: true,
preservesUnits: true,
},
{
id: ReducerID.logmin,
name: 'Min (above zero)',
description: 'Used for log min scale',
standard: true,
preservesUnits: true,
},
{
id: ReducerID.allIsZero,
@@ -225,6 +252,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'All values are zero',
emptyInputResult: false,
standard: true,
preservesUnits: true,
},
{
id: ReducerID.allIsNull,
@@ -232,6 +260,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'All values are null',
emptyInputResult: true,
standard: true,
preservesUnits: false,
},
{
id: ReducerID.changeCount,
@@ -239,6 +268,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'Number of times the value changes',
standard: false,
reduce: calculateChangeCount,
preservesUnits: false,
},
{
id: ReducerID.distinctCount,
@@ -246,12 +276,14 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'Number of distinct values',
standard: false,
reduce: calculateDistinctCount,
preservesUnits: false,
},
{
id: ReducerID.diffperc,
name: 'Difference percent',
description: 'Percentage difference between first and last values',
standard: true,
preservesUnits: false,
},
{
id: ReducerID.allValues,
@@ -259,6 +291,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
description: 'Returns an array with all values',
standard: false,
reduce: (field: Field) => ({ allValues: [...field.values] }),
preservesUnits: false,
},
{
id: ReducerID.uniqueValues,
@@ -268,6 +301,7 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
reduce: (field: Field) => ({
uniqueValues: [...new Set(field.values)],
}),
preservesUnits: false,
},
]);
@@ -385,10 +385,25 @@ export function getFooterItems(
}
function getFormattedValue(field: Field, reducer: string[], theme: GrafanaTheme2) {
const fmt = field.display ?? getDisplayProcessor({ field, theme });
// If we don't have anything to return then we display nothing
const calc = reducer[0];
const v = reduceField({ field, reducers: reducer })[calc];
return formattedValueToString(fmt(v));
if (calc === undefined) {
return '';
}
// Calculate the reduction
const format = field.display ?? getDisplayProcessor({ field, theme });
const fieldCalcValue = reduceField({ field, reducers: reducer })[calc];
// If the reducer preserves units then format the
// end value with the field display processor
const reducerInfo = fieldReducers.get(calc);
if (reducerInfo.preservesUnits) {
return formattedValueToString(format(fieldCalcValue));
}
// Otherwise we simply return the formatted string
return formattedValueToString({ text: fieldCalcValue });
}
// This strips the raw vales from the `rows` object.