Transformations: Fixed transformation crash issue (#25152)

* Transformations: Fixed transformation crash issue

* Updated
This commit is contained in:
Torkel Ödegaard 2020-05-28 08:06:24 +02:00 committed by GitHub
parent 6a4f45625c
commit 3833aa416d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 31 deletions

View File

@ -0,0 +1,26 @@
import { e2e } from '@grafana/e2e';
const PANEL_UNDER_TEST = 'Random walk series';
e2e.scenario({
describeName: 'Panel edit tests - transformations',
itName: 'Tests transformations editor',
addScenarioDataSource: false,
addScenarioDashBoard: false,
skipScenario: false,
scenario: () => {
e2e.flows.openDashboard('5SdHCadmz');
e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST);
e2e.components.Tab.title('Transform')
.should('be.visible')
.click();
e2e.components.TransformTab.newTransform('Reduce')
.should('be.visible')
.click();
e2e.components.Transforms.Reduce.calculationsLabel().should('be.visible');
},
});

View File

@ -97,6 +97,12 @@ export const Components = {
},
TransformTab: {
content: 'Transform editor tab content',
newTransform: (title: string) => `New transform ${title}`,
},
Transforms: {
Reduce: {
calculationsLabel: 'Transform calculations label',
},
},
QueryEditorToolbarItem: {
button: (title: string) => `QueryEditor toolbar item button ${title}`,

View File

@ -9,6 +9,7 @@ import {
} from '@grafana/data';
import { ReduceTransformerOptions } from '@grafana/data/src/transformations/transformers/reduce';
import { selectors } from '@grafana/e2e-selectors';
// TODO: Minimal implementation, needs some <3
export const ReduceTransformerEditor: React.FC<TransformerUIProps<ReduceTransformerOptions>> = ({
@ -18,7 +19,9 @@ export const ReduceTransformerEditor: React.FC<TransformerUIProps<ReduceTransfor
return (
<div className="gf-form-inline">
<div className="gf-form gf-form--grow">
<div className="gf-form-label width-8">Calculations</div>
<div className="gf-form-label width-8" aria-label={selectors.components.Transforms.Reduce.calculationsLabel}>
Calculations
</div>
<StatsPicker
className="flex-grow-1"
placeholder="Choose Stat"

View File

@ -2,7 +2,6 @@ import React, { useCallback } from 'react';
import { config } from 'app/core/config';
import { css } from 'emotion';
import { IconName, stylesFactory, Tab, TabContent, TabsBar } from '@grafana/ui';
import { DataTransformerConfig } from '@grafana/data';
import { PanelEditorTab, PanelEditorTabId } from './types';
import { DashboardModel } from '../../state';
import { QueriesTab } from '../../panel_editor/QueriesTab';
@ -42,10 +41,6 @@ export const PanelEditorTabs: React.FC<PanelEditorTabsProps> = ({ panel, dashboa
return null;
}
const onTransformersChange = (transformers: DataTransformerConfig[]) => {
panel.setTransformations(transformers);
};
return (
<div className={styles.wrapper}>
<TabsBar className={styles.tabBar}>
@ -65,13 +60,7 @@ export const PanelEditorTabs: React.FC<PanelEditorTabsProps> = ({ panel, dashboa
<TabContent className={styles.tabContent}>
{activeTab.id === PanelEditorTabId.Query && <QueriesTab panel={panel} dashboard={dashboard} />}
{activeTab.id === PanelEditorTabId.Alert && <AlertTab panel={panel} dashboard={dashboard} />}
{activeTab.id === PanelEditorTabId.Transform && (
<TransformationsEditor
transformations={panel.transformations || []}
onChange={onTransformersChange}
panel={panel}
/>
)}
{activeTab.id === PanelEditorTabId.Transform && <TransformationsEditor panel={panel} />}
</TabContent>
</div>
);

View File

@ -28,17 +28,25 @@ import { PanelModel } from '../../state';
interface Props {
panel: PanelModel;
onChange: (transformations: DataTransformerConfig[]) => void;
transformations: DataTransformerConfig[];
}
interface State {
data?: DataFrame[];
data: DataFrame[];
transformations: DataTransformerConfig[];
}
export class TransformationsEditor extends React.PureComponent<Props, State> {
subscription?: Unsubscribable;
constructor(props: Props) {
super(props);
this.state = {
transformations: props.panel.transformations || [],
data: [],
};
}
componentDidMount() {
this.subscription = this.props.panel
.getQueryRunner()
@ -54,9 +62,15 @@ export class TransformationsEditor extends React.PureComponent<Props, State> {
}
}
onChange(transformations: DataTransformerConfig[]) {
this.props.panel.setTransformations(transformations);
this.setState({ transformations });
}
onTransformationAdd = (selectable: SelectableValue<string>) => {
const { transformations, onChange } = this.props;
onChange([
const { transformations } = this.state;
this.onChange([
...transformations,
{
id: selectable.value as string,
@ -66,17 +80,17 @@ export class TransformationsEditor extends React.PureComponent<Props, State> {
};
onTransformationChange = (idx: number, config: DataTransformerConfig) => {
const { transformations, onChange } = this.props;
const { transformations } = this.state;
const next = Array.from(transformations);
next[idx] = config;
onChange(next);
this.onChange(next);
};
onTransformationRemove = (idx: number) => {
const { transformations, onChange } = this.props;
const { transformations } = this.state;
const next = Array.from(transformations);
next.splice(idx, 1);
onChange(next);
this.onChange(next);
};
renderTransformationSelector = () => {
@ -108,10 +122,7 @@ export class TransformationsEditor extends React.PureComponent<Props, State> {
};
renderTransformationEditors = () => {
const { transformations } = this.props;
const { data } = this.state;
const preTransformData = data ?? [];
const { data, transformations } = this.state;
return (
<>
@ -123,7 +134,7 @@ export class TransformationsEditor extends React.PureComponent<Props, State> {
return null;
}
const input = transformDataFrame(transformations.slice(0, i), preTransformData);
const input = transformDataFrame(transformations.slice(0, i), data);
const output = transformDataFrame(transformations.slice(i), input);
if (transformationUI) {
@ -182,6 +193,7 @@ export class TransformationsEditor extends React.PureComponent<Props, State> {
title={t.name}
description={t.description}
actions={<Button>Select</Button>}
ariaLabel={selectors.components.TransformTab.newTransform(t.name)}
onClick={() => {
this.onTransformationAdd({ value: t.id });
}}
@ -194,14 +206,17 @@ export class TransformationsEditor extends React.PureComponent<Props, State> {
}
render() {
const hasTransformationsConfigured = this.props.transformations.length > 0;
const { transformations } = this.state;
const hasTransforms = transformations.length > 0;
return (
<CustomScrollbar autoHeightMin="100%">
<Container padding="md">
<div aria-label={selectors.components.TransformTab.content}>
{!hasTransformationsConfigured && this.renderNoAddedTransformsState()}
{hasTransformationsConfigured && this.renderTransformationEditors()}
{hasTransformationsConfigured && this.renderTransformationSelector()}
{!hasTransforms && this.renderNoAddedTransformsState()}
{hasTransforms && this.renderTransformationEditors()}
{hasTransforms && this.renderTransformationSelector()}
</div>
</Container>
</CustomScrollbar>