mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
Alerting: Make two boxes in template form adaptative to the screen (#67967)
Make two boxes in template form adaptative to the screen width to be align in the same line when possible
This commit is contained in:
parent
e0e2535c96
commit
97802e44a6
@ -2,6 +2,7 @@ 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 { AutoSizerProps } from 'react-virtualized-auto-sizer';
|
||||
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
@ -28,6 +29,10 @@ jest.mock('@grafana/ui', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('react-virtualized-auto-sizer', () => {
|
||||
return ({ children }: AutoSizerProps) => children({ height: 1, width: 1 });
|
||||
});
|
||||
|
||||
const PayloadEditorWithState = () => {
|
||||
const [payload, setPayload] = useState(DEFAULT_PAYLOAD);
|
||||
return (
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { css } 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';
|
||||
@ -86,10 +87,12 @@ export function PayloadEditor({
|
||||
<Icon name="info-circle" className={styles.tooltip} size="xl" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
<div className={styles.editorWrapper}>
|
||||
<CodeEditor
|
||||
width={640}
|
||||
height={363}
|
||||
width={width}
|
||||
height={362}
|
||||
language={'json'}
|
||||
showLineNumbers={true}
|
||||
showMiniMap={false}
|
||||
@ -97,6 +100,9 @@ export function PayloadEditor({
|
||||
readOnly={false}
|
||||
onBlur={setPayload}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
|
||||
<div className={styles.buttonsWrapper}>
|
||||
<Button
|
||||
@ -159,28 +165,39 @@ const AlertTemplateDataTable = () => {
|
||||
};
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
jsonEditor: css`
|
||||
width: 605px;
|
||||
height: 363px;
|
||||
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`
|
||||
padding-top: 38px;
|
||||
flex: 1;
|
||||
min-width: 450px;
|
||||
`,
|
||||
tooltip: css`
|
||||
padding-left: ${theme.spacing(1)};
|
||||
`,
|
||||
editorWrapper: css`
|
||||
width: min-content;
|
||||
padding-top: 7px;
|
||||
`,
|
||||
editor: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -4,6 +4,7 @@ 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 AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Stack } from '@grafana/experimental';
|
||||
@ -175,22 +176,25 @@ export const TemplateForm = ({ existing, alertManagerSourceName, config, provena
|
||||
/>
|
||||
</Field>
|
||||
<TemplatingGuideline />
|
||||
<Stack direction="row" alignItems={'center'}>
|
||||
<div>
|
||||
<div className={styles.editorsWrapper}>
|
||||
<div className={styles.contentContainer}>
|
||||
<TabsBar>
|
||||
<Tab label="Content" active={view === 'content'} onChangeTab={() => setView('content')} />
|
||||
{isGrafanaAlertManager && (
|
||||
<Tab label="Preview" active={view === 'preview'} onChangeTab={() => setView('preview')} />
|
||||
)}
|
||||
</TabsBar>
|
||||
<div className={styles.contentContainer}>
|
||||
<div className={styles.contentContainerEditor}>
|
||||
<AutoSizer>
|
||||
{({ width }) => (
|
||||
<>
|
||||
{view === 'content' ? (
|
||||
<div>
|
||||
<Field error={errors?.content?.message} invalid={!!errors.content?.message} required>
|
||||
<div className={styles.editWrapper}>
|
||||
<TemplateEditor
|
||||
value={getValues('content')}
|
||||
width={640}
|
||||
width={width}
|
||||
height={363}
|
||||
onBlur={(value) => setValue('content', value)}
|
||||
/>
|
||||
@ -219,12 +223,16 @@ export const TemplateForm = ({ existing, alertManagerSourceName, config, provena
|
||||
</div>
|
||||
) : (
|
||||
<TemplatePreview
|
||||
width={width}
|
||||
payload={payload}
|
||||
templateName={watch('name')}
|
||||
setPayloadFormatError={setPayloadFormatError}
|
||||
payloadFormatError={payloadFormatError}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
</div>
|
||||
{isGrafanaAlertManager && (
|
||||
@ -237,7 +245,7 @@ export const TemplateForm = ({ existing, alertManagerSourceName, config, provena
|
||||
onPayloadError={onPayloadError}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</div>
|
||||
</FieldSet>
|
||||
<CollapsableSection label="Data cheat sheet" isOpen={false} className={styles.collapsableSection}>
|
||||
<TemplateDataDocs />
|
||||
@ -348,11 +356,13 @@ export function TemplatePreview({
|
||||
templateName,
|
||||
payloadFormatError,
|
||||
setPayloadFormatError,
|
||||
width,
|
||||
}: {
|
||||
payload: string;
|
||||
templateName: string;
|
||||
payloadFormatError: string | null;
|
||||
setPayloadFormatError: (value: React.SetStateAction<string | null>) => void;
|
||||
width: number;
|
||||
}) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
@ -378,8 +388,7 @@ export function TemplatePreview({
|
||||
useEffect(() => onPreview(), [onPreview]);
|
||||
|
||||
return (
|
||||
<Stack direction="row" alignItems="center" gap={2}>
|
||||
<Stack direction="column">
|
||||
<div style={{ width: `${width}px` }} className={styles.preview.wrapper}>
|
||||
{isLoading && (
|
||||
<>
|
||||
<Spinner inline={true} /> Loading preview...
|
||||
@ -391,13 +400,17 @@ export function TemplatePreview({
|
||||
<Button onClick={onPreview} className={styles.preview.button} icon="arrow-up" type="button" variant="secondary">
|
||||
Refresh preview
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
contentContainer: css`
|
||||
flex: 1;
|
||||
margin-bottom: ${theme.spacing(6)};
|
||||
`,
|
||||
contentContainerEditor: css`
|
||||
flex:1;
|
||||
display: flex;
|
||||
padding-top: 10px;
|
||||
gap: ${theme.spacing(2)};
|
||||
@ -407,6 +420,8 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
${theme.breakpoints.up('xxl')} {
|
||||
flex - wrap: nowrap;
|
||||
}
|
||||
min-width: 450px;
|
||||
height: 363px;
|
||||
`,
|
||||
snippets: css`
|
||||
margin-top: ${theme.spacing(2)};
|
||||
@ -417,6 +432,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
font-weight: ${theme.typography.fontWeightBold};
|
||||
`,
|
||||
buttons: css`
|
||||
display: flex;
|
||||
& > * + * {
|
||||
margin-left: ${theme.spacing(1)};
|
||||
}
|
||||
@ -426,43 +442,39 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
max-width: 758px;
|
||||
`,
|
||||
editWrapper: css`
|
||||
display: block;
|
||||
display: flex;
|
||||
width: 100%
|
||||
heigth:100%;
|
||||
position: relative;
|
||||
width: 640px;
|
||||
height: 363px;
|
||||
`,
|
||||
toggle: css({
|
||||
toggle: css`
|
||||
color: theme.colors.text.secondary,
|
||||
marginRight: `${theme.spacing(1)}`,
|
||||
}),
|
||||
previewHeader: css({
|
||||
display: 'flex',
|
||||
cursor: 'pointer',
|
||||
alignItems: 'baseline',
|
||||
color: theme.colors.text.primary,
|
||||
'&:hover': {
|
||||
background: theme.colors.emphasize(theme.colors.background.primary, 0.03),
|
||||
},
|
||||
}),
|
||||
previewHeaderTitle: css({
|
||||
flexGrow: 1,
|
||||
overflow: 'hidden',
|
||||
fontSize: theme.typography.h4.fontSize,
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
margin: 0,
|
||||
}),
|
||||
marginRight: ${theme.spacing(1)}`,
|
||||
preview: {
|
||||
wrapper: css`
|
||||
display: flex;
|
||||
width: 100%
|
||||
heigth:100%;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
`,
|
||||
result: css`
|
||||
width: 640px;
|
||||
width: 100%;
|
||||
height: 363px;
|
||||
`,
|
||||
button: css`
|
||||
flex: none;
|
||||
width: fit-content;
|
||||
margin-top: ${theme.spacing(-3)};
|
||||
margin-top: -6px;
|
||||
`,
|
||||
},
|
||||
collapsableSection: css`
|
||||
width: fit-content;
|
||||
`,
|
||||
editorsWrapper: css`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-wrap: wrap;
|
||||
gap: ${theme.spacing(1)};
|
||||
`,
|
||||
});
|
||||
|
@ -45,6 +45,7 @@ describe('TemplatePreview component', () => {
|
||||
it('Should render error if payload has wrong format', async () => {
|
||||
render(
|
||||
<TemplatePreview
|
||||
width={50}
|
||||
payload={'bla bla bla'}
|
||||
templateName="potato"
|
||||
payloadFormatError={'Unexpected token b in JSON at position 0'}
|
||||
@ -61,6 +62,7 @@ describe('TemplatePreview component', () => {
|
||||
const setError = jest.fn();
|
||||
render(
|
||||
<TemplatePreview
|
||||
width={50}
|
||||
payload={'{"a":"b"}'}
|
||||
templateName="potato"
|
||||
payloadFormatError={'Unexpected token b in JSON at position 0'}
|
||||
@ -76,6 +78,7 @@ describe('TemplatePreview component', () => {
|
||||
it('Should render error if payload has wrong format rendering the preview', async () => {
|
||||
render(
|
||||
<TemplatePreview
|
||||
width={50}
|
||||
payload={'potatos and cherries'}
|
||||
templateName="potato"
|
||||
payloadFormatError={'Unexpected token b in JSON at position 0'}
|
||||
@ -95,6 +98,7 @@ describe('TemplatePreview component', () => {
|
||||
mockPreviewTemplateResponseRejected(server);
|
||||
render(
|
||||
<TemplatePreview
|
||||
width={50}
|
||||
payload={'[{"a":"b"}]'}
|
||||
templateName="potato"
|
||||
payloadFormatError={null}
|
||||
@ -118,6 +122,7 @@ describe('TemplatePreview component', () => {
|
||||
mockPreviewTemplateResponse(server, response);
|
||||
render(
|
||||
<TemplatePreview
|
||||
width={50}
|
||||
payload={'[{"a":"b"}]'}
|
||||
templateName="potato"
|
||||
payloadFormatError={null}
|
||||
@ -143,6 +148,7 @@ describe('TemplatePreview component', () => {
|
||||
mockPreviewTemplateResponse(server, response);
|
||||
render(
|
||||
<TemplatePreview
|
||||
width={50}
|
||||
payload={'[{"a":"b"}]'}
|
||||
templateName="potato"
|
||||
payloadFormatError={null}
|
||||
|
Loading…
Reference in New Issue
Block a user