mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Remove CTA button from read-only notification policy page (#41096)
* don't show cta button for read-only prometheus alertmanager * grammar fix Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com> Co-authored-by: Peter Holmberg <peterholmberg@users.noreply.github.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com> Co-authored-by: Peter Holmberg <peter.hlmbrg@gmail.com>
This commit is contained in:
parent
01bc59e432
commit
345af8374c
@ -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<void> => {
|
||||
|
@ -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<HTMLButtonElement>['onClick'];
|
||||
text: string;
|
||||
|
||||
buttonIcon?: IconName;
|
||||
buttonSize?: 'xs' | 'sm' | 'md' | 'lg';
|
||||
buttonVariant?: ButtonVariant;
|
||||
}
|
||||
|
||||
export const EmptyArea: FC<EmptyAreaProps> = ({
|
||||
buttonIcon,
|
||||
buttonLabel,
|
||||
buttonSize = 'lg',
|
||||
buttonVariant = 'primary',
|
||||
onButtonClick,
|
||||
text,
|
||||
}) => {
|
||||
export const EmptyArea: FC = ({ children }) => {
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<p className={styles.text}>{text}</p>
|
||||
<Button
|
||||
className={styles.button}
|
||||
icon={buttonIcon}
|
||||
onClick={onButtonClick}
|
||||
size={buttonSize}
|
||||
type="button"
|
||||
variant={buttonVariant}
|
||||
>
|
||||
{buttonLabel}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
return <div className={styles.container}>{children}</div>;
|
||||
};
|
||||
|
||||
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};
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
@ -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<HTMLButtonElement>['onClick'];
|
||||
text: string;
|
||||
|
||||
buttonIcon?: IconName;
|
||||
buttonSize?: 'xs' | 'sm' | 'md' | 'lg';
|
||||
buttonVariant?: ButtonVariant;
|
||||
}
|
||||
|
||||
export const EmptyAreaWithCTA: FC<EmptyAreaWithCTAProps> = ({
|
||||
buttonIcon,
|
||||
buttonLabel,
|
||||
buttonSize = 'lg',
|
||||
buttonVariant = 'primary',
|
||||
onButtonClick,
|
||||
text,
|
||||
}) => {
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
return (
|
||||
<EmptyArea>
|
||||
<>
|
||||
<p className={styles.text}>{text}</p>
|
||||
<Button
|
||||
className={styles.button}
|
||||
icon={buttonIcon}
|
||||
onClick={onButtonClick}
|
||||
size={buttonSize}
|
||||
type="button"
|
||||
variant={buttonVariant}
|
||||
>
|
||||
{buttonLabel}
|
||||
</Button>
|
||||
</>
|
||||
</EmptyArea>
|
||||
);
|
||||
};
|
||||
|
||||
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};
|
||||
`,
|
||||
};
|
||||
};
|
@ -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<AmSpecificRoutingProps> = ({
|
||||
<h5>Specific routing</h5>
|
||||
<p>Send specific alerts to chosen contact points, based on matching criteria</p>
|
||||
{!routes.receiver ? (
|
||||
<EmptyArea
|
||||
readOnly ? (
|
||||
<EmptyArea>
|
||||
<p>There is no default contact point configured for the root route.</p>
|
||||
</EmptyArea>
|
||||
) : (
|
||||
<EmptyAreaWithCTA
|
||||
buttonIcon="rocket"
|
||||
buttonLabel="Set a default contact point"
|
||||
onButtonClick={onRootRouteEdit}
|
||||
text="You haven't set a default contact point for the root route yet."
|
||||
/>
|
||||
)
|
||||
) : actualRoutes.length > 0 ? (
|
||||
<>
|
||||
{!isAddMode && !readOnly && (
|
||||
@ -82,8 +89,12 @@ export const AmSpecificRouting: FC<AmSpecificRoutingProps> = ({
|
||||
routes={actualRoutes}
|
||||
/>
|
||||
</>
|
||||
) : readOnly ? (
|
||||
<EmptyArea>
|
||||
<p>There are no specific policies configured.</p>
|
||||
</EmptyArea>
|
||||
) : (
|
||||
<EmptyArea
|
||||
<EmptyAreaWithCTA
|
||||
buttonIcon="plus"
|
||||
buttonLabel="New specific policy"
|
||||
onButtonClick={addNewRoute}
|
||||
|
Loading…
Reference in New Issue
Block a user