mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Keep rule form buttons always on top (#71056)
* Keep form buttons always on top * Fix tests
This commit is contained in:
parent
09330890f3
commit
549e04a8f1
@ -36,6 +36,10 @@ jest.mock('app/features/query/components/QueryEditorRow', () => ({
|
|||||||
QueryEditorRow: () => <p>hi</p>,
|
QueryEditorRow: () => <p>hi</p>,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('app/core/components/AppChrome/AppChromeUpdate', () => ({
|
||||||
|
AppChromeUpdate: ({ actions }: { actions: React.ReactNode }) => <div>{actions}</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
jest.spyOn(config, 'getAllDataSources');
|
jest.spyOn(config, 'getAllDataSources');
|
||||||
|
|
||||||
// these tests are rather slow because we have to wait for various API calls and mocks to be called
|
// these tests are rather slow because we have to wait for various API calls and mocks to be called
|
||||||
|
@ -32,6 +32,10 @@ jest.mock('./components/rule-editor/ExpressionEditor', () => ({
|
|||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('app/core/components/AppChrome/AppChromeUpdate', () => ({
|
||||||
|
AppChromeUpdate: ({ actions }: { actions: React.ReactNode }) => <div>{actions}</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('./api/buildInfo');
|
jest.mock('./api/buildInfo');
|
||||||
jest.mock('./api/ruler');
|
jest.mock('./api/ruler');
|
||||||
jest.mock('../../../../app/features/manage-dashboards/state/actions');
|
jest.mock('../../../../app/features/manage-dashboards/state/actions');
|
||||||
|
@ -32,6 +32,10 @@ jest.mock('./api/buildInfo');
|
|||||||
jest.mock('./api/ruler');
|
jest.mock('./api/ruler');
|
||||||
jest.mock('../../../../app/features/manage-dashboards/state/actions');
|
jest.mock('../../../../app/features/manage-dashboards/state/actions');
|
||||||
|
|
||||||
|
jest.mock('app/core/components/AppChrome/AppChromeUpdate', () => ({
|
||||||
|
AppChromeUpdate: ({ actions }: { actions: React.ReactNode }) => <div>{actions}</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
// there's no angular scope in test and things go terribly wrong when trying to render the query editor row.
|
// there's no angular scope in test and things go terribly wrong when trying to render the query editor row.
|
||||||
// lets just skip it
|
// lets just skip it
|
||||||
jest.mock('app/features/query/components/QueryEditorRow', () => ({
|
jest.mock('app/features/query/components/QueryEditorRow', () => ({
|
||||||
|
@ -39,6 +39,10 @@ jest.mock('./components/rule-editor/RecordingRuleEditor', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('app/core/components/AppChrome/AppChromeUpdate', () => ({
|
||||||
|
AppChromeUpdate: ({ actions }: { actions: React.ReactNode }) => <div>{actions}</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('./api/buildInfo');
|
jest.mock('./api/buildInfo');
|
||||||
jest.mock('./api/ruler');
|
jest.mock('./api/ruler');
|
||||||
jest.mock('../../../../app/features/manage-dashboards/state/actions');
|
jest.mock('../../../../app/features/manage-dashboards/state/actions');
|
||||||
|
@ -7,6 +7,7 @@ import { Link, useParams } from 'react-router-dom';
|
|||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { config, logInfo } from '@grafana/runtime';
|
import { config, logInfo } from '@grafana/runtime';
|
||||||
import { Button, ConfirmModal, CustomScrollbar, Field, HorizontalGroup, Input, Spinner, useStyles2 } from '@grafana/ui';
|
import { Button, ConfirmModal, CustomScrollbar, Field, HorizontalGroup, Input, Spinner, useStyles2 } from '@grafana/ui';
|
||||||
|
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
|
||||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||||
import { contextSrv } from 'app/core/core';
|
import { contextSrv } from 'app/core/core';
|
||||||
import { useCleanup } from 'app/core/hooks/useCleanup';
|
import { useCleanup } from 'app/core/hooks/useCleanup';
|
||||||
@ -192,49 +193,56 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
|
|||||||
const evaluateEveryInForm = watch('evaluateEvery');
|
const evaluateEveryInForm = watch('evaluateEvery');
|
||||||
useEffect(() => setEvaluateEvery(evaluateEveryInForm), [evaluateEveryInForm]);
|
useEffect(() => setEvaluateEvery(evaluateEveryInForm), [evaluateEveryInForm]);
|
||||||
|
|
||||||
|
const actionButtons = (
|
||||||
|
<HorizontalGroup height="auto" justify="flex-end">
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
type="button"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleSubmit((values) => submit(values, false), onInvalid)}
|
||||||
|
disabled={submitState.loading}
|
||||||
|
>
|
||||||
|
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
|
||||||
|
Save rule
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
type="button"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleSubmit((values) => submit(values, true), onInvalid)}
|
||||||
|
disabled={submitState.loading}
|
||||||
|
>
|
||||||
|
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
|
||||||
|
Save rule and exit
|
||||||
|
</Button>
|
||||||
|
<Link to={returnTo}>
|
||||||
|
<Button variant="secondary" disabled={submitState.loading} type="button" onClick={cancelRuleCreation} size="sm">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
{existing ? (
|
||||||
|
<Button fill="outline" variant="destructive" type="button" onClick={() => setShowDeleteModal(true)} size="sm">
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{isCortexLokiOrRecordingRule(watch) && (
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowEditYaml(true)}
|
||||||
|
disabled={submitState.loading}
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Edit YAML
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</HorizontalGroup>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...formAPI}>
|
<FormProvider {...formAPI}>
|
||||||
|
<AppChromeUpdate actions={actionButtons} />
|
||||||
<form onSubmit={(e) => e.preventDefault()} className={styles.form}>
|
<form onSubmit={(e) => e.preventDefault()} className={styles.form}>
|
||||||
<HorizontalGroup height="auto" justify="flex-end">
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
type="button"
|
|
||||||
onClick={handleSubmit((values) => submit(values, false), onInvalid)}
|
|
||||||
disabled={submitState.loading}
|
|
||||||
>
|
|
||||||
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
|
|
||||||
Save rule
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
type="button"
|
|
||||||
onClick={handleSubmit((values) => submit(values, true), onInvalid)}
|
|
||||||
disabled={submitState.loading}
|
|
||||||
>
|
|
||||||
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
|
|
||||||
Save rule and exit
|
|
||||||
</Button>
|
|
||||||
<Link to={returnTo}>
|
|
||||||
<Button variant="secondary" disabled={submitState.loading} type="button" onClick={cancelRuleCreation}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
{existing ? (
|
|
||||||
<Button variant="destructive" type="button" onClick={() => setShowDeleteModal(true)}>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{isCortexLokiOrRecordingRule(watch) && (
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowEditYaml(true)}
|
|
||||||
disabled={submitState.loading}
|
|
||||||
>
|
|
||||||
Edit YAML
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</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}>
|
||||||
|
Loading…
Reference in New Issue
Block a user