diff --git a/public/app/features/alerting/NextGenAlertingPage.tsx b/public/app/features/alerting/NextGenAlertingPage.tsx index 24e5694c6c1..547c9c6420b 100644 --- a/public/app/features/alerting/NextGenAlertingPage.tsx +++ b/public/app/features/alerting/NextGenAlertingPage.tsx @@ -3,8 +3,8 @@ import { hot } from 'react-hot-loader'; import { connect, ConnectedProps } from 'react-redux'; import { css } from 'emotion'; import { GrafanaTheme, SelectableValue } from '@grafana/data'; +import { locationService } from '@grafana/runtime'; import { PageToolbar, stylesFactory, ToolbarButton } from '@grafana/ui'; - import { config } from 'app/core/config'; import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper'; import { AlertingQueryEditor } from './components/AlertingQueryEditor'; @@ -97,7 +97,9 @@ class NextGenAlertingPageUnconnected extends PureComponent { } }; - onDiscard = () => {}; + onDiscard = () => { + locationService.replace(`${config.appSubUrl}/alerting/list`); + }; onTest = () => { const { alertDefinition, evaluateAlertDefinition, evaluateNotSavedAlertDefinition } = this.props; @@ -125,12 +127,12 @@ class NextGenAlertingPageUnconnected extends PureComponent { render() { const { alertDefinition, - getInstances, uiState, updateAlertDefinitionUiState, - queryRunner, getQueryOptions, + getInstances, onRunQueries, + queryRunner, } = this.props; const styles = getStyles(config.theme); @@ -146,10 +148,10 @@ class NextGenAlertingPageUnconnected extends PureComponent { leftPaneComponents={[ , , diff --git a/public/app/features/alerting/components/AlertingQueryPreview.tsx b/public/app/features/alerting/components/AlertingQueryPreview.tsx index bc947d88978..6a7e0537838 100644 --- a/public/app/features/alerting/components/AlertingQueryPreview.tsx +++ b/public/app/features/alerting/components/AlertingQueryPreview.tsx @@ -3,10 +3,12 @@ import { useObservable } from 'react-use'; import { css } from 'emotion'; import AutoSizer from 'react-virtualized-auto-sizer'; import { DataFrame, DataQuery, GrafanaTheme, PanelData } from '@grafana/data'; -import { Button, Icon, Tab, TabContent, TabsBar, useStyles } from '@grafana/ui'; -import { PanelQueryRunner } from '../../query/state/PanelQueryRunner'; +import { config } from '@grafana/runtime'; +import { Icon, Tab, TabContent, TabsBar } from '@grafana/ui'; import { PreviewQueryTab } from './PreviewQueryTab'; import { PreviewInstancesTab } from './PreviewInstancesTab'; +import { EmptyState } from './EmptyState'; +import { PanelQueryRunner } from '../../query/state/PanelQueryRunner'; enum Tabs { Query = 'query', @@ -26,10 +28,9 @@ interface Props { onRunQueries: () => void; } -export const AlertingQueryPreview: FC = ({ getInstances, onRunQueries, onTest, queryRunner, queries }) => { +export const AlertingQueryPreview: FC = ({ getInstances, onRunQueries, onTest, queries, queryRunner }) => { const [activeTab, setActiveTab] = useState(Tabs.Query); - const styles = useStyles(getStyles); - + const styles = getStyles(config.theme); const observable = useMemo(() => queryRunner.getData({ withFieldConfig: true, withTransforms: true }), []); const data = useObservable(observable); const instances = getInstances(); @@ -49,59 +50,66 @@ export const AlertingQueryPreview: FC = ({ getInstances, onRunQueries, on })} - {data && data.state === 'Error' ? ( -
-

