diff --git a/public/app/features/alerting/unified/AmRoutes.test.tsx b/public/app/features/alerting/unified/AmRoutes.test.tsx index d5c9594ccdb..a55faa6076b 100644 --- a/public/app/features/alerting/unified/AmRoutes.test.tsx +++ b/public/app/features/alerting/unified/AmRoutes.test.tsx @@ -79,6 +79,7 @@ const ui = { editRouteButton: byLabelText('Edit route'), deleteRouteButton: byLabelText('Delete route'), newPolicyButton: byRole('button', { name: /New policy/ }), + newPolicyCTAButton: byRole('button', { name: /New specific policy/ }), receiverSelect: byTestId('am-receiver-select'), groupSelect: byTestId('am-group-select'), @@ -508,6 +509,25 @@ describe('AmRoutes', () => { expect(mocks.api.fetchAlertManagerConfig).not.toHaveBeenCalled(); expect(mocks.api.fetchStatus).toHaveBeenCalledTimes(1); }); + + it('Prometheus Alertmanager has no CTA button if there are no specific policies', async () => { + mocks.api.fetchStatus.mockResolvedValue({ + ...someCloudAlertManagerStatus, + config: { + ...someCloudAlertManagerConfig.alertmanager_config, + route: { + ...someCloudAlertManagerConfig.alertmanager_config.route, + routes: undefined, + }, + }, + }); + await renderAmRoutes(dataSources.promAlertManager.name); + const rootRouteContainer = await ui.rootRouteContainer.find(); + expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument(); + expect(ui.newPolicyCTAButton.query()).not.toBeInTheDocument(); + expect(mocks.api.fetchAlertManagerConfig).not.toHaveBeenCalled(); + expect(mocks.api.fetchStatus).toHaveBeenCalledTimes(1); + }); }); const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise => { diff --git a/public/app/features/alerting/unified/components/EmptyArea.tsx b/public/app/features/alerting/unified/components/EmptyArea.tsx index ac8bf038458..6482547d906 100644 --- a/public/app/features/alerting/unified/components/EmptyArea.tsx +++ b/public/app/features/alerting/unified/components/EmptyArea.tsx @@ -1,43 +1,12 @@ -import React, { ButtonHTMLAttributes, FC } from 'react'; +import React, { FC } from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme } from '@grafana/data'; -import { Button, ButtonVariant, IconName, useStyles } from '@grafana/ui'; +import { useStyles } from '@grafana/ui'; -export interface EmptyAreaProps { - buttonLabel: string; - onButtonClick: ButtonHTMLAttributes['onClick']; - text: string; - - buttonIcon?: IconName; - buttonSize?: 'xs' | 'sm' | 'md' | 'lg'; - buttonVariant?: ButtonVariant; -} - -export const EmptyArea: FC = ({ - buttonIcon, - buttonLabel, - buttonSize = 'lg', - buttonVariant = 'primary', - onButtonClick, - text, -}) => { +export const EmptyArea: FC = ({ children }) => { const styles = useStyles(getStyles); - return ( -
-

{text}

- -
- ); + return
{children}
; }; const getStyles = (theme: GrafanaTheme) => { @@ -48,11 +17,5 @@ const getStyles = (theme: GrafanaTheme) => { padding: ${theme.spacing.xl}; text-align: center; `, - text: css` - margin-bottom: ${theme.spacing.md}; - `, - button: css` - margin: ${theme.spacing.md} 0 ${theme.spacing.sm}; - `, }; }; diff --git a/public/app/features/alerting/unified/components/EmptyAreaWithCTA.tsx b/public/app/features/alerting/unified/components/EmptyAreaWithCTA.tsx new file mode 100644 index 00000000000..85d289bf7ae --- /dev/null +++ b/public/app/features/alerting/unified/components/EmptyAreaWithCTA.tsx @@ -0,0 +1,61 @@ +import React, { ButtonHTMLAttributes, FC } from 'react'; +import { css } from '@emotion/css'; +import { GrafanaTheme } from '@grafana/data'; +import { Button, ButtonVariant, IconName, useStyles } from '@grafana/ui'; +import { EmptyArea } from './EmptyArea'; + +export interface EmptyAreaWithCTAProps { + buttonLabel: string; + onButtonClick: ButtonHTMLAttributes['onClick']; + text: string; + + buttonIcon?: IconName; + buttonSize?: 'xs' | 'sm' | 'md' | 'lg'; + buttonVariant?: ButtonVariant; +} + +export const EmptyAreaWithCTA: FC = ({ + buttonIcon, + buttonLabel, + buttonSize = 'lg', + buttonVariant = 'primary', + onButtonClick, + text, +}) => { + const styles = useStyles(getStyles); + + return ( + + <> +

{text}

+ + +
+ ); +}; + +const getStyles = (theme: GrafanaTheme) => { + return { + container: css` + background-color: ${theme.colors.bg2}; + color: ${theme.colors.textSemiWeak}; + padding: ${theme.spacing.xl}; + text-align: center; + `, + text: css` + margin-bottom: ${theme.spacing.md}; + `, + button: css` + margin: ${theme.spacing.md} 0 ${theme.spacing.sm}; + `, + }; +}; diff --git a/public/app/features/alerting/unified/components/amroutes/AmSpecificRouting.tsx b/public/app/features/alerting/unified/components/amroutes/AmSpecificRouting.tsx index d27b8d9f3ce..d4d36a8a197 100644 --- a/public/app/features/alerting/unified/components/amroutes/AmSpecificRouting.tsx +++ b/public/app/features/alerting/unified/components/amroutes/AmSpecificRouting.tsx @@ -6,6 +6,7 @@ import { AmRouteReceiver, FormAmRoute } from '../../types/amroutes'; import { emptyArrayFieldMatcher, emptyRoute } from '../../utils/amroutes'; import { EmptyArea } from '../EmptyArea'; import { AmRoutesTable } from './AmRoutesTable'; +import { EmptyAreaWithCTA } from '../EmptyAreaWithCTA'; export interface AmSpecificRoutingProps { onChange: (routes: FormAmRoute) => void; @@ -43,12 +44,18 @@ export const AmSpecificRouting: FC = ({
Specific routing

Send specific alerts to chosen contact points, based on matching criteria

{!routes.receiver ? ( - + readOnly ? ( + +

There is no default contact point configured for the root route.

+
+ ) : ( + + ) ) : actualRoutes.length > 0 ? ( <> {!isAddMode && !readOnly && ( @@ -82,8 +89,12 @@ export const AmSpecificRouting: FC = ({ routes={actualRoutes} /> + ) : readOnly ? ( + +

There are no specific policies configured.

+
) : ( -