Transformations: Grouping to matrix empty value option (#55591)

* feat: grouping matrix transformer empty value

Allow grouping matrix data transformer to define the value used when the matrix entry is not defined

* fix: grouping to matrix empty value test

* remove balel width for empty value text

Co-authored-by: bohandley <brendan.ohandley@gmail.com>
This commit is contained in:
hugofqt 2022-12-20 21:25:55 +01:00 committed by GitHub
parent c4b4baea2a
commit 8adb16b662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 3 deletions

View File

@ -1,5 +1,5 @@
import { toDataFrame } from '../../dataframe';
import { DataTransformerConfig, FieldType, Field } from '../../types';
import { DataTransformerConfig, FieldType, Field, SpecialValue } from '../../types';
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
import { ArrayVector } from '../../vector';
import { transformDataFrame } from '../transformDataFrame';
@ -105,6 +105,49 @@ describe('Grouping to Matrix', () => {
});
});
it('generates Matrix with empty entries', async () => {
const cfg: DataTransformerConfig<GroupingToMatrixTransformerOptions> = {
id: DataTransformerID.groupingToMatrix,
options: {
emptyValue: SpecialValue.Null,
},
};
const seriesA = toDataFrame({
name: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [1000, 1001] },
{ name: 'Value', type: FieldType.number, values: [1, 2] },
],
});
await expect(transformDataFrame([cfg], [seriesA])).toEmitValuesWith((received) => {
const processed = received[0];
const expected: Field[] = [
{
name: 'Time\\Time',
type: FieldType.string,
values: new ArrayVector([1000, 1001]),
config: {},
},
{
name: '1000',
type: FieldType.number,
values: new ArrayVector([1, null]),
config: {},
},
{
name: '1001',
type: FieldType.number,
values: new ArrayVector([null, 2]),
config: {},
},
];
expect(processed[0].fields).toEqual(expected);
});
});
it('generates Matrix with multiple fields and value type', async () => {
const cfg: DataTransformerConfig<GroupingToMatrixTransformerOptions> = {
id: DataTransformerID.groupingToMatrix,

View File

@ -2,7 +2,7 @@ import { map } from 'rxjs/operators';
import { MutableDataFrame } from '../../dataframe';
import { getFieldDisplayName } from '../../field/fieldState';
import { DataFrame, DataTransformerInfo, Field, FieldType, Vector } from '../../types';
import { DataFrame, DataTransformerInfo, Field, FieldType, SpecialValue, Vector } from '../../types';
import { DataTransformerID } from './ids';
@ -10,11 +10,13 @@ export interface GroupingToMatrixTransformerOptions {
columnField?: string;
rowField?: string;
valueField?: string;
emptyValue?: SpecialValue;
}
const DEFAULT_COLUMN_FIELD = 'Time';
const DEFAULT_ROW_FIELD = 'Time';
const DEFAULT_VALUE_FIELD = 'Value';
const DEFAULT_EMPTY_VALUE = SpecialValue.Empty;
export const groupingToMatrixTransformer: DataTransformerInfo<GroupingToMatrixTransformerOptions> = {
id: DataTransformerID.groupingToMatrix,
@ -32,6 +34,7 @@ export const groupingToMatrixTransformer: DataTransformerInfo<GroupingToMatrixTr
const columnFieldMatch = options.columnField || DEFAULT_COLUMN_FIELD;
const rowFieldMatch = options.rowField || DEFAULT_ROW_FIELD;
const valueFieldMatch = options.valueField || DEFAULT_VALUE_FIELD;
const emptyValue = options.emptyValue || DEFAULT_EMPTY_VALUE;
// Accept only single queries
if (data.length !== 1) {
@ -76,7 +79,7 @@ export const groupingToMatrixTransformer: DataTransformerInfo<GroupingToMatrixTr
for (const columnName of columnValues) {
let values = [];
for (const rowName of rowValues) {
const value = matrixValues[columnName][rowName] ?? '';
const value = matrixValues[columnName][rowName] ?? getSpecialValue(emptyValue);
values.push(value);
}
@ -114,3 +117,17 @@ function findKeyField(frame: DataFrame, matchTitle: string): Field | null {
return null;
}
function getSpecialValue(specialValue: SpecialValue) {
switch (specialValue) {
case SpecialValue.False:
return false;
case SpecialValue.True:
return true;
case SpecialValue.Null:
return null;
case SpecialValue.Empty:
default:
return '';
}
}

View File

@ -84,3 +84,13 @@ export interface MatcherConfig<TOptions = any> {
id: string;
options?: TOptions;
}
/**
* @public
*/
export enum SpecialValue {
True = 'true',
False = 'false',
Null = 'null',
Empty = 'empty',
}

View File

@ -7,6 +7,7 @@ import {
TransformerRegistryItem,
TransformerUIProps,
GroupingToMatrixTransformerOptions,
SpecialValue,
} from '@grafana/data';
import { InlineField, InlineFieldRow, Select } from '@grafana/ui';
@ -49,6 +50,23 @@ export const GroupingToMatrixTransformerEditor: React.FC<TransformerUIProps<Grou
[onChange, options]
);
const specialValueOptions: Array<SelectableValue<SpecialValue>> = [
{ label: 'Null', value: SpecialValue.Null, description: 'Null value' },
{ label: 'True', value: SpecialValue.True, description: 'Boolean true value' },
{ label: 'False', value: SpecialValue.False, description: 'Boolean false value' },
{ label: 'Empty', value: SpecialValue.Empty, description: 'Empty string' },
];
const onSelectEmptyValue = useCallback(
(value: SelectableValue<SpecialValue>) => {
onChange({
...options,
emptyValue: value?.value,
});
},
[onChange, options]
);
return (
<>
<InlineFieldRow>
@ -61,6 +79,9 @@ export const GroupingToMatrixTransformerEditor: React.FC<TransformerUIProps<Grou
<InlineField label="Cell Value" labelWidth={10}>
<Select options={fieldNames} value={options.valueField} onChange={onSelectValue} isClearable />
</InlineField>
<InlineField label="Empty Value">
<Select options={specialValueOptions} value={options.emptyValue} onChange={onSelectEmptyValue} isClearable />
</InlineField>
</InlineFieldRow>
</>
);