mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Navigation: Use navid and pagnav in alert rules pages (#55722)
* add navid and pagenav to edit/add/view alert rules * move ruleeditor smaller component to its own file * fix form alignments with new layout * fixed broken test * reuse AlertingPageWrapper
This commit is contained in:
@@ -191,7 +191,7 @@ export class DataSourcePicker extends PureComponent<DataSourcePickerProps, DataS
|
|||||||
getOptionLabel={(o) => {
|
getOptionLabel={(o) => {
|
||||||
if (o.meta && isUnsignedPluginSignature(o.meta.signature) && o !== value) {
|
if (o.meta && isUnsignedPluginSignature(o.meta.signature) && o !== value) {
|
||||||
return (
|
return (
|
||||||
<HorizontalGroup align="center" justify="space-between">
|
<HorizontalGroup align="center" justify="space-between" height="auto">
|
||||||
<span>{o.label}</span> <PluginSignatureBadge status={o.meta.signature} />
|
<span>{o.label}</span> <PluginSignatureBadge status={o.meta.signature} />
|
||||||
</HorizontalGroup>
|
</HorizontalGroup>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,14 +20,6 @@ jest.mock('../../core/app_events', () => ({
|
|||||||
|
|
||||||
const defaultProps: Props = {
|
const defaultProps: Props = {
|
||||||
...getRouteComponentProps({}),
|
...getRouteComponentProps({}),
|
||||||
navModel: {
|
|
||||||
main: {
|
|
||||||
text: 'foo',
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
text: 'foo',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
search: '',
|
search: '',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
alertRules: [],
|
alertRules: [],
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { Button, FilterInput, LinkButton, Select, VerticalGroup } from '@grafana
|
|||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||||
import { getNavModel } from 'app/core/selectors/navModel';
|
|
||||||
import { AlertRule, StoreState } from 'app/types';
|
import { AlertRule, StoreState } from 'app/types';
|
||||||
|
|
||||||
import { ShowModalReactEvent } from '../../types/events';
|
import { ShowModalReactEvent } from '../../types/events';
|
||||||
@@ -21,7 +20,6 @@ import { getAlertRuleItems, getSearchQuery } from './state/selectors';
|
|||||||
|
|
||||||
function mapStateToProps(state: StoreState) {
|
function mapStateToProps(state: StoreState) {
|
||||||
return {
|
return {
|
||||||
navModel: getNavModel(state.navIndex, 'alert-list'),
|
|
||||||
alertRules: getAlertRuleItems(state),
|
alertRules: getAlertRuleItems(state),
|
||||||
search: getSearchQuery(state.alertRules),
|
search: getSearchQuery(state.alertRules),
|
||||||
isLoading: state.alertRules.isLoading,
|
isLoading: state.alertRules.isLoading,
|
||||||
@@ -94,10 +92,10 @@ export class AlertRuleListUnconnected extends PureComponent<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { navModel, alertRules, search, isLoading } = this.props;
|
const { alertRules, search, isLoading } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page navModel={navModel}>
|
<Page navId="alert-list">
|
||||||
<Page.Contents isLoading={isLoading}>
|
<Page.Contents isLoading={isLoading}>
|
||||||
<div className="page-action-bar">
|
<div className="page-action-bar">
|
||||||
<div className="gf-form gf-form--grow">
|
<div className="gf-form gf-form--grow">
|
||||||
|
|||||||
24
public/app/features/alerting/unified/AlertWarning.tsx
Normal file
24
public/app/features/alerting/unified/AlertWarning.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { Alert, LinkButton, useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
|
interface AlertWarningProps {
|
||||||
|
title: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
export function AlertWarning({ title, children }: AlertWarningProps) {
|
||||||
|
return (
|
||||||
|
<Alert className={useStyles2(warningStyles).warning} severity="warning" title={title}>
|
||||||
|
<p>{children}</p>
|
||||||
|
<LinkButton href="alerting/list">To rule list</LinkButton>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const warningStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
warning: css`
|
||||||
|
margin: ${theme.spacing(4)};
|
||||||
|
`,
|
||||||
|
});
|
||||||
53
public/app/features/alerting/unified/ExistingRuleEditor.tsx
Normal file
53
public/app/features/alerting/unified/ExistingRuleEditor.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { Alert, LoadingPlaceholder } from '@grafana/ui';
|
||||||
|
import { useCleanup } from 'app/core/hooks/useCleanup';
|
||||||
|
import { useDispatch } from 'app/types';
|
||||||
|
import { RuleIdentifier } from 'app/types/unified-alerting';
|
||||||
|
|
||||||
|
import { AlertWarning } from './AlertWarning';
|
||||||
|
import { AlertRuleForm } from './components/rule-editor/AlertRuleForm';
|
||||||
|
import { useIsRuleEditable } from './hooks/useIsRuleEditable';
|
||||||
|
import { useUnifiedAlertingSelector } from './hooks/useUnifiedAlertingSelector';
|
||||||
|
import { fetchEditableRuleAction } from './state/actions';
|
||||||
|
import { initialAsyncRequestState } from './utils/redux';
|
||||||
|
import * as ruleId from './utils/rule-id';
|
||||||
|
|
||||||
|
interface ExistingRuleEditorProps {
|
||||||
|
identifier: RuleIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExistingRuleEditor({ identifier }: ExistingRuleEditorProps) {
|
||||||
|
useCleanup((state) => (state.unifiedAlerting.ruleForm.existingRule = initialAsyncRequestState));
|
||||||
|
const { loading, result, error, dispatched } = useUnifiedAlertingSelector((state) => state.ruleForm.existingRule);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { isEditable } = useIsRuleEditable(ruleId.ruleIdentifierToRuleSourceName(identifier), result?.rule);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dispatched) {
|
||||||
|
dispatch(fetchEditableRuleAction(identifier));
|
||||||
|
}
|
||||||
|
}, [dispatched, dispatch, identifier]);
|
||||||
|
|
||||||
|
if (loading || isEditable === undefined) {
|
||||||
|
return <LoadingPlaceholder text="Loading rule..." />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<Alert severity="error" title="Failed to load rule">
|
||||||
|
{error.message}
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return <AlertWarning title="Rule not found">Sorry! This rule does not exist.</AlertWarning>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditable === false) {
|
||||||
|
return <AlertWarning title="Cannot edit rule">Sorry! You do not have permission to edit this rule.</AlertWarning>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <AlertRuleForm existing={result} />;
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import { getRulesSourceByName } from './utils/datasource';
|
|||||||
import { createViewLink } from './utils/misc';
|
import { createViewLink } from './utils/misc';
|
||||||
|
|
||||||
type RedirectToRuleViewerProps = GrafanaRouteComponentProps<{ name?: string; sourceName?: string }>;
|
type RedirectToRuleViewerProps = GrafanaRouteComponentProps<{ name?: string; sourceName?: string }>;
|
||||||
const pageTitle = 'Alerting / Find rule';
|
const pageTitle = 'Find rule';
|
||||||
|
|
||||||
export function RedirectToRuleViewer(props: RedirectToRuleViewerProps): JSX.Element | null {
|
export function RedirectToRuleViewer(props: RedirectToRuleViewerProps): JSX.Element | null {
|
||||||
const { name, sourceName } = props.match.params;
|
const { name, sourceName } = props.match.params;
|
||||||
|
|||||||
@@ -1,115 +1,72 @@
|
|||||||
import { css } from '@emotion/css';
|
import React, { FC } from 'react';
|
||||||
import React, { FC, useEffect } from 'react';
|
|
||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { NavModelItem } from '@grafana/data';
|
||||||
import { Alert, LinkButton, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui';
|
import { withErrorBoundary } from '@grafana/ui';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
|
||||||
import { useCleanup } from 'app/core/hooks/useCleanup';
|
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||||
import { useDispatch } from 'app/types';
|
import { useDispatch } from 'app/types';
|
||||||
import { RuleIdentifier } from 'app/types/unified-alerting';
|
|
||||||
|
|
||||||
|
import { AlertWarning } from './AlertWarning';
|
||||||
|
import { ExistingRuleEditor } from './ExistingRuleEditor';
|
||||||
|
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
||||||
import { AlertRuleForm } from './components/rule-editor/AlertRuleForm';
|
import { AlertRuleForm } from './components/rule-editor/AlertRuleForm';
|
||||||
import { useIsRuleEditable } from './hooks/useIsRuleEditable';
|
import { fetchAllPromBuildInfoAction } from './state/actions';
|
||||||
import { useUnifiedAlertingSelector } from './hooks/useUnifiedAlertingSelector';
|
|
||||||
import { fetchAllPromBuildInfoAction, fetchEditableRuleAction } from './state/actions';
|
|
||||||
import { useRulesAccess } from './utils/accessControlHooks';
|
import { useRulesAccess } from './utils/accessControlHooks';
|
||||||
import { initialAsyncRequestState } from './utils/redux';
|
|
||||||
import * as ruleId from './utils/rule-id';
|
import * as ruleId from './utils/rule-id';
|
||||||
|
|
||||||
interface ExistingRuleEditorProps {
|
type RuleEditorProps = GrafanaRouteComponentProps<{ id?: string }>;
|
||||||
identifier: RuleIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ExistingRuleEditor: FC<ExistingRuleEditorProps> = ({ identifier }) => {
|
const defaultPageNav: Partial<NavModelItem> = {
|
||||||
useCleanup((state) => (state.unifiedAlerting.ruleForm.existingRule = initialAsyncRequestState));
|
icon: 'bell',
|
||||||
const { loading, result, error, dispatched } = useUnifiedAlertingSelector((state) => state.ruleForm.existingRule);
|
id: 'alert-rule-view',
|
||||||
const dispatch = useDispatch();
|
breadcrumbs: [{ title: 'Alert rules', url: 'alerting/list' }],
|
||||||
const { isEditable } = useIsRuleEditable(ruleId.ruleIdentifierToRuleSourceName(identifier), result?.rule);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!dispatched) {
|
|
||||||
dispatch(fetchEditableRuleAction(identifier));
|
|
||||||
}
|
|
||||||
}, [dispatched, dispatch, identifier]);
|
|
||||||
|
|
||||||
if (loading || isEditable === undefined) {
|
|
||||||
return (
|
|
||||||
<Page.Contents>
|
|
||||||
<LoadingPlaceholder text="Loading rule..." />
|
|
||||||
</Page.Contents>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<Page.Contents>
|
|
||||||
<Alert severity="error" title="Failed to load rule">
|
|
||||||
{error.message}
|
|
||||||
</Alert>
|
|
||||||
</Page.Contents>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
return <AlertWarning title="Rule not found">Sorry! This rule does not exist.</AlertWarning>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEditable === false) {
|
|
||||||
return <AlertWarning title="Cannot edit rule">Sorry! You do not have permission to edit this rule.</AlertWarning>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <AlertRuleForm existing={result} />;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type RuleEditorProps = GrafanaRouteComponentProps<{ id?: string }>;
|
const getPageNav = (state: 'edit' | 'add') => {
|
||||||
|
if (state === 'edit') {
|
||||||
|
return { ...defaultPageNav, id: 'alert-rule-edit', text: 'Edit rule' };
|
||||||
|
} else if (state === 'add') {
|
||||||
|
return { ...defaultPageNav, id: 'alert-rule-add', text: 'Add rule' };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const RuleEditor: FC<RuleEditorProps> = ({ match }) => {
|
const RuleEditor: FC<RuleEditorProps> = ({ match }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { id } = match.params;
|
const { id } = match.params;
|
||||||
const identifier = ruleId.tryParse(id, true);
|
const identifier = ruleId.tryParse(id, true);
|
||||||
|
|
||||||
const { loading } = useAsync(async () => {
|
const { loading = true } = useAsync(async () => {
|
||||||
await dispatch(fetchAllPromBuildInfoAction());
|
await dispatch(fetchAllPromBuildInfoAction());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const { canCreateGrafanaRules, canCreateCloudRules, canEditRules } = useRulesAccess();
|
const { canCreateGrafanaRules, canCreateCloudRules, canEditRules } = useRulesAccess();
|
||||||
|
|
||||||
if (!identifier && !canCreateGrafanaRules && !canCreateCloudRules) {
|
const getContent = () => {
|
||||||
return <AlertWarning title="Cannot create rules">Sorry! You are not allowed to create rules.</AlertWarning>;
|
if (loading) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (identifier && !canEditRules(identifier.ruleSourceName)) {
|
if (!identifier && !canCreateGrafanaRules && !canCreateCloudRules) {
|
||||||
return <AlertWarning title="Cannot edit rules">Sorry! You are not allowed to edit rules.</AlertWarning>;
|
return <AlertWarning title="Cannot create rules">Sorry! You are not allowed to create rules.</AlertWarning>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (identifier && !canEditRules(identifier.ruleSourceName)) {
|
||||||
return (
|
return <AlertWarning title="Cannot edit rules">Sorry! You are not allowed to edit rules.</AlertWarning>;
|
||||||
<Page.Contents>
|
}
|
||||||
<LoadingPlaceholder text="Loading..." />
|
|
||||||
</Page.Contents>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (identifier) {
|
if (identifier) {
|
||||||
return <ExistingRuleEditor key={id} identifier={identifier} />;
|
return <ExistingRuleEditor key={id} identifier={identifier} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <AlertRuleForm />;
|
return <AlertRuleForm />;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AlertingPageWrapper isLoading={loading} pageId="alert-list" pageNav={getPageNav(identifier ? 'edit' : 'add')}>
|
||||||
|
{getContent()}
|
||||||
|
</AlertingPageWrapper>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const AlertWarning: FC<{ title: string }> = ({ title, children }) => (
|
|
||||||
<Alert className={useStyles2(warningStyles).warning} severity="warning" title={title}>
|
|
||||||
<p>{children}</p>
|
|
||||||
<LinkButton href="alerting/list">To rule list</LinkButton>
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
|
|
||||||
const warningStyles = (theme: GrafanaTheme2) => ({
|
|
||||||
warning: css`
|
|
||||||
margin: ${theme.spacing(4)};
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default withErrorBoundary(RuleEditor, { style: 'page' });
|
export default withErrorBoundary(RuleEditor, { style: 'page' });
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ describe('RuleViewer', () => {
|
|||||||
});
|
});
|
||||||
await renderRuleViewer();
|
await renderRuleViewer();
|
||||||
|
|
||||||
expect(screen.getByText('Alerting / View rule')).toBeInTheDocument();
|
expect(screen.getByText(/view rule/i)).toBeInTheDocument();
|
||||||
expect(screen.getByText('Test alert')).toBeInTheDocument();
|
expect(screen.getByText(/test alert/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render page with cloud alert', async () => {
|
it('should render page with cloud alert', async () => {
|
||||||
@@ -74,8 +74,8 @@ describe('RuleViewer', () => {
|
|||||||
error: undefined,
|
error: undefined,
|
||||||
});
|
});
|
||||||
await renderRuleViewer();
|
await renderRuleViewer();
|
||||||
expect(screen.getByText('Alerting / View rule')).toBeInTheDocument();
|
expect(screen.getByText(/view rule/i)).toBeInTheDocument();
|
||||||
expect(screen.getByText('Cloud test alert')).toBeInTheDocument();
|
expect(screen.getByText(/cloud test alert/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ type RuleViewerProps = GrafanaRouteComponentProps<{ id?: string; sourceName?: st
|
|||||||
|
|
||||||
const errorMessage = 'Could not find data source for rule';
|
const errorMessage = 'Could not find data source for rule';
|
||||||
const errorTitle = 'Could not view rule';
|
const errorTitle = 'Could not view rule';
|
||||||
const pageTitle = 'Alerting / View rule';
|
const pageTitle = 'View rule';
|
||||||
|
|
||||||
export function RuleViewer({ match }: RuleViewerProps) {
|
export function RuleViewer({ match }: RuleViewerProps) {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { logInfo } from '@grafana/runtime';
|
import { logInfo } from '@grafana/runtime';
|
||||||
import { Button, ConfirmModal, CustomScrollbar, PageToolbar, Spinner, useStyles2 } from '@grafana/ui';
|
import { Button, ConfirmModal, CustomScrollbar, Spinner, useStyles2, HorizontalGroup } from '@grafana/ui';
|
||||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||||
import { useCleanup } from 'app/core/hooks/useCleanup';
|
import { useCleanup } from 'app/core/hooks/useCleanup';
|
||||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||||
@@ -110,7 +110,7 @@ export const AlertRuleForm: FC<Props> = ({ existing }) => {
|
|||||||
return (
|
return (
|
||||||
<FormProvider {...formAPI}>
|
<FormProvider {...formAPI}>
|
||||||
<form onSubmit={(e) => e.preventDefault()} className={styles.form}>
|
<form onSubmit={(e) => e.preventDefault()} className={styles.form}>
|
||||||
<PageToolbar title={`${existing ? 'Edit' : 'Create'} alert rule`} pageIcon="bell">
|
<HorizontalGroup height="auto" justify="flex-end">
|
||||||
<Link to={returnTo}>
|
<Link to={returnTo}>
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@@ -155,7 +155,7 @@ export const AlertRuleForm: FC<Props> = ({ existing }) => {
|
|||||||
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
|
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
|
||||||
Save and exit
|
Save and exit
|
||||||
</Button>
|
</Button>
|
||||||
</PageToolbar>
|
</HorizontalGroup>
|
||||||
<div className={styles.contentOuter}>
|
<div className={styles.contentOuter}>
|
||||||
<CustomScrollbar autoHeightMin="100%" hideHorizontalTrack={true}>
|
<CustomScrollbar autoHeightMin="100%" hideHorizontalTrack={true}>
|
||||||
<div className={styles.contentInner}>
|
<div className={styles.contentInner}>
|
||||||
@@ -212,9 +212,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
background: ${theme.colors.background.primary};
|
background: ${theme.colors.background.primary};
|
||||||
border: 1px solid ${theme.colors.border.weak};
|
border: 1px solid ${theme.colors.border.weak};
|
||||||
border-radius: ${theme.shape.borderRadius()};
|
border-radius: ${theme.shape.borderRadius()};
|
||||||
margin: ${theme.spacing(0, 2, 2)};
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
margin-top: ${theme.spacing(1)};
|
||||||
`,
|
`,
|
||||||
flexRow: css`
|
flexRow: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { useStyles2 } from '@grafana/ui';
|
||||||
import { PageToolbar, useStyles2 } from '@grafana/ui';
|
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -12,14 +11,21 @@ type Props = {
|
|||||||
wrapInContent?: boolean;
|
wrapInContent?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultPageNav: Partial<NavModelItem> = {
|
||||||
|
icon: 'bell',
|
||||||
|
id: 'alert-rule-view',
|
||||||
|
breadcrumbs: [{ title: 'Alert rules', url: 'alerting/list' }],
|
||||||
|
};
|
||||||
|
|
||||||
export function RuleViewerLayout(props: Props): JSX.Element | null {
|
export function RuleViewerLayout(props: Props): JSX.Element | null {
|
||||||
const { wrapInContent = true, children, title } = props;
|
const { wrapInContent = true, children, title } = props;
|
||||||
const styles = useStyles2(getPageStyles);
|
const styles = useStyles2(getPageStyles);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page pageNav={{ ...defaultPageNav, text: title }} navId="alert-list">
|
||||||
<PageToolbar title={title} pageIcon="bell" onGoBack={() => locationService.push('/alerting/list')} />
|
<Page.Contents>
|
||||||
<div className={styles.content}>{wrapInContent ? <RuleViewerLayoutContent {...props} /> : children}</div>
|
<div className={styles.content}>{wrapInContent ? <RuleViewerLayoutContent {...props} /> : children}</div>
|
||||||
|
</Page.Contents>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -37,7 +43,6 @@ export function RuleViewerLayoutContent({ children, padding = 2 }: ContentProps)
|
|||||||
const getPageStyles = (theme: GrafanaTheme2) => {
|
const getPageStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
content: css`
|
content: css`
|
||||||
margin: ${theme.spacing(0, 2, 2)};
|
|
||||||
max-width: ${theme.breakpoints.values.xxl}px;
|
max-width: ${theme.breakpoints.values.xxl}px;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user