mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 20:24:18 -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 { DataTransformerID } from './ids';
|
||||
import { toDataFrame, toDataFrameDTO } from '../../dataframe/processDataFrame';
|
||||
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||
import { reduceTransformer } from './reduce';
|
||||
import { transformDataFrame } from '../transformDataFrame';
|
||||
import { Field, FieldType } from '../../types';
|
||||
import { ArrayVector } from '../../vector';
|
||||
|
||||
const seriesWithValues = toDataFrame({
|
||||
const seriesAWithSingleField = toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{ name: 'A', values: [1, 2, 3, 4] }, // Numbers
|
||||
{ name: 'B', values: ['a', 'b', 'c', 'd'] }, // Strings
|
||||
{ name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000] },
|
||||
{ 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(() => {
|
||||
mockTransformationsRegistry([reduceTransformer]);
|
||||
});
|
||||
it('filters by include', () => {
|
||||
|
||||
it('reduces multiple data frames with many fields', () => {
|
||||
const cfg = {
|
||||
id: DataTransformerID.reduce,
|
||||
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];
|
||||
expect(processed.fields.length).toBe(5);
|
||||
expect(toDataFrameDTO(processed)).toMatchSnapshot();
|
||||
const processed = transformDataFrame([cfg], [seriesAWithMultipleFields, seriesBWithMultipleFields]);
|
||||
const expected: Field[] = [
|
||||
{
|
||||
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 { MatcherConfig, DataTransformerInfo } from '../../types/transformations';
|
||||
import { ReducerID, fieldReducers, reduceField } from '../fieldReducer';
|
||||
import { DataTransformerInfo, MatcherConfig } from '../../types/transformations';
|
||||
import { fieldReducers, reduceField, ReducerID } from '../fieldReducer';
|
||||
import { alwaysFieldMatcher } from '../matchers/predicates';
|
||||
import { DataFrame, Field, FieldType } from '../../types/dataFrame';
|
||||
import { ArrayVector } from '../../vector/ArrayVector';
|
||||
import { KeyValue } from '../../types/data';
|
||||
import { guessFieldTypeForField } from '../../dataframe/processDataFrame';
|
||||
import { getFieldMatcher } from '../matchers';
|
||||
import { FieldMatcherID } from '../matchers/ids';
|
||||
import { filterFieldsTransformer } from './filter';
|
||||
|
||||
export interface ReduceTransformerOptions {
|
||||
reducers: ReducerID[];
|
||||
@ -15,10 +17,10 @@ export interface ReduceTransformerOptions {
|
||||
|
||||
export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> = {
|
||||
id: DataTransformerID.reduce,
|
||||
name: 'Reducer',
|
||||
description: 'Return a DataFrame with the reduction results',
|
||||
name: 'Reduce',
|
||||
description: 'Reduce all rows to a single row and concatenate all results',
|
||||
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[]) => {
|
||||
const processed: DataFrame[] = [];
|
||||
for (const series of data) {
|
||||
|
||||
for (let seriesIndex = 0; seriesIndex < data.length; seriesIndex++) {
|
||||
const series = data[seriesIndex];
|
||||
const values: ArrayVector[] = [];
|
||||
const fields: Field[] = [];
|
||||
const byId: KeyValue<ArrayVector> = {};
|
||||
|
||||
values.push(new ArrayVector()); // The name
|
||||
fields.push({
|
||||
name: 'Field',
|
||||
@ -43,10 +48,12 @@ export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> =
|
||||
values: values[0],
|
||||
config: {},
|
||||
});
|
||||
|
||||
for (const info of calculators) {
|
||||
const vals = new ArrayVector();
|
||||
byId[info.id] = vals;
|
||||
values.push(vals);
|
||||
|
||||
fields.push({
|
||||
name: info.id,
|
||||
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++) {
|
||||
const field = series.fields[i];
|
||||
|
||||
if (field.type === FieldType.time) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matcher(field)) {
|
||||
const results = reduceField({
|
||||
field,
|
||||
reducers,
|
||||
});
|
||||
|
||||
// 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) {
|
||||
const v = results[info.id];
|
||||
byId[info.id].buffer.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const f of fields) {
|
||||
const t = guessFieldTypeForField(f);
|
||||
|
||||
if (t) {
|
||||
f.type = t;
|
||||
}
|
||||
}
|
||||
|
||||
processed.push({
|
||||
...series, // Same properties, different fields
|
||||
fields,
|
||||
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,
|
||||
size = 'auto',
|
||||
tabSelectsValue = true,
|
||||
className,
|
||||
value,
|
||||
width,
|
||||
}: SelectBaseProps<T>) {
|
||||
@ -277,7 +278,7 @@ export function SelectBase<T>({
|
||||
width: inputSizesPixels(size),
|
||||
}),
|
||||
}}
|
||||
className={cx('select-container', widthClass)}
|
||||
className={cx('select-container', widthClass, className)}
|
||||
{...commonSelectProps}
|
||||
{...creatableProps}
|
||||
{...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 {
|
||||
SingleStatBaseOptions,
|
||||
sharedSingleStatPanelChangedHandler,
|
||||
sharedSingleStatMigrationHandler,
|
||||
convertOldAngularValueMapping,
|
||||
sharedSingleStatPanelChangedHandler,
|
||||
} from './SingleStatBaseOptions';
|
||||
|
@ -13,11 +13,11 @@ interface Props {
|
||||
stats: string[];
|
||||
allowMultiple?: boolean;
|
||||
defaultStat?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export class StatsPicker extends PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
width: 12,
|
||||
static defaultProps: Partial<Props> = {
|
||||
allowMultiple: false,
|
||||
};
|
||||
|
||||
@ -62,12 +62,13 @@ export class StatsPicker extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { stats, allowMultiple, defaultStat, placeholder } = this.props;
|
||||
const { stats, allowMultiple, defaultStat, placeholder, className } = this.props;
|
||||
|
||||
const select = fieldReducers.selectOptions(stats);
|
||||
return (
|
||||
<Select
|
||||
value={select.current}
|
||||
className={className}
|
||||
isClearable={!defaultStat}
|
||||
isMulti={allowMultiple}
|
||||
isSearchable={true}
|
||||
|
@ -64,7 +64,7 @@ export function getColumns(data: DataFrame, availableWidth: number, columnMinWid
|
||||
columns.push({
|
||||
Cell,
|
||||
id: field.name,
|
||||
Header: field.name,
|
||||
Header: field.config.title ?? field.name,
|
||||
accessor: field.name,
|
||||
width: fieldTableOptions.width,
|
||||
});
|
||||
|
@ -15,8 +15,11 @@ export const ReduceTransformerEditor: React.FC<TransformerUIProps<ReduceTransfor
|
||||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<div className="gf-form-label width-8">Calculations</div>
|
||||
<StatsPicker
|
||||
width={25}
|
||||
className="flex-grow-1"
|
||||
placeholder="Choose Stat"
|
||||
allowMultiple
|
||||
stats={options.reducers || []}
|
||||
@ -27,6 +30,8 @@ export const ReduceTransformerEditor: React.FC<TransformerUIProps<ReduceTransfor
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -34,6 +39,6 @@ export const reduceTransformRegistryItem: TransformerRegistyItem<ReduceTransform
|
||||
id: DataTransformerID.reduce,
|
||||
editor: ReduceTransformerEditor,
|
||||
transformation: standardTransformers.reduceTransformer,
|
||||
name: 'Reduce',
|
||||
description: 'Return a DataFrame with the reduction results',
|
||||
name: standardTransformers.reduceTransformer.name,
|
||||
description: standardTransformers.reduceTransformer.description,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user