There was an error :(

-
{data.error?.data?.error}
-
- ) : queries && queries.length > 0 ? ( - - {({ width, height }) => { - switch (activeTab) { - case Tabs.Instances: - return ( - 0} - instances={instances} - styles={styles} - width={width} - height={height} - onTest={onTest} - /> - ); - - case Tabs.Query: - default: - if (data) { - return ; - } - return ( -
-

Run queries to view data.

- -
- ); - } - }} -
- ) : ( -
-

No queries added.

-
Start adding queries to this alert and a visualisation for your queries will appear here.
-
- Learn more about how to create alert definitions -
-
- )} + {data && + (data.state === 'Error' ? ( + +
{data.error?.data?.error}
+
+ ) : ( + + ))}
); }; +interface PreviewProps { + queries: DataQuery[]; + instances: DataFrame[]; + onTest: () => void; + data: PanelData; + activeTab: string; + onRunQueries: () => void; +} + +const QueriesAndInstances: FC = ({ queries, instances, onTest, data, activeTab, onRunQueries }) => { + if (queries.length === 0) { + return ( + +
Start adding queries to this alert and a visualisation for your queries will appear here.
+
+ Learn more about how to create alert definitions +
+
+ ); + } + + return ( + + {({ width, height }) => { + switch (activeTab) { + case Tabs.Instances: + return ; + + case Tabs.Query: + default: + return ; + } + }} + + ); +}; + const getStyles = (theme: GrafanaTheme) => { return { wrapper: css` - label: alertDefinitionPreviewTabs; display: flex; flex-direction: column; width: 100%; @@ -112,19 +120,5 @@ const getStyles = (theme: GrafanaTheme) => { background: ${theme.colors.panelBg}; height: 100%; `, - noQueries: css` - color: ${theme.colors.textSemiWeak}; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - height: 100%; - width: 100%; - `, - noQueriesHeader: css` - color: ${theme.colors.textSemiWeak}; - `, }; }; - -export type PreviewStyles = ReturnType; diff --git a/public/app/features/alerting/components/EmptyState.tsx b/public/app/features/alerting/components/EmptyState.tsx new file mode 100644 index 00000000000..fc043b4025a --- /dev/null +++ b/public/app/features/alerting/components/EmptyState.tsx @@ -0,0 +1,37 @@ +import React, { FC, ReactNode } from 'react'; +import { css } from 'emotion'; +import { useStyles } from '@grafana/ui'; +import { GrafanaTheme } from '@grafana/data'; + +interface Props { + title: string; + children: ReactNode | ReactNode[]; +} + +export const EmptyState: FC = ({ children, title }) => { + const styles = useStyles(getStyles); + + return ( +
+

{title}

+ {children} +
+ ); +}; + +const getStyles = (theme: GrafanaTheme) => { + return { + emptyState: css` + color: ${theme.colors.textSemiWeak}; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; + `, + emptyStateHeader: css` + color: ${theme.colors.textSemiWeak}; + `, + }; +}; diff --git a/public/app/features/alerting/components/PreviewInstancesTab.tsx b/public/app/features/alerting/components/PreviewInstancesTab.tsx index 438c1e8d3d6..32b9081b5b5 100644 --- a/public/app/features/alerting/components/PreviewInstancesTab.tsx +++ b/public/app/features/alerting/components/PreviewInstancesTab.tsx @@ -1,25 +1,22 @@ import React, { FC } from 'react'; import { DataFrame } from '@grafana/data'; import { Button, Table } from '@grafana/ui'; -import { PreviewStyles } from './AlertingQueryPreview'; +import { EmptyState } from './EmptyState'; interface Props { instances: DataFrame[]; - isTested: boolean; - styles: PreviewStyles; width: number; height: number; onTest: () => void; } -export const PreviewInstancesTab: FC = ({ instances, isTested, onTest, height, styles, width }) => { - if (!isTested) { +export const PreviewInstancesTab: FC = ({ instances, onTest, height, width }) => { + if (!instances.length) { return ( -
-

You haven’t tested your alert yet.

+
In order to see your instances, you need to test your alert first.
-
+ ); } return ; diff --git a/public/app/features/alerting/components/PreviewQueryTab.tsx b/public/app/features/alerting/components/PreviewQueryTab.tsx index e9d3e4dad45..e19614bb472 100644 --- a/public/app/features/alerting/components/PreviewQueryTab.tsx +++ b/public/app/features/alerting/components/PreviewQueryTab.tsx @@ -1,15 +1,17 @@ import React, { FC, useMemo, useState } from 'react'; -import { getFrameDisplayName, GrafanaTheme, PanelData, SelectableValue, toDataFrame } from '@grafana/data'; -import { Select, stylesFactory, Table, useTheme } from '@grafana/ui'; import { css } from 'emotion'; +import { getFrameDisplayName, GrafanaTheme, PanelData, SelectableValue } from '@grafana/data'; +import { Button, Select, stylesFactory, Table, useTheme } from '@grafana/ui'; +import { EmptyState } from './EmptyState'; interface Props { - data?: PanelData; + data: PanelData; width: number; height: number; + onRunQueries: () => void; } -export const PreviewQueryTab: FC = ({ data, height, width }) => { +export const PreviewQueryTab: FC = ({ data, height, onRunQueries, width }) => { const [currentSeries, setSeries] = useState(0); const theme = useTheme(); const styles = getStyles(theme, height); @@ -24,8 +26,16 @@ export const PreviewQueryTab: FC = ({ data, height, width }) => { // Select padding const padding = 16; - if (!data?.series?.length) { - return
; + if (!data) { + return ( + + + + ); + } + + if (!data.series) { + return null; } if (data.series.length > 1) { @@ -48,12 +58,14 @@ export const PreviewQueryTab: FC = ({ data, height, width }) => { ); } + return
; }; const getStyles = stylesFactory((theme: GrafanaTheme, height: number) => { return { wrapper: css` + label: preview-wrapper; height: ${height}px; `, selectWrapper: css`