diff --git a/.betterer.results b/.betterer.results
index bd1b51f525d..9184482f4f4 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -1807,13 +1807,7 @@ exports[`better eslint`] = {
"public/app/features/alerting/unified/components/receivers/PayloadEditor.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
- [0, 0, 0, "Styles should be written using objects.", "2"],
- [0, 0, 0, "Styles should be written using objects.", "3"],
- [0, 0, 0, "Styles should be written using objects.", "4"],
- [0, 0, 0, "Styles should be written using objects.", "5"],
- [0, 0, 0, "Styles should be written using objects.", "6"],
- [0, 0, 0, "Styles should be written using objects.", "7"],
- [0, 0, 0, "Styles should be written using objects.", "8"]
+ [0, 0, 0, "Styles should be written using objects.", "2"]
],
"public/app/features/alerting/unified/components/receivers/ReceiversSection.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
@@ -1826,18 +1820,7 @@ exports[`better eslint`] = {
],
"public/app/features/alerting/unified/components/receivers/TemplateForm.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
- [0, 0, 0, "Styles should be written using objects.", "1"],
- [0, 0, 0, "Styles should be written using objects.", "2"],
- [0, 0, 0, "Styles should be written using objects.", "3"],
- [0, 0, 0, "Styles should be written using objects.", "4"],
- [0, 0, 0, "Styles should be written using objects.", "5"],
- [0, 0, 0, "Styles should be written using objects.", "6"],
- [0, 0, 0, "Styles should be written using objects.", "7"],
- [0, 0, 0, "Styles should be written using objects.", "8"],
- [0, 0, 0, "Styles should be written using objects.", "9"],
- [0, 0, 0, "Styles should be written using objects.", "10"],
- [0, 0, 0, "Styles should be written using objects.", "11"],
- [0, 0, 0, "Styles should be written using objects.", "12"]
+ [0, 0, 0, "Styles should be written using objects.", "1"]
],
"public/app/features/alerting/unified/components/receivers/form/ChannelOptions.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
diff --git a/packages/grafana-data/src/types/icon.ts b/packages/grafana-data/src/types/icon.ts
index 4b6761b33b4..ab02b8790f1 100644
--- a/packages/grafana-data/src/types/icon.ts
+++ b/packages/grafana-data/src/types/icon.ts
@@ -14,6 +14,7 @@ export const availableIconsIndex = {
'adjust-circle': true,
'angle-double-down': true,
'angle-double-right': true,
+ 'angle-double-left': true,
'angle-double-up': true,
'angle-down': true,
'angle-left': true,
diff --git a/public/app/features/alerting/routes.tsx b/public/app/features/alerting/routes.tsx
index ddc9d112773..6272ab22e76 100644
--- a/public/app/features/alerting/routes.tsx
+++ b/public/app/features/alerting/routes.tsx
@@ -97,6 +97,16 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
() => import(/* webpackChunkName: "NotificationsListPage" */ 'app/features/alerting/unified/Receivers')
),
},
+ {
+ path: '/alerting/notifications/templates/*',
+ roles: evaluateAccess([
+ AccessControlAction.AlertingNotificationsRead,
+ AccessControlAction.AlertingNotificationsExternalRead,
+ ]),
+ component: importAlertingComponent(
+ () => import(/* webpackChunkName: "Templates" */ 'app/features/alerting/unified/Templates')
+ ),
+ },
{
path: '/alerting/notifications/:type/new',
roles: evaluateAccess([
diff --git a/public/app/features/alerting/unified/Receivers.tsx b/public/app/features/alerting/unified/Receivers.tsx
index 49d69693365..fc37703fba1 100644
--- a/public/app/features/alerting/unified/Receivers.tsx
+++ b/public/app/features/alerting/unified/Receivers.tsx
@@ -2,33 +2,22 @@ import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { withErrorBoundary } from '@grafana/ui';
-const ContactPointsV2 = SafeDynamicImport(() => import('./components/contact-points/ContactPoints'));
-const EditContactPoint = SafeDynamicImport(() => import('./components/contact-points/EditContactPoint'));
-const NewContactPoint = SafeDynamicImport(() => import('./components/contact-points/NewContactPoint'));
-const EditMessageTemplate = SafeDynamicImport(() => import('./components/contact-points/EditMessageTemplate'));
-const NewMessageTemplate = SafeDynamicImport(() => import('./components/contact-points/NewMessageTemplate'));
-const GlobalConfig = SafeDynamicImport(() => import('./components/contact-points/components/GlobalConfig'));
-const DuplicateMessageTemplate = SafeDynamicImport(
- () => import('./components/contact-points/DuplicateMessageTemplate')
-);
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
+const ContactPointsV2 = SafeDynamicImport(() => import('./components/contact-points/ContactPoints'));
+const EditContactPoint = SafeDynamicImport(() => import('./components/contact-points/EditContactPoint'));
+const NewContactPoint = SafeDynamicImport(() => import('./components/contact-points/NewContactPoint'));
+const GlobalConfig = SafeDynamicImport(() => import('./components/contact-points/components/GlobalConfig'));
+
const ContactPoints = (_props: GrafanaRouteComponentProps): JSX.Element => (
-
-
-
diff --git a/public/app/features/alerting/unified/Templates.tsx b/public/app/features/alerting/unified/Templates.tsx
new file mode 100644
index 00000000000..e728d588812
--- /dev/null
+++ b/public/app/features/alerting/unified/Templates.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { Route, Switch } from 'react-router-dom';
+
+import { withErrorBoundary } from '@grafana/ui';
+import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
+import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
+
+import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
+
+const EditMessageTemplate = SafeDynamicImport(() => import('./components/contact-points/EditMessageTemplate'));
+const NewMessageTemplate = SafeDynamicImport(() => import('./components/contact-points/NewMessageTemplate'));
+const DuplicateMessageTemplate = SafeDynamicImport(
+ () => import('./components/contact-points/DuplicateMessageTemplate')
+);
+
+const NotificationTemplates = (_props: GrafanaRouteComponentProps): JSX.Element => (
+
+
+
+
+
+
+
+);
+
+export default withErrorBoundary(NotificationTemplates, { style: 'page' });
diff --git a/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx b/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx
index 44fe60d61e0..62f9069b8db 100644
--- a/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx
+++ b/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx
@@ -18,9 +18,7 @@ interface AlertingPageWrapperProps extends PageProps {
export const AlertingPageWrapper = ({ children, isLoading, ...rest }: AlertingPageWrapperProps) => (
-
- {children}
-
+ {children}
);
diff --git a/public/app/features/alerting/unified/components/contact-points/templates/EditorColumnHeader.tsx b/public/app/features/alerting/unified/components/contact-points/templates/EditorColumnHeader.tsx
new file mode 100644
index 00000000000..8b6e37f0047
--- /dev/null
+++ b/public/app/features/alerting/unified/components/contact-points/templates/EditorColumnHeader.tsx
@@ -0,0 +1,33 @@
+import { css } from '@emotion/css';
+import React from 'react';
+
+import { GrafanaTheme2 } from '@grafana/data';
+import { useStyles2, Stack, Label } from '@grafana/ui';
+
+export function EditorColumnHeader({ label, actions }: { label: string; actions?: React.ReactNode }) {
+ const styles = useStyles2(editorColumnStyles);
+
+ return (
+
+
+
+ {actions}
+
+
+ );
+}
+
+export const editorColumnStyles = (theme: GrafanaTheme2) => ({
+ container: css({
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: theme.spacing(1, 2),
+ backgroundColor: theme.colors.background.secondary,
+ borderBottom: `1px solid ${theme.colors.border.medium}`,
+ }),
+ label: css({
+ margin: 0,
+ }),
+});
diff --git a/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx b/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx
index 3691f4b2a97..1c5876b7bdb 100644
--- a/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx
+++ b/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx
@@ -168,10 +168,13 @@ export function AlertInstanceModalSelector({
const instances: TestTemplateAlert[] =
selectedInstances?.map((instance: AlertmanagerAlert) => {
const alert: TestTemplateAlert = {
+ status: 'firing',
annotations: instance.annotations,
labels: instance.labels,
startsAt: instance.startsAt,
endsAt: instance.endsAt,
+ generatorURL: instance.generatorURL,
+ fingerprint: instance.fingerprint,
};
return alert;
}) || [];
diff --git a/public/app/features/alerting/unified/components/receivers/PayloadEditor.test.tsx b/public/app/features/alerting/unified/components/receivers/PayloadEditor.test.tsx
index cd8137f2391..aaced7dfded 100644
--- a/public/app/features/alerting/unified/components/receivers/PayloadEditor.test.tsx
+++ b/public/app/features/alerting/unified/components/receivers/PayloadEditor.test.tsx
@@ -1,15 +1,13 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { default as React, useState } from 'react';
-import { Provider } from 'react-redux';
import { Props } from 'react-virtualized-auto-sizer';
-
-import { configureStore } from 'app/store/configureStore';
-
-import 'whatwg-fetch';
+import { TestProvider } from 'test/helpers/TestProvider';
import { PayloadEditor, RESET_TO_DEFAULT } from './PayloadEditor';
+import 'whatwg-fetch';
+
const DEFAULT_PAYLOAD = `[
{
"annotations": {
@@ -48,17 +46,11 @@ const PayloadEditorWithState = () => {
defaultPayload={DEFAULT_PAYLOAD}
setPayloadFormatError={jest.fn()}
payloadFormatError={null}
- onPayloadError={jest.fn()}
/>
);
};
const renderWithProvider = () => {
- const store = configureStore();
- render(
-
-
-
- );
+ render(, { wrapper: TestProvider });
};
describe('Payload editor', () => {
@@ -82,7 +74,10 @@ describe('Payload editor', () => {
expect(screen.getByTestId('mockeditor')).toHaveValue(
'[ { "annotations": { "summary": "Instance instance1 has been down for more than 5 minutes" }, "labels": { "instance": "instance1" }, "startsAt": "2023-04-25T15:28:56.440Z" }]this is the something'
);
- await userEvent.click(screen.getByText(RESET_TO_DEFAULT));
+
+ // click edit payload > reset to defaults
+ await userEvent.click(screen.getByRole('button', { name: 'Edit payload' }));
+ await userEvent.click(screen.getByRole('menuitem', { name: RESET_TO_DEFAULT }));
await waitFor(() =>
expect(screen.queryByTestId('mockeditor')).toHaveValue(
'[ { "annotations": { "summary": "Instance instance1 has been down for more than 5 minutes" }, "labels": { "instance": "instance1" }, "startsAt": "2023-04-25T15:28:56.440Z" }]'
diff --git a/public/app/features/alerting/unified/components/receivers/PayloadEditor.tsx b/public/app/features/alerting/unified/components/receivers/PayloadEditor.tsx
index a306daf7b5f..95895b7ce58 100644
--- a/public/app/features/alerting/unified/components/receivers/PayloadEditor.tsx
+++ b/public/app/features/alerting/unified/components/receivers/PayloadEditor.tsx
@@ -1,17 +1,19 @@
-import { css } from '@emotion/css';
+import { css, cx } from '@emotion/css';
import React, { useState } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import { GrafanaTheme2 } from '@grafana/data';
-import { Badge, Button, CodeEditor, Icon, Tooltip, useStyles2 } from '@grafana/ui';
+import { Button, CodeEditor, Dropdown, Menu, Stack, Toggletip, useStyles2 } from '@grafana/ui';
import { TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types';
+import { EditorColumnHeader } from '../contact-points/templates/EditorColumnHeader';
+
import { AlertInstanceModalSelector } from './AlertInstanceModalSelector';
import { AlertTemplatePreviewData } from './TemplateData';
import { TemplateDataTable } from './TemplateDataDocs';
import { GenerateAlertDataModal } from './form/GenerateAlertDataModal';
-export const RESET_TO_DEFAULT = 'Reset to default';
+export const RESET_TO_DEFAULT = 'Reset to defaults';
export function PayloadEditor({
payload,
@@ -19,14 +21,14 @@ export function PayloadEditor({
defaultPayload,
setPayloadFormatError,
payloadFormatError,
- onPayloadError,
+ className,
}: {
payload: string;
defaultPayload: string;
setPayload: React.Dispatch>;
setPayloadFormatError: (value: React.SetStateAction) => void;
payloadFormatError: string | null;
- onPayloadError: () => void;
+ className?: string;
}) {
const styles = useStyles2(getStyles);
const onReset = () => {
@@ -48,7 +50,6 @@ export function PayloadEditor({
setPayloadFormatError(null);
} catch (e) {
setPayloadFormatError(e instanceof Error ? e.message : 'Invalid JSON.');
- onPayloadError();
throw e;
}
};
@@ -79,67 +80,65 @@ export function PayloadEditor({
const [isAlertSelectorOpen, setIsAlertSelectorOpen] = useState(false);
return (
-
-
-
- Payload data
- } theme="info">
-
-
-
-
- {({ width }) => (
-
+ <>
+
+
+
+
+
+
+
+
+ }
+ >
+
+
+ } placement="top" fitContent>
+
+
+
+ }
+ />
+
+
+
+ {({ width, height }) => (
-
- )}
-
-
-
-
-
-
-
-
- {payloadFormatError !== null && (
-
- )}
+ )}
+
+
setIsAlertSelectorOpen(false)}
/>
-
+ >
);
}
const AlertTemplateDataTable = () => {
- const styles = useStyles2(getStyles);
- return (
-
- Alert template data This is the list of alert data fields used in the preview.
-
- }
- dataItems={AlertTemplatePreviewData}
- />
- );
+ return ;
};
const getStyles = (theme: GrafanaTheme2) => ({
- jsonEditor: css`
- width: 100%;
- height: 100%;
- `,
- buttonsWrapper: css`
- margin-top: ${theme.spacing(1)};
- display: flex;
- flex-wrap: wrap;
- `,
- button: css`
- flex: none;
- width: fit-content;
- padding-right: ${theme.spacing(1)};
- margin-right: ${theme.spacing(1)};
- margin-bottom: ${theme.spacing(1)};
- `,
- title: css`
- font-weight: ${theme.typography.fontWeightBold};
- heigth: 41px;
- padding-top: 10px;
- padding-left: ${theme.spacing(2)};
- margin-top: 19px;
- `,
wrapper: css`
- flex: 1;
- min-width: 450px;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
`,
tooltip: css`
padding-left: ${theme.spacing(1)};
`,
- editorWrapper: css`
- width: min-content;
- padding-top: 7px;
- `,
- editor: css`
- display: flex;
- flex-direction: column;
- margin-top: ${theme.spacing(-1)};
- `,
+ label: css({
+ margin: 0,
+ }),
+ editorWrapper: css({
+ flex: 1,
+ }),
+ editorContainer: css({
+ width: 'fit-content',
+ border: 'none',
+ }),
templateDataDocsHeader: css`
color: ${theme.colors.text.primary};
diff --git a/public/app/features/alerting/unified/components/receivers/TemplateData.ts b/public/app/features/alerting/unified/components/receivers/TemplateData.ts
index a46eaaaa8e4..a928280f759 100644
--- a/public/app/features/alerting/unified/components/receivers/TemplateData.ts
+++ b/public/app/features/alerting/unified/components/receivers/TemplateData.ts
@@ -1,6 +1,6 @@
export interface TemplateDataItem {
name: string;
- type: 'string' | '[]Alert' | 'KeyValue' | 'time.Time';
+ type: string;
notes: string;
}
@@ -61,23 +61,23 @@ export const GlobalTemplateData: TemplateDataItem[] = [
export const AlertTemplatePreviewData: TemplateDataItem[] = [
{
- name: 'Labels',
- type: 'KeyValue',
+ name: 'labels',
+ type: 'Object{}',
notes: 'Set of labels attached to the alert.',
},
{
- name: 'Annotations',
- type: 'KeyValue',
+ name: 'annotations',
+ type: 'Object{}',
notes: 'Set of annotations attached to the alert.',
},
{
- name: 'StartsAt',
- type: 'time.Time',
+ name: 'startsAt',
+ type: 'string (ISO8601)',
notes: 'Time the alert started firing.',
},
{
- name: 'EndsAt',
- type: 'time.Time',
+ name: 'endsAt',
+ type: 'string (ISO8601)',
notes: 'Time the alert ends firing.',
},
];
diff --git a/public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx b/public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx
index bbf5b49dbc2..905c397e921 100644
--- a/public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx
+++ b/public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx
@@ -67,7 +67,7 @@ const getTemplateDataDocsStyles = (theme: GrafanaTheme2) => ({
interface TemplateDataTableProps {
dataItems: TemplateDataItem[];
- caption: JSX.Element | string;
+ caption?: JSX.Element | string;
typeRenderer?: (type: TemplateDataItem['type']) => React.ReactNode;
}
@@ -76,7 +76,7 @@ export function TemplateDataTable({ dataItems, caption, typeRenderer }: Template
return (
- {caption}
+ {caption && {caption}}
| Name |
diff --git a/public/app/features/alerting/unified/components/receivers/TemplateEditor.tsx b/public/app/features/alerting/unified/components/receivers/TemplateEditor.tsx
index c2223ded6d5..e09b1f94efa 100644
--- a/public/app/features/alerting/unified/components/receivers/TemplateEditor.tsx
+++ b/public/app/features/alerting/unified/components/receivers/TemplateEditor.tsx
@@ -44,6 +44,9 @@ const TemplateEditor = (props: TemplateEditorProps) => {
showLineNumbers={true}
showMiniMap={false}
{...props}
+ monacoOptions={{
+ scrollBeyondLastLine: false,
+ }}
onEditorDidMount={onEditorDidMount}
onBeforeEditorMount={(monaco) => {
registerLanguage(monaco, goTemplateLanguageDefinition);
diff --git a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx
index 6ed886fc853..4c0efa81fe3 100644
--- a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx
+++ b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx
@@ -1,9 +1,9 @@
-import { css } from '@emotion/css';
-import { subDays } from 'date-fns';
+import { css, cx } from '@emotion/css';
+import { addMinutes, subDays, subHours } from 'date-fns';
import { Location } from 'history';
-import React, { useCallback, useEffect, useState } from 'react';
-import { FormProvider, useForm, useFormContext, Validate } from 'react-hook-form';
-import { useLocation } from 'react-router-dom';
+import React, { useRef, useState } from 'react';
+import { FormProvider, useForm, Validate } from 'react-hook-form';
+import { useToggle } from 'react-use';
import AutoSizer from 'react-virtualized-auto-sizer';
import { GrafanaTheme2 } from '@grafana/data';
@@ -11,28 +11,21 @@ import { isFetchError } from '@grafana/runtime';
import {
Alert,
Button,
- CollapsableSection,
- Field,
FieldSet,
Input,
LinkButton,
- Spinner,
- Tab,
- TabsBar,
useStyles2,
Stack,
+ useSplitter,
+ Drawer,
+ InlineField,
+ Box,
} from '@grafana/ui';
import { useCleanup } from 'app/core/hooks/useCleanup';
-import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
+import { AlertManagerCortexConfig, TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types';
import { useDispatch } from 'app/types';
-import {
- AlertField,
- TemplatePreviewErrors,
- TemplatePreviewResponse,
- TemplatePreviewResult,
- usePreviewTemplateMutation,
-} from '../../api/templateApi';
+import { AppChromeUpdate } from '../../../../../core/components/AppChrome/AppChromeUpdate';
import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector';
import { updateAlertManagerConfigAction } from '../../state/actions';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
@@ -40,10 +33,12 @@ import { makeAMLink } from '../../utils/misc';
import { initialAsyncRequestState } from '../../utils/redux';
import { ensureDefine } from '../../utils/templates';
import { ProvisionedResource, ProvisioningAlert } from '../Provisioning';
+import { EditorColumnHeader } from '../contact-points/templates/EditorColumnHeader';
import { PayloadEditor } from './PayloadEditor';
import { TemplateDataDocs } from './TemplateDataDocs';
import { TemplateEditor } from './TemplateEditor';
+import { TemplatePreview } from './TemplatePreview';
import { snippets } from './editor/templateDataSuggestions';
export interface TemplateFormValues {
@@ -64,35 +59,56 @@ interface Props {
}
export const isDuplicating = (location: Location) => location.pathname.endsWith('/duplicate');
-const DEFAULT_PAYLOAD = `[
- {
- "annotations": {
- "summary": "Instance instance1 has been down for more than 5 minutes"
- },
- "labels": {
- "instance": "instance1"
- },
- "startsAt": "${subDays(new Date(), 1).toISOString()}"
- }]
-`;
-
+/**
+ * We're going for this type of layout, but with the ability to resize the columns.
+ * To achieve this, we're using the useSplitter hook from Grafana UI twice.
+ * The first hook is for the vertical splitter between the template editor and the payload editor.
+ * The second hook is for the horizontal splitter between the template editor and the preview.
+ * If we're using a vanilla Alertmanager source, we don't show the payload editor nor the preview but we still use the splitter at 100/0.
+ *
+ * ┌───────────────────┐┌───────────┐
+ * │ Template ││ Preview │
+ * │ ││ │
+ * │ ││ │
+ * │ ││ │
+ * └───────────────────┘│ │
+ * ┌───────────────────┐│ │
+ * │ Payload ││ │
+ * │ ││ │
+ * │ ││ │
+ * │ ││ │
+ * └───────────────────┘└───────────┘
+ */
export const TemplateForm = ({ existing, alertManagerSourceName, config, provenance }: Props) => {
const styles = useStyles2(getStyles);
const dispatch = useDispatch();
useCleanup((state) => (state.unifiedAlerting.saveAMConfig = initialAsyncRequestState));
+ const formRef = useRef(null);
+ const isGrafanaAlertManager = alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME;
const { loading, error } = useUnifiedAlertingSelector((state) => state.saveAMConfig);
- const location = useLocation();
- const isduplicating = isDuplicating(location);
+ const [cheatsheetOpened, toggleCheatsheetOpened] = useToggle(false);
- const [payload, setPayload] = useState(DEFAULT_PAYLOAD);
+ const [payload, setPayload] = useState(defaultPayloadString);
const [payloadFormatError, setPayloadFormatError] = useState(null);
- const [view, setView] = useState<'content' | 'preview'>('content');
+ // splitter for template and payload editor
+ const columnSplitter = useSplitter({
+ direction: 'column',
+ // if Grafana Alertmanager, split 50/50, otherwise 100/0 because there is no payload editor
+ initialSize: isGrafanaAlertManager ? 0.5 : 1,
+ dragPosition: 'middle',
+ });
- const onPayloadError = () => setView('preview');
+ // splitter for template editor and preview
+ const rowSplitter = useSplitter({
+ direction: 'row',
+ // if Grafana Alertmanager, split 60/40, otherwise 100/0 because there is no preview
+ initialSize: isGrafanaAlertManager ? 0.6 : 1,
+ dragPosition: 'middle',
+ });
const submit = (values: TemplateFormValues) => {
// wrap content in "define" if it's not already wrapped, in case user did not do it/
@@ -152,115 +168,154 @@ export const TemplateForm = ({ existing, alertManagerSourceName, config, provena
? true
: 'Another template with this name already exists.';
};
- const isGrafanaAlertManager = alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME;
+
+ const actionButtons = (
+
+
+
+ Cancel
+
+
+ );
return (
-
-