persist form values after redirect to rule page

This commit is contained in:
laurenashleigh
2026-01-12 11:41:25 +00:00
parent d9e9d52028
commit de4ff128fd
4 changed files with 48 additions and 20 deletions
@@ -6,6 +6,7 @@ import { GrafanaTheme2 } from '@grafana/data';
import { t } from '@grafana/i18n'; import { t } from '@grafana/i18n';
import { Button, Drawer, Stack, useStyles2 } from '@grafana/ui'; import { Button, Drawer, Stack, useStyles2 } from '@grafana/ui';
import { useAppNotification } from 'app/core/copy/appNotification'; import { useAppNotification } from 'app/core/copy/appNotification';
import { getMessageFromError } from 'app/core/utils/errors';
import { RuleDefinitionSection } from 'app/features/alerting/unified/components/RuleDefinitionSection'; import { RuleDefinitionSection } from 'app/features/alerting/unified/components/RuleDefinitionSection';
import { isGrafanaGroupUpdatedResponse } from '../api/alertRuleModel'; import { isGrafanaGroupUpdatedResponse } from '../api/alertRuleModel';
@@ -22,14 +23,20 @@ export interface AlertRuleDrawerFormProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
title?: string; title?: string;
onContinueInAlerting?: () => void; onContinueInAlerting?: (values: RuleFormValues) => void;
prefill?: Partial<RuleFormValues>; prefill?: Partial<RuleFormValues>;
} }
export function AlertRuleDrawerForm({ isOpen, onClose, title, onContinueInAlerting, prefill }: AlertRuleDrawerFormProps) { export function AlertRuleDrawerForm({
isOpen,
onClose,
title,
onContinueInAlerting,
prefill,
}: AlertRuleDrawerFormProps) {
const baseDefaults = useMemo(() => getDefaultFormValues(RuleFormType.grafana), []); const baseDefaults = useMemo(() => getDefaultFormValues(RuleFormType.grafana), []);
const methods = useForm<RuleFormValues>({ const methods = useForm<RuleFormValues>({
defaultValues: prefill ? { ...baseDefaults, ...(prefill as Partial<RuleFormValues>) } : baseDefaults, defaultValues: prefill ? { ...baseDefaults, ...prefill } : baseDefaults,
}); });
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const [addRuleToRuleGroup] = useAddRuleToRuleGroup(); const [addRuleToRuleGroup] = useAddRuleToRuleGroup();
@@ -38,9 +45,9 @@ export function AlertRuleDrawerForm({ isOpen, onClose, title, onContinueInAlerti
// Keep form in sync if prefill changes between openings // Keep form in sync if prefill changes between openings
useEffect(() => { useEffect(() => {
if (prefill) { if (prefill) {
methods.reset({ ...baseDefaults, ...(prefill as Partial<RuleFormValues>) }); methods.reset({ ...baseDefaults, ...prefill });
} }
}, [prefill, methods]); }, [prefill, methods, baseDefaults]);
if (!isOpen) { if (!isOpen) {
return null; return null;
@@ -48,7 +55,8 @@ export function AlertRuleDrawerForm({ isOpen, onClose, title, onContinueInAlerti
const submit = async (values: RuleFormValues) => { const submit = async (values: RuleFormValues) => {
try { try {
const groupName = values.group && values.group.trim().length > 0 ? values.group : (values.name?.trim() || 'default'); const groupName =
values.group && values.group.trim().length > 0 ? values.group : values.name?.trim() || 'default';
const effectiveValues: RuleFormValues = { ...values, group: groupName }; const effectiveValues: RuleFormValues = { ...values, group: groupName };
const dto = formValuesToRulerGrafanaRuleDTO(effectiveValues); const dto = formValuesToRulerGrafanaRuleDTO(effectiveValues);
@@ -59,9 +67,9 @@ export function AlertRuleDrawerForm({ isOpen, onClose, title, onContinueInAlerti
return; return;
} }
notifyApp.error('Failed to create rule', 'The rule was not created. Please review the form and try again.'); notifyApp.error('Failed to create rule', 'The rule was not created. Please review the form and try again.');
} catch (err: any) { } catch (err) {
const msg = err?.data?.message || err?.message || 'Unknown error while creating the rule.'; const errorMessage = getMessageFromError(err);
notifyApp.error('Failed to create rule', msg); notifyApp.error('Failed to create rule', errorMessage);
} }
}; };
@@ -93,7 +101,7 @@ export function AlertRuleDrawerForm({ isOpen, onClose, title, onContinueInAlerti
{t('alerting.common.cancel', 'Cancel')} {t('alerting.common.cancel', 'Cancel')}
</Button> </Button>
{onContinueInAlerting && ( {onContinueInAlerting && (
<Button variant="secondary" type="button" onClick={onContinueInAlerting}> <Button variant="secondary" type="button" onClick={() => onContinueInAlerting(methods.getValues())}>
{t('alerting.simplified.continue-in-alerting', 'Continue in Alerting')} {t('alerting.simplified.continue-in-alerting', 'Continue in Alerting')}
</Button> </Button>
)} )}
@@ -83,7 +83,9 @@ export function RuleDefinitionSection({ type }: { type: RuleFormType }) {
isOpen={showLabelsEditor} isOpen={showLabelsEditor}
onClose={(labelsToUpdate) => { onClose={(labelsToUpdate) => {
if (labelsToUpdate) { if (labelsToUpdate) {
const filtered = labelsToUpdate.filter((l) => (l?.key ?? '').length > 0 || (l?.value ?? '').length > 0); const filtered = labelsToUpdate.filter(
(l) => (l?.key ?? '').length > 0 || (l?.value ?? '').length > 0
);
setValue('labels', filtered, { shouldDirty: true, shouldValidate: true }); setValue('labels', filtered, { shouldDirty: true, shouldValidate: true });
} }
setShowLabelsEditor(false); setShowLabelsEditor(false);
@@ -12,6 +12,7 @@ import { useSelector } from 'app/types/store';
import { LogMessages } from '../../Analytics'; import { LogMessages } from '../../Analytics';
import { AlertRuleDrawerForm } from '../../components/AlertRuleDrawerForm'; import { AlertRuleDrawerForm } from '../../components/AlertRuleDrawerForm';
import type { RuleFormValues } from '../../types/rule-form';
import { panelToRuleFormValues } from '../../utils/rule-form'; import { panelToRuleFormValues } from '../../utils/rule-form';
interface Props { interface Props {
@@ -58,10 +59,10 @@ export const NewRuleFromPanelButton = ({ dashboard, panel, className }: Props) =
); );
} }
const onContinueInAlerting = async () => { const navigateToAlerting = async (currentValues?: RuleFormValues) => {
logInfo(LogMessages.alertRuleFromPanel); logInfo(LogMessages.alertRuleFromPanel);
// Refresh values to ensure they're up-to-date with current panel state // Prefer current drawer values if provided; otherwise refresh from panel state
const updateToDateFormValues = await panelToRuleFormValues(panel, dashboard); const updateToDateFormValues = currentValues ?? (await panelToRuleFormValues(panel, dashboard));
const ruleFormUrl = urlUtil.renderUrl('alerting/new', { const ruleFormUrl = urlUtil.renderUrl('alerting/new', {
defaults: JSON.stringify(updateToDateFormValues), defaults: JSON.stringify(updateToDateFormValues),
returnTo: location.pathname + location.search, returnTo: location.pathname + location.search,
@@ -69,6 +70,14 @@ export const NewRuleFromPanelButton = ({ dashboard, panel, className }: Props) =
locationService.push(ruleFormUrl); locationService.push(ruleFormUrl);
}; };
const onContinueInAlertingFromDrawer = (values: RuleFormValues) => {
void navigateToAlerting(values);
};
const onContinueInAlertingButton = () => {
void navigateToAlerting(undefined);
};
const shouldUseDrawer = config.featureToggles.createAlertRuleFromPanel; const shouldUseDrawer = config.featureToggles.createAlertRuleFromPanel;
if (shouldUseDrawer) { if (shouldUseDrawer) {
@@ -85,7 +94,7 @@ export const NewRuleFromPanelButton = ({ dashboard, panel, className }: Props) =
<AlertRuleDrawerForm <AlertRuleDrawerForm
isOpen={isOpen} isOpen={isOpen}
onClose={() => setIsOpen(false)} onClose={() => setIsOpen(false)}
onContinueInAlerting={onContinueInAlerting} onContinueInAlerting={onContinueInAlertingFromDrawer}
prefill={formValues ?? undefined} prefill={formValues ?? undefined}
/> />
</> </>
@@ -95,7 +104,7 @@ export const NewRuleFromPanelButton = ({ dashboard, panel, className }: Props) =
return ( return (
<LinkButton <LinkButton
icon="bell" icon="bell"
onClick={onContinueInAlerting} onClick={onContinueInAlertingButton}
href={urlUtil.renderUrl('alerting/new', { href={urlUtil.renderUrl('alerting/new', {
defaults: JSON.stringify(formValues), defaults: JSON.stringify(formValues),
returnTo: location.pathname + location.search, returnTo: location.pathname + location.search,
@@ -9,6 +9,7 @@ import { VizPanel } from '@grafana/scenes';
import { Alert, Button } from '@grafana/ui'; import { Alert, Button } from '@grafana/ui';
import { LogMessages } from 'app/features/alerting/unified/Analytics'; import { LogMessages } from 'app/features/alerting/unified/Analytics';
import { AlertRuleDrawerForm } from 'app/features/alerting/unified/components/AlertRuleDrawerForm'; import { AlertRuleDrawerForm } from 'app/features/alerting/unified/components/AlertRuleDrawerForm';
import type { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
import { scenesPanelToRuleFormValues } from 'app/features/alerting/unified/utils/rule-form'; import { scenesPanelToRuleFormValues } from 'app/features/alerting/unified/utils/rule-form';
interface ScenesNewRuleFromPanelButtonProps { interface ScenesNewRuleFromPanelButtonProps {
@@ -45,10 +46,10 @@ export const ScenesNewRuleFromPanelButton = ({ panel, className }: ScenesNewRule
); );
} }
const onClick = async () => { const navigateToAlerting = async (currentValues?: RuleFormValues) => {
logInfo(LogMessages.alertRuleFromPanel); logInfo(LogMessages.alertRuleFromPanel);
const updateToDateFormValues = await scenesPanelToRuleFormValues(panel); const updateToDateFormValues = currentValues ?? (await scenesPanelToRuleFormValues(panel));
const ruleFormUrl = urlUtil.renderUrl('/alerting/new', { const ruleFormUrl = urlUtil.renderUrl('/alerting/new', {
defaults: JSON.stringify(updateToDateFormValues), defaults: JSON.stringify(updateToDateFormValues),
@@ -58,6 +59,14 @@ export const ScenesNewRuleFromPanelButton = ({ panel, className }: ScenesNewRule
locationService.push(ruleFormUrl); locationService.push(ruleFormUrl);
}; };
const onContinueInAlertingFromDrawer = (values: RuleFormValues) => {
void navigateToAlerting(values);
};
const onButtonClick = () => {
void navigateToAlerting(undefined);
};
const shouldUseDrawer = config.featureToggles.createAlertRuleFromPanel; const shouldUseDrawer = config.featureToggles.createAlertRuleFromPanel;
if (shouldUseDrawer) { if (shouldUseDrawer) {
@@ -76,7 +85,7 @@ export const ScenesNewRuleFromPanelButton = ({ panel, className }: ScenesNewRule
<AlertRuleDrawerForm <AlertRuleDrawerForm
isOpen={isOpen} isOpen={isOpen}
onClose={() => setIsOpen(false)} onClose={() => setIsOpen(false)}
onContinueInAlerting={onClick} onContinueInAlerting={onContinueInAlertingFromDrawer}
prefill={formValues ?? undefined} prefill={formValues ?? undefined}
/> />
</> </>
@@ -84,7 +93,7 @@ export const ScenesNewRuleFromPanelButton = ({ panel, className }: ScenesNewRule
} }
return ( return (
<Button icon="bell" onClick={onClick} className={className} data-testid="create-alert-rule-button"> <Button icon="bell" onClick={onButtonClick} className={className} data-testid="create-alert-rule-button">
<Trans i18nKey="dashboard-scene.scenes-new-rule-from-panel-button.new-alert-rule">New alert rule</Trans> <Trans i18nKey="dashboard-scene.scenes-new-rule-from-panel-button.new-alert-rule">New alert rule</Trans>
</Button> </Button>
); );