mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Transformers: changes reduce transformer (#23611)
* Transformers: changes reduce transformer * Refactor: fixes lenght of frame * Minor tweaks and polish Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
parent
ed8c3430c4
commit
97c5e2971d
@ -1,73 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Reducer Transformer filters by include 1`] = `
|
|
||||||
Object {
|
|
||||||
"fields": Array [
|
|
||||||
Object {
|
|
||||||
"config": Object {},
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "Field",
|
|
||||||
"type": "string",
|
|
||||||
"values": Array [
|
|
||||||
"A",
|
|
||||||
"B",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"config": Object {
|
|
||||||
"title": "First",
|
|
||||||
},
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "first",
|
|
||||||
"type": "number",
|
|
||||||
"values": Array [
|
|
||||||
1,
|
|
||||||
"a",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"config": Object {
|
|
||||||
"title": "Min",
|
|
||||||
},
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "min",
|
|
||||||
"type": "number",
|
|
||||||
"values": Array [
|
|
||||||
1,
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"config": Object {
|
|
||||||
"title": "Max",
|
|
||||||
},
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "max",
|
|
||||||
"type": "number",
|
|
||||||
"values": Array [
|
|
||||||
4,
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"config": Object {
|
|
||||||
"title": "Delta",
|
|
||||||
},
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "delta",
|
|
||||||
"type": "number",
|
|
||||||
"values": Array [
|
|
||||||
3,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"meta": Object {
|
|
||||||
"transformations": Array [
|
|
||||||
"reduce",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"name": undefined,
|
|
||||||
"refId": undefined,
|
|
||||||
}
|
|
||||||
`;
|
|
@ -1,14 +1,43 @@
|
|||||||
import { ReducerID } from '../fieldReducer';
|
import { ReducerID } from '../fieldReducer';
|
||||||
import { DataTransformerID } from './ids';
|
import { DataTransformerID } from './ids';
|
||||||
import { toDataFrame, toDataFrameDTO } from '../../dataframe/processDataFrame';
|
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||||
import { reduceTransformer } from './reduce';
|
import { reduceTransformer } from './reduce';
|
||||||
import { transformDataFrame } from '../transformDataFrame';
|
import { transformDataFrame } from '../transformDataFrame';
|
||||||
|
import { Field, FieldType } from '../../types';
|
||||||
|
import { ArrayVector } from '../../vector';
|
||||||
|
|
||||||
const seriesWithValues = toDataFrame({
|
const seriesAWithSingleField = toDataFrame({
|
||||||
|
name: 'A',
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'A', values: [1, 2, 3, 4] }, // Numbers
|
{ name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000] },
|
||||||
{ name: 'B', values: ['a', 'b', 'c', 'd'] }, // Strings
|
{ name: 'temperature', type: FieldType.number, values: [3, 4, 5, 6] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesAWithMultipleFields = toDataFrame({
|
||||||
|
name: 'A',
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000] },
|
||||||
|
{ name: 'temperature', type: FieldType.number, values: [3, 4, 5, 6] },
|
||||||
|
{ name: 'humidity', type: FieldType.number, values: [10000.3, 10000.4, 10000.5, 10000.6] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesBWithSingleField = toDataFrame({
|
||||||
|
name: 'B',
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, values: [1000, 3000, 5000, 7000] },
|
||||||
|
{ name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesBWithMultipleFields = toDataFrame({
|
||||||
|
name: 'B',
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, values: [1000, 3000, 5000, 7000] },
|
||||||
|
{ name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] },
|
||||||
|
{ name: 'humidity', type: FieldType.number, values: [11000.1, 11000.3, 11000.5, 11000.7] },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -16,15 +45,188 @@ describe('Reducer Transformer', () => {
|
|||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockTransformationsRegistry([reduceTransformer]);
|
mockTransformationsRegistry([reduceTransformer]);
|
||||||
});
|
});
|
||||||
it('filters by include', () => {
|
|
||||||
|
it('reduces multiple data frames with many fields', () => {
|
||||||
const cfg = {
|
const cfg = {
|
||||||
id: DataTransformerID.reduce,
|
id: DataTransformerID.reduce,
|
||||||
options: {
|
options: {
|
||||||
reducers: [ReducerID.first, ReducerID.min, ReducerID.max, ReducerID.delta],
|
reducers: [ReducerID.first, ReducerID.min, ReducerID.max, ReducerID.last],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const processed = transformDataFrame([cfg], [seriesWithValues])[0];
|
const processed = transformDataFrame([cfg], [seriesAWithMultipleFields, seriesBWithMultipleFields]);
|
||||||
expect(processed.fields.length).toBe(5);
|
const expected: Field[] = [
|
||||||
expect(toDataFrameDTO(processed)).toMatchSnapshot();
|
{
|
||||||
|
name: 'Field',
|
||||||
|
type: FieldType.string,
|
||||||
|
values: new ArrayVector(['temperature {A}', 'humidity {A}', 'temperature {B}', 'humidity {B}']),
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'first',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([3, 10000.3, 1, 11000.1]),
|
||||||
|
config: { title: 'First' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'min',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([3, 10000.3, 1, 11000.1]),
|
||||||
|
config: { title: 'Min' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'max',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([6, 10000.6, 7, 11000.7]),
|
||||||
|
config: { title: 'Max' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'last',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([6, 10000.6, 7, 11000.7]),
|
||||||
|
config: { title: 'Last' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(processed.length).toEqual(1);
|
||||||
|
expect(processed[0].length).toEqual(4);
|
||||||
|
expect(processed[0].fields).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reduces multiple data frames with single field', () => {
|
||||||
|
const cfg = {
|
||||||
|
id: DataTransformerID.reduce,
|
||||||
|
options: {
|
||||||
|
reducers: [ReducerID.first, ReducerID.min, ReducerID.max, ReducerID.last],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const processed = transformDataFrame([cfg], [seriesAWithSingleField, seriesBWithSingleField]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
{
|
||||||
|
name: 'Field',
|
||||||
|
type: FieldType.string,
|
||||||
|
values: new ArrayVector(['temperature {A}', 'temperature {B}']),
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'first',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([3, 1]),
|
||||||
|
config: { title: 'First' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'min',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([3, 1]),
|
||||||
|
config: { title: 'Min' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'max',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([6, 7]),
|
||||||
|
config: { title: 'Max' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'last',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([6, 7]),
|
||||||
|
config: { title: 'Last' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(processed.length).toEqual(1);
|
||||||
|
expect(processed[0].length).toEqual(2);
|
||||||
|
expect(processed[0].fields).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reduces single data frame with many fields', () => {
|
||||||
|
const cfg = {
|
||||||
|
id: DataTransformerID.reduce,
|
||||||
|
options: {
|
||||||
|
reducers: [ReducerID.first, ReducerID.min, ReducerID.max, ReducerID.last],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const processed = transformDataFrame([cfg], [seriesAWithMultipleFields]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
{
|
||||||
|
name: 'Field',
|
||||||
|
type: FieldType.string,
|
||||||
|
values: new ArrayVector(['temperature', 'humidity']),
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'first',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([3, 10000.3]),
|
||||||
|
config: { title: 'First' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'min',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([3, 10000.3]),
|
||||||
|
config: { title: 'Min' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'max',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([6, 10000.6]),
|
||||||
|
config: { title: 'Max' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'last',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([6, 10000.6]),
|
||||||
|
config: { title: 'Last' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(processed.length).toEqual(1);
|
||||||
|
expect(processed[0].length).toEqual(2);
|
||||||
|
expect(processed[0].fields).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reduces single data frame with single field', () => {
|
||||||
|
const cfg = {
|
||||||
|
id: DataTransformerID.reduce,
|
||||||
|
options: {
|
||||||
|
reducers: [ReducerID.first, ReducerID.min, ReducerID.max, ReducerID.last],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const processed = transformDataFrame([cfg], [seriesAWithSingleField]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
{
|
||||||
|
name: 'Field',
|
||||||
|
type: FieldType.string,
|
||||||
|
values: new ArrayVector(['temperature']),
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'first',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([3]),
|
||||||
|
config: { title: 'First' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'min',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([3]),
|
||||||
|
config: { title: 'Min' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'max',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([6]),
|
||||||
|
config: { title: 'Max' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'last',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector([6]),
|
||||||
|
config: { title: 'Last' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(processed.length).toEqual(1);
|
||||||
|
expect(processed[0].length).toEqual(1);
|
||||||
|
expect(processed[0].fields).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import { DataTransformerID } from './ids';
|
import { DataTransformerID } from './ids';
|
||||||
import { MatcherConfig, DataTransformerInfo } from '../../types/transformations';
|
import { DataTransformerInfo, MatcherConfig } from '../../types/transformations';
|
||||||
import { ReducerID, fieldReducers, reduceField } from '../fieldReducer';
|
import { fieldReducers, reduceField, ReducerID } from '../fieldReducer';
|
||||||
import { alwaysFieldMatcher } from '../matchers/predicates';
|
import { alwaysFieldMatcher } from '../matchers/predicates';
|
||||||
import { DataFrame, Field, FieldType } from '../../types/dataFrame';
|
import { DataFrame, Field, FieldType } from '../../types/dataFrame';
|
||||||
import { ArrayVector } from '../../vector/ArrayVector';
|
import { ArrayVector } from '../../vector/ArrayVector';
|
||||||
import { KeyValue } from '../../types/data';
|
import { KeyValue } from '../../types/data';
|
||||||
import { guessFieldTypeForField } from '../../dataframe/processDataFrame';
|
import { guessFieldTypeForField } from '../../dataframe/processDataFrame';
|
||||||
import { getFieldMatcher } from '../matchers';
|
import { getFieldMatcher } from '../matchers';
|
||||||
|
import { FieldMatcherID } from '../matchers/ids';
|
||||||
|
import { filterFieldsTransformer } from './filter';
|
||||||
|
|
||||||
export interface ReduceTransformerOptions {
|
export interface ReduceTransformerOptions {
|
||||||
reducers: ReducerID[];
|
reducers: ReducerID[];
|
||||||
@ -15,10 +17,10 @@ export interface ReduceTransformerOptions {
|
|||||||
|
|
||||||
export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> = {
|
export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> = {
|
||||||
id: DataTransformerID.reduce,
|
id: DataTransformerID.reduce,
|
||||||
name: 'Reducer',
|
name: 'Reduce',
|
||||||
description: 'Return a DataFrame with the reduction results',
|
description: 'Reduce all rows to a single row and concatenate all results',
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
reducers: [ReducerID.min, ReducerID.max, ReducerID.mean, ReducerID.last],
|
reducers: [ReducerID.max],
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,10 +34,13 @@ export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> =
|
|||||||
|
|
||||||
return (data: DataFrame[]) => {
|
return (data: DataFrame[]) => {
|
||||||
const processed: DataFrame[] = [];
|
const processed: DataFrame[] = [];
|
||||||
for (const series of data) {
|
|
||||||
|
for (let seriesIndex = 0; seriesIndex < data.length; seriesIndex++) {
|
||||||
|
const series = data[seriesIndex];
|
||||||
const values: ArrayVector[] = [];
|
const values: ArrayVector[] = [];
|
||||||
const fields: Field[] = [];
|
const fields: Field[] = [];
|
||||||
const byId: KeyValue<ArrayVector> = {};
|
const byId: KeyValue<ArrayVector> = {};
|
||||||
|
|
||||||
values.push(new ArrayVector()); // The name
|
values.push(new ArrayVector()); // The name
|
||||||
fields.push({
|
fields.push({
|
||||||
name: 'Field',
|
name: 'Field',
|
||||||
@ -43,10 +48,12 @@ export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> =
|
|||||||
values: values[0],
|
values: values[0],
|
||||||
config: {},
|
config: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const info of calculators) {
|
for (const info of calculators) {
|
||||||
const vals = new ArrayVector();
|
const vals = new ArrayVector();
|
||||||
byId[info.id] = vals;
|
byId[info.id] = vals;
|
||||||
values.push(vals);
|
values.push(vals);
|
||||||
|
|
||||||
fields.push({
|
fields.push({
|
||||||
name: info.id,
|
name: info.id,
|
||||||
type: FieldType.other, // UNKNOWN until after we call the functions
|
type: FieldType.other, // UNKNOWN until after we call the functions
|
||||||
@ -57,34 +64,80 @@ export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> =
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < series.fields.length; i++) {
|
for (let i = 0; i < series.fields.length; i++) {
|
||||||
const field = series.fields[i];
|
const field = series.fields[i];
|
||||||
|
|
||||||
|
if (field.type === FieldType.time) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (matcher(field)) {
|
if (matcher(field)) {
|
||||||
const results = reduceField({
|
const results = reduceField({
|
||||||
field,
|
field,
|
||||||
reducers,
|
reducers,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the name list
|
// Update the name list
|
||||||
values[0].buffer.push(field.name);
|
const seriesName = series.name ?? series.refId ?? seriesIndex;
|
||||||
|
const fieldName =
|
||||||
|
field.name === seriesName || data.length === 1 ? field.name : `${field.name} {${seriesName}}`;
|
||||||
|
|
||||||
|
values[0].buffer.push(fieldName);
|
||||||
|
|
||||||
for (const info of calculators) {
|
for (const info of calculators) {
|
||||||
const v = results[info.id];
|
const v = results[info.id];
|
||||||
byId[info.id].buffer.push(v);
|
byId[info.id].buffer.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const f of fields) {
|
for (const f of fields) {
|
||||||
const t = guessFieldTypeForField(f);
|
const t = guessFieldTypeForField(f);
|
||||||
|
|
||||||
if (t) {
|
if (t) {
|
||||||
f.type = t;
|
f.type = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processed.push({
|
processed.push({
|
||||||
...series, // Same properties, different fields
|
...series, // Same properties, different fields
|
||||||
fields,
|
fields,
|
||||||
length: values[0].length,
|
length: values[0].length,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return processed;
|
|
||||||
|
const withoutTime = filterFieldsTransformer.transformer({ exclude: { id: FieldMatcherID.time } })(processed);
|
||||||
|
return mergeResults(withoutTime);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mergeResults = (data: DataFrame[]) => {
|
||||||
|
if (data.length <= 1) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseFrame = data[0];
|
||||||
|
|
||||||
|
for (let seriesIndex = 1; seriesIndex < data.length; seriesIndex++) {
|
||||||
|
const series = data[seriesIndex];
|
||||||
|
|
||||||
|
for (const baseField of baseFrame.fields) {
|
||||||
|
for (const field of series.fields) {
|
||||||
|
if (baseField.type !== field.type || baseField.name !== field.name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseValues: any[] = ((baseField.values as unknown) as ArrayVector).buffer;
|
||||||
|
const values: any[] = ((field.values as unknown) as ArrayVector).buffer;
|
||||||
|
((baseField.values as unknown) as ArrayVector).buffer = baseValues.concat(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
baseFrame.name = undefined;
|
||||||
|
baseFrame.length = baseFrame.fields[0].values.length;
|
||||||
|
|
||||||
|
return [baseFrame];
|
||||||
|
};
|
||||||
|
@ -102,6 +102,7 @@ export function SelectBase<T>({
|
|||||||
renderControl,
|
renderControl,
|
||||||
size = 'auto',
|
size = 'auto',
|
||||||
tabSelectsValue = true,
|
tabSelectsValue = true,
|
||||||
|
className,
|
||||||
value,
|
value,
|
||||||
width,
|
width,
|
||||||
}: SelectBaseProps<T>) {
|
}: SelectBaseProps<T>) {
|
||||||
@ -277,7 +278,7 @@ export function SelectBase<T>({
|
|||||||
width: inputSizesPixels(size),
|
width: inputSizesPixels(size),
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
className={cx('select-container', widthClass)}
|
className={cx('select-container', widthClass, className)}
|
||||||
{...commonSelectProps}
|
{...commonSelectProps}
|
||||||
{...creatableProps}
|
{...creatableProps}
|
||||||
{...asyncSelectProps}
|
{...asyncSelectProps}
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
// Libraries
|
|
||||||
import React, { PureComponent, ChangeEvent } from 'react';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import { FormLabel } from '../FormLabel/FormLabel';
|
|
||||||
import { FormField } from '../FormField/FormField';
|
|
||||||
import { StatsPicker } from '../StatsPicker/StatsPicker';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import Select from '../Forms/Legacy/Select/Select';
|
|
||||||
import {
|
|
||||||
ReduceDataOptions,
|
|
||||||
DEFAULT_FIELD_DISPLAY_VALUES_LIMIT,
|
|
||||||
ReducerID,
|
|
||||||
toNumberString,
|
|
||||||
toIntegerOrUndefined,
|
|
||||||
SelectableValue,
|
|
||||||
} from '@grafana/data';
|
|
||||||
|
|
||||||
const showOptions: Array<SelectableValue<boolean>> = [
|
|
||||||
{
|
|
||||||
value: true,
|
|
||||||
label: 'All Values',
|
|
||||||
description: 'Each row in the response data',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: false,
|
|
||||||
label: 'Calculation',
|
|
||||||
description: 'Calculate a value based on the response',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
labelWidth?: number;
|
|
||||||
value: ReduceDataOptions;
|
|
||||||
onChange: (value: ReduceDataOptions, event?: React.SyntheticEvent<HTMLElement>) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FieldDisplayEditor extends PureComponent<Props> {
|
|
||||||
onShowValuesChange = (item: SelectableValue<boolean>) => {
|
|
||||||
const val = item.value === true;
|
|
||||||
this.props.onChange({ ...this.props.value, values: val });
|
|
||||||
};
|
|
||||||
|
|
||||||
onCalcsChange = (calcs: string[]) => {
|
|
||||||
this.props.onChange({ ...this.props.value, calcs });
|
|
||||||
};
|
|
||||||
|
|
||||||
onLimitChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.props.onChange({
|
|
||||||
...this.props.value,
|
|
||||||
limit: toIntegerOrUndefined(event.target.value),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { value } = this.props;
|
|
||||||
const { calcs, values, limit } = value;
|
|
||||||
|
|
||||||
const labelWidth = this.props.labelWidth || 5;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="gf-form">
|
|
||||||
<FormLabel width={labelWidth}>Show</FormLabel>
|
|
||||||
<Select
|
|
||||||
options={showOptions}
|
|
||||||
value={values ? showOptions[0] : showOptions[1]}
|
|
||||||
onChange={this.onShowValuesChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{values ? (
|
|
||||||
<FormField
|
|
||||||
label="Limit"
|
|
||||||
labelWidth={labelWidth}
|
|
||||||
placeholder={`${DEFAULT_FIELD_DISPLAY_VALUES_LIMIT}`}
|
|
||||||
onChange={this.onLimitChange}
|
|
||||||
value={toNumberString(limit)}
|
|
||||||
type="number"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="gf-form">
|
|
||||||
<FormLabel width={labelWidth}>Calc</FormLabel>
|
|
||||||
<StatsPicker
|
|
||||||
width={12}
|
|
||||||
placeholder="Choose Stat"
|
|
||||||
defaultStat={ReducerID.mean}
|
|
||||||
allowMultiple={false}
|
|
||||||
stats={calcs}
|
|
||||||
onChange={this.onCalcsChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
export { FieldDisplayEditor } from './FieldDisplayEditor';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SingleStatBaseOptions,
|
SingleStatBaseOptions,
|
||||||
sharedSingleStatPanelChangedHandler,
|
|
||||||
sharedSingleStatMigrationHandler,
|
sharedSingleStatMigrationHandler,
|
||||||
convertOldAngularValueMapping,
|
convertOldAngularValueMapping,
|
||||||
|
sharedSingleStatPanelChangedHandler,
|
||||||
} from './SingleStatBaseOptions';
|
} from './SingleStatBaseOptions';
|
||||||
|
@ -13,11 +13,11 @@ interface Props {
|
|||||||
stats: string[];
|
stats: string[];
|
||||||
allowMultiple?: boolean;
|
allowMultiple?: boolean;
|
||||||
defaultStat?: string;
|
defaultStat?: string;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StatsPicker extends PureComponent<Props> {
|
export class StatsPicker extends PureComponent<Props> {
|
||||||
static defaultProps = {
|
static defaultProps: Partial<Props> = {
|
||||||
width: 12,
|
|
||||||
allowMultiple: false,
|
allowMultiple: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,12 +62,13 @@ export class StatsPicker extends PureComponent<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { stats, allowMultiple, defaultStat, placeholder } = this.props;
|
const { stats, allowMultiple, defaultStat, placeholder, className } = this.props;
|
||||||
|
|
||||||
const select = fieldReducers.selectOptions(stats);
|
const select = fieldReducers.selectOptions(stats);
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
value={select.current}
|
value={select.current}
|
||||||
|
className={className}
|
||||||
isClearable={!defaultStat}
|
isClearable={!defaultStat}
|
||||||
isMulti={allowMultiple}
|
isMulti={allowMultiple}
|
||||||
isSearchable={true}
|
isSearchable={true}
|
||||||
|
@ -64,7 +64,7 @@ export function getColumns(data: DataFrame, availableWidth: number, columnMinWid
|
|||||||
columns.push({
|
columns.push({
|
||||||
Cell,
|
Cell,
|
||||||
id: field.name,
|
id: field.name,
|
||||||
Header: field.name,
|
Header: field.config.title ?? field.name,
|
||||||
accessor: field.name,
|
accessor: field.name,
|
||||||
width: fieldTableOptions.width,
|
width: fieldTableOptions.width,
|
||||||
});
|
});
|
||||||
|
@ -15,18 +15,23 @@ export const ReduceTransformerEditor: React.FC<TransformerUIProps<ReduceTransfor
|
|||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<StatsPicker
|
<div className="gf-form-inline">
|
||||||
width={25}
|
<div className="gf-form gf-form--grow">
|
||||||
placeholder="Choose Stat"
|
<div className="gf-form-label width-8">Calculations</div>
|
||||||
allowMultiple
|
<StatsPicker
|
||||||
stats={options.reducers || []}
|
className="flex-grow-1"
|
||||||
onChange={stats => {
|
placeholder="Choose Stat"
|
||||||
onChange({
|
allowMultiple
|
||||||
...options,
|
stats={options.reducers || []}
|
||||||
reducers: stats as ReducerID[],
|
onChange={stats => {
|
||||||
});
|
onChange({
|
||||||
}}
|
...options,
|
||||||
/>
|
reducers: stats as ReducerID[],
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,6 +39,6 @@ export const reduceTransformRegistryItem: TransformerRegistyItem<ReduceTransform
|
|||||||
id: DataTransformerID.reduce,
|
id: DataTransformerID.reduce,
|
||||||
editor: ReduceTransformerEditor,
|
editor: ReduceTransformerEditor,
|
||||||
transformation: standardTransformers.reduceTransformer,
|
transformation: standardTransformers.reduceTransformer,
|
||||||
name: 'Reduce',
|
name: standardTransformers.reduceTransformer.name,
|
||||||
description: 'Return a DataFrame with the reduction results',
|
description: standardTransformers.reduceTransformer.description,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user