diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx index 9b9a7759e49..aca75152cb4 100644 --- a/public/app/features/alerting/AlertTab.tsx +++ b/public/app/features/alerting/AlertTab.tsx @@ -1,7 +1,6 @@ import React, { PureComponent } from 'react'; import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; -import { css } from 'emotion'; -import { Alert, Button, IconName, CustomScrollbar, Container, HorizontalGroup, ConfirmModal, Modal } from '@grafana/ui'; +import { Alert, Button, ConfirmModal, Container, CustomScrollbar, HorizontalGroup, IconName, Modal } from '@grafana/ui'; import { selectors } from '@grafana/e2e-selectors'; import { AngularComponent, getAngularLoader, getDataSourceSrv } from '@grafana/runtime'; import { getAlertingValidationMessage } from './getAlertingValidationMessage'; @@ -14,8 +13,7 @@ import { DashboardModel } from '../dashboard/state/DashboardModel'; import { PanelModel } from '../dashboard/state/PanelModel'; import { TestRuleResult } from './TestRuleResult'; import { AppNotificationSeverity, StoreState } from 'app/types'; -import { updateLocation } from 'app/core/actions'; -import { PanelEditorTabId } from '../dashboard/components/PanelEditor/types'; +import { PanelNotSupported } from '../dashboard/components/PanelEditor/PanelNotSupported'; interface OwnProps { dashboard: DashboardModel; @@ -26,14 +24,12 @@ interface ConnectedProps { angularPanelComponent?: AngularComponent | null; } -interface DispatchProps { - updateLocation: typeof updateLocation; -} +interface DispatchProps {} export type Props = OwnProps & ConnectedProps & DispatchProps; interface State { - validatonMessage: string; + validationMessage: string; showStateHistory: boolean; showDeleteConfirmation: boolean; showTestRule: boolean; @@ -45,7 +41,7 @@ class UnConnectedAlertTab extends PureComponent { panelCtrl: any; state: State = { - validatonMessage: '', + validationMessage: '', showStateHistory: false, showDeleteConfirmation: false, showTestRule: false, @@ -94,15 +90,15 @@ class UnConnectedAlertTab extends PureComponent { this.component = loader.load(this.element, scopeProps, template); - const validatonMessage = await getAlertingValidationMessage( + const validationMessage = await getAlertingValidationMessage( panel.transformations, panel.targets, getDataSourceSrv(), panel.datasource ); - if (validatonMessage) { - this.setState({ validatonMessage }); + if (validationMessage) { + this.setState({ validationMessage }); } } @@ -112,37 +108,11 @@ class UnConnectedAlertTab extends PureComponent { this.forceUpdate(); }; - switchToQueryTab = () => { - const { updateLocation } = this.props; - updateLocation({ query: { tab: PanelEditorTabId.Query }, partial: true }); - }; - - onToggleModal = (prop: keyof Omit) => { + onToggleModal = (prop: keyof Omit) => { const value = this.state[prop]; this.setState({ ...this.state, [prop]: !value }); }; - renderValidationMessage = () => { - const { validatonMessage } = this.state; - - return ( -
-

{validatonMessage}

-
-
- -
-
- ); - }; - renderTestRule = () => { if (!this.state.showTestRule) { return null; @@ -213,11 +183,11 @@ class UnConnectedAlertTab extends PureComponent { render() { const { alert, transformations } = this.props.panel; - const { validatonMessage } = this.state; + const { validationMessage } = this.state; const hasTransformations = transformations && transformations.length > 0; - if (!alert && validatonMessage) { - return this.renderValidationMessage(); + if (!alert && validationMessage) { + return ; } const model = { @@ -253,7 +223,7 @@ class UnConnectedAlertTab extends PureComponent { )} - {!alert && !validatonMessage && } + {!alert && !validationMessage && } @@ -272,6 +242,6 @@ const mapStateToProps: MapStateToProps = ( }; }; -const mapDispatchToProps: MapDispatchToProps = { updateLocation }; +const mapDispatchToProps: MapDispatchToProps = {}; export const AlertTab = connect(mapStateToProps, mapDispatchToProps)(UnConnectedAlertTab); diff --git a/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.test.tsx b/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.test.tsx new file mode 100644 index 00000000000..c5d020dee74 --- /dev/null +++ b/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.test.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { PanelNotSupported, Props } from './PanelNotSupported'; +import { updateLocation } from '../../../../core/actions'; +import { PanelEditorTabId } from './types'; + +const setupTestContext = (options: Partial) => { + const defaults: Props = { + message: '', + dispatch: jest.fn(), + }; + + const props = { ...defaults, ...options }; + render(); + + return { props }; +}; + +describe('PanelNotSupported', () => { + describe('when component is mounted', () => { + it('then the supplied message should be shown', () => { + setupTestContext({ message: 'Expected message' }); + + expect(screen.getByRole('heading', { name: /expected message/i })).toBeInTheDocument(); + }); + + it('then the back to queries button should exist', () => { + setupTestContext({ message: 'Expected message' }); + + expect(screen.getByRole('button', { name: /go back to queries/i })).toBeInTheDocument(); + }); + }); + + describe('when the back to queries button is clicked', () => { + it('then correct action should be dispatched', () => { + const { + props: { dispatch }, + } = setupTestContext({}); + + userEvent.click(screen.getByRole('button', { name: /go back to queries/i })); + + expect(dispatch).toHaveBeenCalledTimes(1); + expect(dispatch).toHaveBeenCalledWith(updateLocation({ query: { tab: PanelEditorTabId.Query }, partial: true })); + }); + }); +}); diff --git a/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.tsx b/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.tsx new file mode 100644 index 00000000000..ee81503118a --- /dev/null +++ b/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.tsx @@ -0,0 +1,33 @@ +import React, { FC, useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import { Dispatch } from 'redux'; +import { Button, VerticalGroup } from '@grafana/ui'; + +import { Layout } from '@grafana/ui/src/components/Layout/Layout'; +import { PanelEditorTabId } from './types'; +import { updateLocation } from '../../../../core/actions'; + +export interface Props { + message: string; + dispatch?: Dispatch; +} + +export const PanelNotSupported: FC = ({ message, dispatch: propsDispatch }) => { + const dispatch = propsDispatch ? propsDispatch : useDispatch(); + const onBackToQueries = useCallback(() => { + dispatch(updateLocation({ query: { tab: PanelEditorTabId.Query }, partial: true })); + }, [dispatch]); + + return ( + + +

{message}

+
+ +
+
+
+ ); +}; diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx index dc02c12633d..c3ff05d5f21 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { + Alert, Button, Container, CustomScrollbar, @@ -10,14 +11,14 @@ import { VerticalGroup, } from '@grafana/ui'; import { + DataFrame, DataTransformerConfig, + DocsId, GrafanaTheme, + PanelData, SelectableValue, standardTransformersRegistry, transformDataFrame, - DataFrame, - PanelData, - DocsId, } from '@grafana/data'; import { TransformationOperationRow } from './TransformationOperationRow'; import { Card, CardProps } from '../../../../core/components/Card/Card'; @@ -27,6 +28,8 @@ import { Unsubscribable } from 'rxjs'; import { PanelModel } from '../../state'; import { getDocsLink } from 'app/core/utils/docsLinks'; import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; +import { PanelNotSupported } from '../PanelEditor/PanelNotSupported'; +import { AppNotificationSeverity } from '../../../../types'; interface TransformationsEditorProps { panel: PanelModel; @@ -285,14 +288,27 @@ export class TransformationsEditor extends React.PureComponent 0; + if (!hasTransforms && alert) { + return ; + } + return (
+ {hasTransforms && alert ? ( + + ) : null} {!hasTransforms && this.renderNoAddedTransformsState()} {hasTransforms && this.renderTransformationEditors()} {hasTransforms && this.renderTransformationSelector()}