Alerting: updated alerting creation order (#48548)

This commit is contained in:
Gilles De Mey 2022-05-03 11:27:34 +02:00 committed by GitHub
parent d774deab99
commit 2d9d12380c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 331 additions and 293 deletions

View File

@ -18,12 +18,11 @@ import { initialAsyncRequestState } from '../../utils/redux';
import { rulerRuleToFormValues, getDefaultFormValues, getDefaultQueries } from '../../utils/rule-form';
import * as ruleId from '../../utils/rule-id';
import { AlertTypeStep } from './AlertTypeStep';
import { CloudConditionsStep } from './CloudConditionsStep';
import { CloudEvaluationBehavior } from './CloudEvaluationBehavior';
import { DetailsStep } from './DetailsStep';
import { GrafanaConditionsStep } from './GrafanaConditionsStep';
import { QueryStep } from './QueryStep';
import { GrafanaEvaluationBehavior } from './GrafanaEvaluationBehavior';
import { RuleInspector } from './RuleInspector';
import { QueryAndAlertConditionStep } from './query-and-alert-condition/QueryAndAlertConditionStep';
type Props = {
existing?: RuleWithLocation;
@ -151,11 +150,10 @@ export const AlertRuleForm: FC<Props> = ({ existing }) => {
<div className={styles.contentOuter}>
<CustomScrollbar autoHeightMin="100%" hideHorizontalTrack={true}>
<div className={styles.contentInner}>
<AlertTypeStep editingExistingRule={!!existing} />
<QueryAndAlertConditionStep editingExistingRule={!!existing} />
{showStep2 && (
<>
<QueryStep />
{type === RuleFormType.grafana ? <GrafanaConditionsStep /> : <CloudConditionsStep />}
{type === RuleFormType.grafana ? <GrafanaEvaluationBehavior /> : <CloudEvaluationBehavior />}
<DetailsStep />
</>
)}

View File

@ -1,215 +0,0 @@
import { css } from '@emotion/css';
import React, { FC } from 'react';
import { useFormContext } from 'react-hook-form';
import { DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental';
import { Field, Icon, Input, InputControl, Label, Tooltip, useStyles2 } from '@grafana/ui';
import { contextSrv } from 'app/core/services/context_srv';
import { AccessControlAction } from 'app/types';
import { RuleFormType, RuleFormValues } from '../../types/rule-form';
import { CloudRulesSourcePicker } from './CloudRulesSourcePicker';
import { GroupAndNamespaceFields } from './GroupAndNamespaceFields';
import { RuleEditorSection } from './RuleEditorSection';
import { Folder, RuleFolderPicker } from './RuleFolderPicker';
import { RuleTypePicker } from './rule-types/RuleTypePicker';
import { checkForPathSeparator } from './util';
interface Props {
editingExistingRule: boolean;
}
const recordingRuleNameValidationPattern = {
message:
'Recording rule name must be valid metric name. It may only contain letters, numbers, and colons. It may not contain whitespace.',
value: /^[a-zA-Z_:][a-zA-Z0-9_:]*$/,
};
export const AlertTypeStep: FC<Props> = ({ editingExistingRule }) => {
const styles = useStyles2(getStyles);
const { enabledRuleTypes, defaultRuleType } = getAvailableRuleTypes();
const {
register,
control,
watch,
formState: { errors },
setValue,
getValues,
} = useFormContext<RuleFormValues & { location?: string }>();
const ruleFormType = watch('type');
const dataSourceName = watch('dataSourceName');
return (
<RuleEditorSection stepNo={1} title="Rule type">
{!editingExistingRule && (
<Field error={errors.type?.message} invalid={!!errors.type?.message} data-testid="alert-type-picker">
<InputControl
render={({ field: { onChange } }) => (
<RuleTypePicker
aria-label="Rule type"
selected={getValues('type') ?? defaultRuleType}
onChange={onChange}
enabledTypes={enabledRuleTypes}
/>
)}
name="type"
control={control}
rules={{
required: { value: true, message: 'Please select alert type' },
}}
/>
</Field>
)}
<Field
className={styles.formInput}
label="Rule name"
error={errors?.name?.message}
invalid={!!errors.name?.message}
>
<Input
id="name"
{...register('name', {
required: { value: true, message: 'Must enter an alert name' },
pattern: ruleFormType === RuleFormType.cloudRecording ? recordingRuleNameValidationPattern : undefined,
validate: {
pathSeparator: (value: string) => {
// we use the alert rule name as the "groupname" for Grafana managed alerts, so we can't allow path separators
if (ruleFormType === RuleFormType.grafana) {
return checkForPathSeparator(value);
}
return true;
},
},
})}
autoFocus={true}
/>
</Field>
<div className={styles.flexRow}>
{(ruleFormType === RuleFormType.cloudRecording || ruleFormType === RuleFormType.cloudAlerting) && (
<Field
className={styles.formInput}
label="Select data source"
error={errors.dataSourceName?.message}
invalid={!!errors.dataSourceName?.message}
data-testid="datasource-picker"
>
<InputControl
render={({ field: { onChange, ref, ...field } }) => (
<CloudRulesSourcePicker
{...field}
onChange={(ds: DataSourceInstanceSettings) => {
// reset location if switching data sources, as different rules source will have different groups and namespaces
setValue('location', undefined);
onChange(ds?.name ?? null);
}}
/>
)}
name="dataSourceName"
control={control}
rules={{
required: { value: true, message: 'Please select a data source' },
}}
/>
</Field>
)}
</div>
{(ruleFormType === RuleFormType.cloudRecording || ruleFormType === RuleFormType.cloudAlerting) &&
dataSourceName && <GroupAndNamespaceFields rulesSourceName={dataSourceName} />}
{ruleFormType === RuleFormType.grafana && (
<div className={styles.flexRow}>
<Field
label={
<Label htmlFor="folder" description={'Select a folder to store your rule.'}>
<Stack gap={0.5}>
Folder
<Tooltip
placement="top"
content={
<div>
Each folder has unique folder permission. When you store multiple rules in a folder, the folder
access permissions get assigned to the rules.
</div>
}
>
<Icon name="info-circle" size="xs" />
</Tooltip>
</Stack>
</Label>
}
className={styles.formInput}
error={errors.folder?.message}
invalid={!!errors.folder?.message}
data-testid="folder-picker"
>
<InputControl
render={({ field: { ref, ...field } }) => (
<RuleFolderPicker inputId="folder" {...field} enableCreateNew={true} enableReset={true} />
)}
name="folder"
rules={{
required: { value: true, message: 'Please select a folder' },
validate: {
pathSeparator: (folder: Folder) => checkForPathSeparator(folder.title),
},
}}
/>
</Field>
<Field
label="Group"
data-testid="group-picker"
description="Rules within the same group are evaluated after the same time interval."
className={styles.formInput}
error={errors.group?.message}
invalid={!!errors.group?.message}
>
<Input
id="group"
{...register('group', {
required: { value: true, message: 'Must enter a group name' },
})}
/>
</Field>
</div>
)}
</RuleEditorSection>
);
};
function getAvailableRuleTypes() {
const canCreateGrafanaRules = contextSrv.hasPermission(AccessControlAction.AlertingRuleCreate);
const canCreateCloudRules = contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalWrite);
const defaultRuleType = canCreateGrafanaRules ? RuleFormType.grafana : RuleFormType.cloudAlerting;
const enabledRuleTypes: RuleFormType[] = [];
if (canCreateGrafanaRules) {
enabledRuleTypes.push(RuleFormType.grafana);
}
if (canCreateCloudRules) {
enabledRuleTypes.push(RuleFormType.cloudAlerting, RuleFormType.cloudRecording);
}
return { enabledRuleTypes, defaultRuleType };
}
const getStyles = (theme: GrafanaTheme2) => ({
formInput: css`
width: 330px;
& + & {
margin-left: ${theme.spacing(3)};
}
`,
flexRow: css`
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-end;
`,
});

View File

@ -11,7 +11,7 @@ import { timeOptions } from '../../utils/time';
import { PreviewRule } from './PreviewRule';
import { RuleEditorSection } from './RuleEditorSection';
export const CloudConditionsStep: FC = () => {
export const CloudEvaluationBehavior: FC = () => {
const styles = useStyles(getStyles);
const {
register,
@ -28,7 +28,7 @@ export const CloudConditionsStep: FC = () => {
}
return (
<RuleEditorSection stepNo={3} title="Define alert conditions">
<RuleEditorSection stepNo={2} title="Alert evaluation behavior">
<Field label="For" description="Expression has to be true for this long for the alert to be fired.">
<div className={styles.flexRow}>
<Field invalid={!!errors.forTime?.message} error={errors.forTime?.message} className={styles.inlineField}>

View File

@ -1,3 +1,4 @@
import { last } from 'lodash';
import React, { FC, useEffect, useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
@ -31,10 +32,13 @@ export const ConditionField: FC = () => {
// reset condition if option no longer exists or if it is unset, but there are options available
useEffect(() => {
const expressions = queries.filter((query) => query.datasourceUid === ExpressionDatasourceUID);
if (condition && !options.find(({ value }) => value === condition)) {
setValue('condition', expressions.length ? expressions[expressions.length - 1].refId : null);
} else if (!condition && expressions.length) {
setValue('condition', expressions[expressions.length - 1].refId);
const lastExpression = last(expressions);
const conditionExists = options.find(({ value }) => value === condition);
if (condition && !conditionExists) {
setValue('condition', lastExpression?.refId ?? null);
} else if (!condition && lastExpression) {
setValue('condition', lastExpression.refId, { shouldValidate: true });
}
}, [condition, options, queries, setValue]);

View File

@ -1,20 +1,42 @@
import { css } from '@emotion/css';
import React, { FC } from 'react';
import { useFormContext } from 'react-hook-form';
import { GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental';
import { useStyles2, Field, Input, InputControl, Label, Tooltip, Icon } from '@grafana/ui';
import { RuleFormType, RuleFormValues } from '../../types/rule-form';
import AnnotationsField from './AnnotationsField';
import { GroupAndNamespaceFields } from './GroupAndNamespaceFields';
import LabelsField from './LabelsField';
import { RuleEditorSection } from './RuleEditorSection';
import { RuleFolderPicker, Folder } from './RuleFolderPicker';
import { checkForPathSeparator } from './util';
const recordingRuleNameValidationPattern = {
message:
'Recording rule name must be valid metric name. It may only contain letters, numbers, and colons. It may not contain whitespace.',
value: /^[a-zA-Z_:][a-zA-Z0-9_:]*$/,
};
export const DetailsStep: FC = () => {
const { watch } = useFormContext<RuleFormValues>();
const {
register,
watch,
formState: { errors },
} = useFormContext<RuleFormValues & { location?: string }>();
const styles = useStyles2(getStyles);
const ruleFormType = watch('type');
const dataSourceName = watch('dataSourceName');
const type = watch('type');
return (
<RuleEditorSection
stepNo={type === RuleFormType.cloudRecording ? 3 : 4}
stepNo={type === RuleFormType.cloudRecording ? 2 : 3}
title={
type === RuleFormType.cloudRecording ? 'Add details for your recording rule' : 'Add details for your alert'
}
@ -24,8 +46,107 @@ export const DetailsStep: FC = () => {
: 'Write a summary and add labels to help you better manage your alerts'
}
>
<Field
className={styles.formInput}
label="Rule name"
error={errors?.name?.message}
invalid={!!errors.name?.message}
>
<Input
id="name"
{...register('name', {
required: { value: true, message: 'Must enter an alert name' },
pattern: ruleFormType === RuleFormType.cloudRecording ? recordingRuleNameValidationPattern : undefined,
validate: {
pathSeparator: (value: string) => {
// we use the alert rule name as the "groupname" for Grafana managed alerts, so we can't allow path separators
if (ruleFormType === RuleFormType.grafana) {
return checkForPathSeparator(value);
}
return true;
},
},
})}
/>
</Field>
{(ruleFormType === RuleFormType.cloudRecording || ruleFormType === RuleFormType.cloudAlerting) &&
dataSourceName && <GroupAndNamespaceFields rulesSourceName={dataSourceName} />}
{ruleFormType === RuleFormType.grafana && (
<div className={styles.flexRow}>
<Field
label={
<Label htmlFor="folder" description={'Select a folder to store your rule.'}>
<Stack gap={0.5}>
Folder
<Tooltip
placement="top"
content={
<div>
Each folder has unique folder permission. When you store multiple rules in a folder, the folder
access permissions get assigned to the rules.
</div>
}
>
<Icon name="info-circle" size="xs" />
</Tooltip>
</Stack>
</Label>
}
className={styles.formInput}
error={errors.folder?.message}
invalid={!!errors.folder?.message}
data-testid="folder-picker"
>
<InputControl
render={({ field: { ref, ...field } }) => (
<RuleFolderPicker inputId="folder" {...field} enableCreateNew={true} enableReset={true} />
)}
name="folder"
rules={{
required: { value: true, message: 'Please select a folder' },
validate: {
pathSeparator: (folder: Folder) => checkForPathSeparator(folder.title),
},
}}
/>
</Field>
<Field
label="Group"
data-testid="group-picker"
description="Rules within the same group are evaluated after the same time interval."
className={styles.formInput}
error={errors.group?.message}
invalid={!!errors.group?.message}
>
<Input
id="group"
{...register('group', {
required: { value: true, message: 'Must enter a group name' },
})}
/>
</Field>
</div>
)}
{type !== RuleFormType.cloudRecording && <AnnotationsField />}
<LabelsField />
</RuleEditorSection>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
formInput: css`
width: 330px;
& + & {
margin-left: ${theme.spacing(3)};
}
`,
flexRow: css`
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-end;
`,
});

View File

@ -9,7 +9,6 @@ import { RuleFormValues } from '../../types/rule-form';
import { positiveDurationValidationPattern, durationValidationPattern } from '../../utils/time';
import { CollapseToggle } from '../CollapseToggle';
import { ConditionField } from './ConditionField';
import { GrafanaAlertStatePicker } from './GrafanaAlertStatePicker';
import { GrafanaConditionEvalWarning } from './GrafanaConditionEvalWarning';
import { PreviewRule } from './PreviewRule';
@ -46,7 +45,7 @@ const evaluateEveryValidationOptions: RegisterOptions = {
},
};
export const GrafanaConditionsStep: FC = () => {
export const GrafanaEvaluationBehavior: FC = () => {
const styles = useStyles2(getStyles);
const [showErrorHandling, setShowErrorHandling] = useState(false);
const {
@ -58,8 +57,8 @@ export const GrafanaConditionsStep: FC = () => {
const evaluateForId = 'eval-for-input';
return (
<RuleEditorSection stepNo={3} title="Define alert conditions">
<ConditionField />
// TODO remove "and alert condition" for recording rules
<RuleEditorSection stepNo={2} title="Alert evaluation behavior">
<Field
label="Evaluate"
description="Evaluation interval applies to every rule within a group. It can overwrite the interval of an existing alert rule."

View File

@ -111,33 +111,6 @@ export class QueryEditor extends PureComponent<Props, State> {
);
};
renderAddQueryRow(styles: ReturnType<typeof getStyles>) {
return (
<HorizontalGroup spacing="md" align="flex-start">
<Button
type="button"
icon="plus"
onClick={this.onNewAlertingQuery}
variant="secondary"
aria-label={selectors.components.QueryTab.addQuery}
>
Query
</Button>
{config.expressionsEnabled && (
<Button
type="button"
icon="plus"
onClick={this.onNewExpressionQuery}
variant="secondary"
className={styles.expressionButton}
>
<span>Expression&nbsp;</span>
</Button>
)}
</HorizontalGroup>
);
}
isRunning() {
const data = Object.values(this.state.panelDataByRefId).find((d) => Boolean(d));
return data?.state === LoadingState.Loading;
@ -145,24 +118,19 @@ export class QueryEditor extends PureComponent<Props, State> {
renderRunQueryButton() {
const isRunning = this.isRunning();
const styles = getStyles(config.theme2);
if (isRunning) {
return (
<div className={styles.runWrapper}>
<Button icon="fa fa-spinner" type="button" variant="destructive" onClick={this.onCancelQueries}>
Cancel
</Button>
</div>
<Button icon="fa fa-spinner" type="button" variant="destructive" onClick={this.onCancelQueries}>
Cancel
</Button>
);
}
return (
<div className={styles.runWrapper}>
<Button icon="sync" type="button" onClick={this.onRunQueries}>
Run queries
</Button>
</div>
<Button icon="sync" type="button" onClick={this.onRunQueries}>
Run queries
</Button>
);
}
@ -180,8 +148,23 @@ export class QueryEditor extends PureComponent<Props, State> {
onDuplicateQuery={this.onDuplicateQuery}
onRunQueries={this.onRunQueries}
/>
{this.renderAddQueryRow(styles)}
{this.renderRunQueryButton()}
<HorizontalGroup spacing="sm" align="flex-start">
<Button
type="button"
icon="plus"
onClick={this.onNewAlertingQuery}
variant="secondary"
aria-label={selectors.components.QueryTab.addQuery}
>
Add query
</Button>
{config.expressionsEnabled && (
<Button type="button" icon="plus" onClick={this.onNewExpressionQuery} variant="secondary">
Add expression
</Button>
)}
{this.renderRunQueryButton()}
</HorizontalGroup>
</div>
);
}
@ -227,8 +210,5 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => {
border: 1px solid ${theme.colors.border.medium};
border-radius: ${theme.shape.borderRadius()};
`,
expressionButton: css`
margin-right: ${theme.spacing(0.5)};
`,
};
});

View File

@ -8,7 +8,7 @@ import { contextSrv } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
import { AccessControlAction } from 'app/types';
import { AlertTypeStep } from './AlertTypeStep';
import { AlertType } from './AlertType';
const ui = {
ruleTypePicker: {
@ -28,7 +28,7 @@ function renderAlertTypeStep() {
render(
<Provider store={store}>
<AlertTypeStep editingExistingRule={false} />
<AlertType editingExistingRule={false} />
</Provider>,
{ wrapper: FormProviderWrapper }
);

View File

@ -0,0 +1,116 @@
import { css } from '@emotion/css';
import React, { FC } from 'react';
import { useFormContext } from 'react-hook-form';
import { DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data';
import { Field, InputControl, useStyles2 } from '@grafana/ui';
import { contextSrv } from 'app/core/services/context_srv';
import { AccessControlAction } from 'app/types';
import { RuleFormType, RuleFormValues } from '../../../types/rule-form';
import { CloudRulesSourcePicker } from '../CloudRulesSourcePicker';
import { RuleTypePicker } from '../rule-types/RuleTypePicker';
interface Props {
editingExistingRule: boolean;
}
export const AlertType: FC<Props> = ({ editingExistingRule }) => {
const { enabledRuleTypes, defaultRuleType } = getAvailableRuleTypes();
const {
control,
formState: { errors },
getValues,
setValue,
watch,
} = useFormContext<RuleFormValues & { location?: string }>();
const styles = useStyles2(getStyles);
const ruleFormType = watch('type');
return (
<>
{!editingExistingRule && (
<Field error={errors.type?.message} invalid={!!errors.type?.message} data-testid="alert-type-picker">
<InputControl
render={({ field: { onChange } }) => (
<RuleTypePicker
aria-label="Rule type"
selected={getValues('type') ?? defaultRuleType}
onChange={onChange}
enabledTypes={enabledRuleTypes}
/>
)}
name="type"
control={control}
rules={{
required: { value: true, message: 'Please select alert type' },
}}
/>
</Field>
)}
<div className={styles.flexRow}>
{(ruleFormType === RuleFormType.cloudRecording || ruleFormType === RuleFormType.cloudAlerting) && (
<Field
className={styles.formInput}
label="Select data source"
error={errors.dataSourceName?.message}
invalid={!!errors.dataSourceName?.message}
data-testid="datasource-picker"
>
<InputControl
render={({ field: { onChange, ref, ...field } }) => (
<CloudRulesSourcePicker
{...field}
onChange={(ds: DataSourceInstanceSettings) => {
// reset location if switching data sources, as different rules source will have different groups and namespaces
setValue('location', undefined);
onChange(ds?.name ?? null);
}}
/>
)}
name="dataSourceName"
control={control}
rules={{
required: { value: true, message: 'Please select a data source' },
}}
/>
</Field>
)}
</div>
</>
);
};
function getAvailableRuleTypes() {
const canCreateGrafanaRules = contextSrv.hasPermission(AccessControlAction.AlertingRuleCreate);
const canCreateCloudRules = contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalWrite);
const defaultRuleType = canCreateGrafanaRules ? RuleFormType.grafana : RuleFormType.cloudAlerting;
const enabledRuleTypes: RuleFormType[] = [];
if (canCreateGrafanaRules) {
enabledRuleTypes.push(RuleFormType.grafana);
}
if (canCreateCloudRules) {
enabledRuleTypes.push(RuleFormType.cloudAlerting, RuleFormType.cloudRecording);
}
return { enabledRuleTypes, defaultRuleType };
}
const getStyles = (theme: GrafanaTheme2) => ({
formInput: css`
width: 330px;
& + & {
margin-left: ${theme.spacing(3)};
}
`,
flexRow: css`
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-end;
`,
});

View File

@ -3,31 +3,36 @@ import { useFormContext } from 'react-hook-form';
import { Field, InputControl } from '@grafana/ui';
import { RuleFormType, RuleFormValues } from '../../types/rule-form';
import { RuleFormType, RuleFormValues } from '../../../types/rule-form';
import { ExpressionEditor } from '../ExpressionEditor';
import { QueryEditor } from '../QueryEditor';
import { ExpressionEditor } from './ExpressionEditor';
import { QueryEditor } from './QueryEditor';
import { RuleEditorSection } from './RuleEditorSection';
export const QueryStep: FC = () => {
export const Query: FC = () => {
const {
control,
watch,
formState: { errors },
} = useFormContext<RuleFormValues>();
const type = watch('type');
const dataSourceName = watch('dataSourceName');
const isGrafanaManagedType = type === RuleFormType.grafana;
const isCloudAlertRuleType = type === RuleFormType.cloudAlerting;
const isRecordingRuleType = type === RuleFormType.cloudRecording;
const showCloudExpressionEditor = (isRecordingRuleType || isCloudAlertRuleType) && dataSourceName;
return (
<RuleEditorSection
stepNo={2}
title={type === RuleFormType.cloudRecording ? 'Create a query to be recorded' : 'Create a query to be alerted on'}
>
{(type === RuleFormType.cloudRecording || type === RuleFormType.cloudAlerting) && dataSourceName && (
<div>
{/* This is the PromQL Editor for Cloud rules and recording rules */}
{showCloudExpressionEditor && (
<Field error={errors.expression?.message} invalid={!!errors.expression?.message}>
<InputControl
name="expression"
render={({ field: { ref, ...field } }) => <ExpressionEditor {...field} dataSourceName={dataSourceName} />}
render={({ field: { ref, ...field } }) => {
return <ExpressionEditor {...field} dataSourceName={dataSourceName} />;
}}
control={control}
rules={{
required: { value: true, message: 'A valid expression is required' },
@ -35,7 +40,9 @@ export const QueryStep: FC = () => {
/>
</Field>
)}
{type === RuleFormType.grafana && (
{/* This is the editor for Grafana managed rules */}
{isGrafanaManagedType && (
<Field
invalid={!!errors.queries}
error={(!!errors.queries && 'Must provide at least one valid query.') || undefined}
@ -50,6 +57,6 @@ export const QueryStep: FC = () => {
/>
</Field>
)}
</RuleEditorSection>
</div>
);
};

View File

@ -0,0 +1,28 @@
import React, { FC } from 'react';
import { useFormContext } from 'react-hook-form';
import { RuleFormType, RuleFormValues } from '../../../types/rule-form';
import { ConditionField } from '../ConditionField';
import { RuleEditorSection } from '../RuleEditorSection';
import { AlertType } from './AlertType';
import { Query } from './Query';
interface Props {
editingExistingRule: boolean;
}
export const QueryAndAlertConditionStep: FC<Props> = ({ editingExistingRule }) => {
const { watch } = useFormContext<RuleFormValues>();
const type = watch('type');
const isGrafanaManagedType = type === RuleFormType.grafana;
return (
<RuleEditorSection stepNo={1} title="Set a query and alert condition">
<AlertType editingExistingRule={editingExistingRule} />
{type && <Query />}
{isGrafanaManagedType && <ConditionField />}
</RuleEditorSection>
);
};