Folders: Fix create button disable on submit & duplication logic (#109696)

This commit is contained in:
Tom Ratcliffe
2025-08-21 17:40:50 +01:00
committed by GitHub
parent 6d538f62c8
commit e49230d3eb
5 changed files with 40 additions and 20 deletions
@@ -48,9 +48,11 @@ function FolderCreationModal({
const styles = useStyles2(getStyles);
const notifyApp = useAppNotification();
const [title, setTitle] = useState('');
const [isCreatingFolder, setIsCreatingFolder] = useState(false);
const [createFolder] = useNewFolderMutation();
const onSubmit = async () => {
setIsCreatingFolder(true);
const { data, error } = await createFolder({ title });
if (error) {
@@ -59,6 +61,7 @@ function FolderCreationModal({
onCreate({ title: data.title, uid: data.uid });
notifyApp.success('Folder created');
}
setIsCreatingFolder(false);
};
return (
@@ -93,7 +96,7 @@ function FolderCreationModal({
</Button>
<Button
onClick={onSubmit}
disabled={!title}
disabled={!title || isCreatingFolder}
data-testid={selectors.components.AlertRules.newFolderNameCreateButton}
>
<Trans i18nKey="alerting.create-new-folder.folder.create">Create</Trans>
@@ -123,7 +123,11 @@ export default function CreateNewButton({
{parentFolder?.managedBy === ManagerKind.Repo || isProvisionedInstance ? (
<NewProvisionedFolderForm onDismiss={() => setShowNewFolderDrawer(false)} parentFolder={parentFolder} />
) : (
<NewFolderForm onConfirm={onCreateFolder} onCancel={() => setShowNewFolderDrawer(false)} />
<NewFolderForm
onConfirm={onCreateFolder}
onCancel={() => setShowNewFolderDrawer(false)}
parentFolder={parentFolder}
/>
)}
</Drawer>
)}
@@ -3,12 +3,14 @@ import { useForm } from 'react-hook-form';
import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { Button, Input, Field, Stack } from '@grafana/ui';
import { FolderDTO } from 'app/types/folders';
import { validationSrv } from '../../manage-dashboards/services/ValidationSrv';
interface Props {
onConfirm: (folderName: string) => void;
onCancel: () => void;
parentFolder?: FolderDTO;
}
interface FormModel {
@@ -17,11 +19,11 @@ interface FormModel {
const initialFormModel: FormModel = { folderName: '' };
export function NewFolderForm({ onCancel, onConfirm }: Props) {
export function NewFolderForm({ onCancel, onConfirm, parentFolder }: Props) {
const {
handleSubmit,
register,
formState: { errors },
formState: { errors, isSubmitting },
} = useForm<FormModel>({ defaultValues: initialFormModel });
const translatedFolderNameRequiredPhrase = t(
@@ -48,7 +50,7 @@ export function NewFolderForm({ onCancel, onConfirm }: Props) {
defaultValue={initialFormModel.folderName}
{...register('folderName', {
required: translatedFolderNameRequiredPhrase,
validate: async (v) => await validateFolderName(v),
validate: async (v) => await validateFolderName(v, parentFolder?.uid),
})}
/>
</Field>
@@ -56,7 +58,7 @@ export function NewFolderForm({ onCancel, onConfirm }: Props) {
<Button variant="secondary" fill="outline" onClick={onCancel}>
<Trans i18nKey="browse-dashboards.new-folder-form.cancel-label">Cancel</Trans>
</Button>
<Button type="submit">
<Button type="submit" disabled={isSubmitting}>
<Trans i18nKey="browse-dashboards.new-folder-form.create-label">Create</Trans>
</Button>
</Stack>
@@ -64,9 +66,9 @@ export function NewFolderForm({ onCancel, onConfirm }: Props) {
);
}
export async function validateFolderName(folderName: string) {
export async function validateFolderName(folderName: string, parentFolderUid?: string) {
try {
await validationSrv.validateNewFolderName(folderName);
await validationSrv.validateNewFolderName(folderName, parentFolderUid);
return true;
} catch (e) {
if (e instanceof Error) {
@@ -24,18 +24,28 @@ export class ValidationSrv {
);
}
validateNewFolderName(name?: string) {
return this.validate(
this.rootName,
name,
t(
'manage-dashboards.validation-srv.message-same-name-general',
'A folder or dashboard in the general folder with the same name already exists'
)
);
validateNewFolderName(name?: string, parentFolderUid?: string) {
const validationMessage = parentFolderUid
? t(
'manage-dashboards.validation-srv.message-same-name-current-folder',
'A dashboard or a folder with the same name already exists in the current folder'
)
: t(
'manage-dashboards.validation-srv.message-same-name-general',
'A folder or dashboard with the same name already exists in the root folder'
);
return this.validate(parentFolderUid || this.rootName, name, validationMessage);
}
private async validate(folderUID: string, name: string | undefined, existingErrorMessage: string) {
private async validate(
/** Folder in which to validate newly created resource */
folderUID: string,
/** Name of the resource being created */
name: string | undefined,
/** Error message to throw if the resource already exists */
existingErrorMessage: string
) {
name = (name || '').trim();
const nameLowerCased = name.toLowerCase();
@@ -59,7 +69,7 @@ export class ValidationSrv {
const searcher = getGrafanaSearcher();
const dashboardResults = await searcher.search({
kind: ['dashboard'],
kind: ['dashboard', 'folder'],
query: name,
location: folderUID || 'general',
});
+2 -1
View File
@@ -9755,7 +9755,8 @@
"message-name-required": "Name is required",
"message-reserved-name": "This is a reserved name and cannot be used for a folder.",
"message-same-name": "A dashboard or a folder with the same name already exists",
"message-same-name-general": "A folder or dashboard in the general folder with the same name already exists"
"message-same-name-current-folder": "A dashboard or a folder with the same name already exists in the current folder",
"message-same-name-general": "A folder or dashboard with the same name already exists in the root folder"
}
},
"metric-select": {