From 5b2ba605d5a68981db475b73b4e1e440d751cd31 Mon Sep 17 00:00:00 2001 From: Oscar Kilhed Date: Thu, 11 Jan 2024 15:29:41 +0100 Subject: [PATCH] Show transformations when editing scene dashboard --- .../PanelDataPane/PanelDataPane.tsx | 2 +- .../EmptyTransformationsMessage.tsx | 37 ++++++ .../PanelDataTransformationsTab.test.tsx | 64 +++++++++++ .../PanelDataTransformationsTab.tsx | 106 ++++++++++++++++++ .../PanelDataTransformationsTab.tsx | 46 -------- .../TransformationsEditor.tsx | 40 +------ 6 files changed, 214 insertions(+), 81 deletions(-) create mode 100644 public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/EmptyTransformationsMessage.tsx create mode 100644 public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/PanelDataTransformationsTab.test.tsx create mode 100644 public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/PanelDataTransformationsTab.tsx delete mode 100644 public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformationsTab.tsx diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx index 891ffe9e780..a187790f63b 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx @@ -16,7 +16,7 @@ import { VizPanelManager } from '../VizPanelManager'; import { PanelDataAlertingTab } from './PanelDataAlertingTab'; import { PanelDataQueriesTab } from './PanelDataQueriesTab'; -import { PanelDataTransformationsTab } from './PanelDataTransformationsTab'; +import { PanelDataTransformationsTab } from './PanelDataTransformations/PanelDataTransformationsTab'; import { PanelDataPaneTab } from './types'; export interface PanelDataPaneState extends SceneObjectState { diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/EmptyTransformationsMessage.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/EmptyTransformationsMessage.tsx new file mode 100644 index 00000000000..c452a9cfd11 --- /dev/null +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/EmptyTransformationsMessage.tsx @@ -0,0 +1,37 @@ +import React from 'react'; + +import { selectors } from '@grafana/e2e-selectors'; +import { Box, Button, Stack, Text } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; + +interface EmptyTransformationsProps { + onShowPicker: () => void; +} +export function EmptyTransformationsMessage(props: EmptyTransformationsProps) { + return ( + + + + Start transforming data + + + + Transformations allow data to be changed in various ways before your visualization is shown. +
+ This includes joining data together, renaming fields, making calculations, formatting data for display, and + more. +
+
+ +
+
+ ); +} diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/PanelDataTransformationsTab.test.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/PanelDataTransformationsTab.test.tsx new file mode 100644 index 00000000000..3de27eba3fa --- /dev/null +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/PanelDataTransformationsTab.test.tsx @@ -0,0 +1,64 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { FieldType, LoadingState, TimeRange, standardTransformersRegistry, toDataFrame } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; +import { SceneDataTransformer } from '@grafana/scenes'; +import { getStandardTransformers } from 'app/features/transformers/standardTransformers'; + +import { PanelDataTransformationsTab, PanelDataTransformationsTabRendered } from './PanelDataTransformationsTab'; + +function createPanelManagerMock(sceneDataTransformer: SceneDataTransformer) { + return { + panelManager: { + useState: () => { + return { + panel: { + useState: () => { + return { $data: sceneDataTransformer }; + }, + }, + }; + }, + }, + } as unknown as PanelDataTransformationsTab; +} + +describe('PanelDataTransformationsTab', () => { + it('renders empty message when there are no transformations', async () => { + const modelMock = createPanelManagerMock(new SceneDataTransformer({ transformations: [] })); + render(); + + await screen.findByTestId(selectors.components.Transforms.noTransformationsMessage); + }); + + it('renders transformations when there are transformations', async () => { + standardTransformersRegistry.setInit(getStandardTransformers); + const modelMock = createPanelManagerMock( + new SceneDataTransformer({ + data: { + timeRange: {} as unknown as TimeRange, + state: {} as unknown as LoadingState, + series: [ + toDataFrame({ + name: 'A', + fields: [ + { name: 'time', type: FieldType.time, values: [100, 200, 300] }, + { name: 'values', type: FieldType.number, values: [1, 2, 3] }, + ], + }), + ], + }, + transformations: [ + { + id: 'calculateField', + options: {}, + }, + ], + }) + ); + render(); + + await screen.findByText('1 - Add field from calculation'); + }); +}); diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/PanelDataTransformationsTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/PanelDataTransformationsTab.tsx new file mode 100644 index 00000000000..767b34423d4 --- /dev/null +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/PanelDataTransformationsTab.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { DragDropContext, Droppable } from 'react-beautiful-dnd'; + +import { IconName } from '@grafana/data'; +import { SceneObjectBase, SceneComponentProps, SceneDataTransformer } from '@grafana/scenes'; +import { Container, CustomScrollbar } from '@grafana/ui'; +import { TransformationOperationRows } from 'app/features/dashboard/components/TransformationsEditor/TransformationOperationRows'; +import { TransformationsEditorTransformation } from 'app/features/dashboard/components/TransformationsEditor/types'; + +import { VizPanelManager } from '../../VizPanelManager'; +import { PanelDataPaneTabState, PanelDataPaneTab } from '../types'; + +import { EmptyTransformationsMessage } from './EmptyTransformationsMessage'; + +interface PanelDataTransformationsTabState extends PanelDataPaneTabState {} + +export class PanelDataTransformationsTab + extends SceneObjectBase + implements PanelDataPaneTab +{ + static Component = PanelDataTransformationsTabRendered; + tabId = 'transformations'; + icon: IconName = 'process'; + private _panelManager: VizPanelManager; + + getTabLabel() { + return 'Transformations'; + } + + getItemsCount() { + return null; + } + + constructor(panelManager: VizPanelManager) { + super({}); + + this._panelManager = panelManager; + } + + get panelManager() { + return this._panelManager; + } +} + +interface TransformationEditorProps { + sceneDataTransformer: SceneDataTransformer; +} + +function TransformationsEditor(props: TransformationEditorProps) { + const dataState = props.sceneDataTransformer.useState(); + const transformationEditorRows: TransformationsEditorTransformation[] = []; + + let i = 0; // Ids need to be unique for drag to change order to work + for (const t of dataState.transformations) { + if ('id' in t) { + transformationEditorRows.push({ id: `${i} - ${t.id}`, transformation: t }); + i++; + } + } + + return ( + {}}> + + {(provided) => { + return ( +
+ {}} + onRemove={() => {}} + configs={transformationEditorRows} + data={{ + series: dataState.data?.series || [], + annotations: dataState.data?.annotations || [], + }} + > + {provided.placeholder} +
+ ); + }} +
+
+ ); +} + +export function PanelDataTransformationsTabRendered({ model }: SceneComponentProps) { + const panelManagerState = model.panelManager.useState(); + const panelState = panelManagerState.panel.useState(); + + if (!(panelState.$data instanceof SceneDataTransformer)) { + return; + } + + const dataState = panelState.$data.useState(); + + return ( + + + {dataState.transformations.length < 1 ? ( + {}}> + ) : ( + + )} + + + ); +} diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformationsTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformationsTab.tsx deleted file mode 100644 index c0025078ed7..00000000000 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformationsTab.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; - -import { IconName } from '@grafana/data'; -import { SceneObjectBase, SceneComponentProps } from '@grafana/scenes'; - -import { VizPanelManager } from '../VizPanelManager'; - -import { PanelDataPaneTabState, PanelDataPaneTab } from './types'; - -interface PanelDataTransformationsTabState extends PanelDataPaneTabState {} - -export class PanelDataTransformationsTab - extends SceneObjectBase - implements PanelDataPaneTab -{ - static Component = PanelDataTransformationsTabRendered; - tabId = 'transformations'; - icon: IconName = 'process'; - private _panelManager: VizPanelManager; - - getTabLabel() { - return 'Transformations'; - } - - getItemsCount() { - return 0; - } - - constructor(panelManager: VizPanelManager) { - super({}); - - this._panelManager = panelManager; - } - - get panelManager() { - return this._panelManager; - } -} - -function PanelDataTransformationsTabRendered({ model }: SceneComponentProps) { - // const { dataRef } = model.useState(); - // const dataObj = dataRef.resolve(); - // // const { transformations } = dataObj.useState(); - - return
TODO Transformations
; -} diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx index 62f881f8f4a..e96768207bd 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx @@ -21,12 +21,9 @@ import { withTheme, IconButton, ButtonGroup, - Box, - Text, - Stack, } from '@grafana/ui'; import config from 'app/core/config'; -import { Trans } from 'app/core/internationalization'; +import { EmptyTransformationsMessage } from 'app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformations/EmptyTransformationsMessage'; import { PanelModel } from '../../state'; import { PanelNotSupported } from '../PanelEditor/PanelNotSupported'; @@ -258,36 +255,11 @@ class UnThemedTransformationsEditor extends React.PureComponent { return ( - - - - Start transforming data - - - - Transformations allow data to be changed in various ways before your visualization is shown. -
- This includes joining data together, renaming fields, making calculations, formatting data for display, - and more. -
-
- -
-
+ { + this.setState({ showPicker: true }); + }} + > ); };