From 46b9094c20f22a2610922bf529e1f927d0ff16b9 Mon Sep 17 00:00:00 2001 From: Haris Rozajac <58232930+harisrozajac@users.noreply.github.com> Date: Tue, 28 Oct 2025 08:39:25 -0600 Subject: [PATCH] [release-11.6.8] Dashboards: Disable saving while title is validating (#113057) save button validation --- .../saving/SaveDashboardAsForm.tsx | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx b/public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx index 96e52c2346a..eafc1d1e239 100644 --- a/public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx +++ b/public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx @@ -1,5 +1,5 @@ import debounce from 'debounce-promise'; -import { ChangeEvent, useState } from 'react'; +import { ChangeEvent, useEffect, useState } from 'react'; import { UseFormSetValue, useForm } from 'react-hook-form'; import { selectors } from '@grafana/e2e-selectors'; @@ -28,7 +28,7 @@ export interface Props { export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) { const { changedSaveModel } = changeInfo; - const { register, handleSubmit, setValue, formState, getValues, watch } = useForm({ + const { register, handleSubmit, setValue, formState, getValues, watch, trigger } = useForm({ mode: 'onBlur', defaultValues: { title: changeInfo.isNew ? changedSaveModel.title! : `${changedSaveModel.title} Copy`, @@ -41,13 +41,18 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) { }, }); - const { errors, isValid, defaultValues } = formState; + const { errors, isValid, defaultValues, validatingFields } = formState; const formValues = watch(); const { state, onSaveDashboard } = useSaveDashboard(false); const [contentSent, setContentSent] = useState<{ title?: string; folderUid?: string }>({}); - const [hasFolderChanged, setHasFolderChanged] = useState(false); + + // Validate title on form mount to catch invalid default values + useEffect(() => { + trigger('title'); + }, [trigger]); + const onSave = async (overwrite: boolean) => { const data = getValues(); @@ -81,8 +86,15 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) { ); const saveButton = (overwrite: boolean) => { - const showSaveButton = !isValid && hasFolderChanged ? true : isValid; - return ; + const isTitleValidating = !!validatingFields.title; + return ( + + ); }; function renderFooter(error?: Error) { const formValuesMatchContentSent = @@ -133,8 +145,8 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) { { setValue('folder', { uid, title }); - const folderUid = dashboard.state.meta.folderUid; - setHasFolderChanged(uid !== folderUid); + // Re-validate title when folder changes to check for duplicates in new folder + trigger('title'); }} // Old folder picker fields value={formValues.folder?.uid}