Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion public/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"generalError": "Error",
"notUnique": "File names must be unique.",
"or": "or",
"unsupportedExtension": "File names must end in {{allowedExtensions}}."
"unsupportedExtension": "File names must end in {{allowedExtensions}}.",
"emptyFileName": "File name cannot be empty."
},
"files": "Project files",
"images": "Image gallery",
Expand Down
7 changes: 7 additions & 0 deletions src/components/Modals/NewFileModal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,11 @@ describe("Testing the new file modal", () => {
];
expect(store.getActions()).toEqual(expectedActions);
});

test("Empty file name throws error", () => {
fireEvent.change(inputBox, { target: { value: ".py" } });
fireEvent.click(saveButton);
const expectedActions = [setNameError("filePanel.errors.emptyFileName")];
expect(store.getActions()).toEqual(expectedActions);
});
});
7 changes: 7 additions & 0 deletions src/components/Modals/RenameFileModal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,11 @@ describe("Testing the rename file modal", () => {
];
expect(store.getActions()).toEqual(expectedActions);
});

test("Empty file name throws error", () => {
fireEvent.change(inputBox, { target: { value: ".py" } });
fireEvent.click(saveButton);
const expectedActions = [setNameError("filePanel.errors.emptyFileName")];
expect(store.getActions()).toEqual(expectedActions);
});
});
13 changes: 11 additions & 2 deletions src/utils/componentNameValidation.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ const allowedExtensions = {
const reservedFileNames = ["INSTRUCTIONS.md"];

const isValidFileName = (fileName, projectType, componentNames) => {
const extension = fileName.split(".").slice(1).join(".");
const parts = fileName.split(".");
const extension = parts.slice(1).join(".");
const baseName = parts[0];

if (
baseName.length > 0 &&
!reservedFileNames.includes(fileName) &&
allowedExtensions[projectType].includes(extension) &&
!componentNames.includes(fileName) &&
Expand All @@ -31,7 +35,10 @@ export const validateFileName = (
callback,
currentFileName = null,
) => {
const extension = fileName.split(".").slice(1).join(".");
const parts = fileName.split(".");
const extension = parts.slice(1).join(".");
const baseName = parts[0];

if (
isValidFileName(fileName, projectType, componentNames) ||
(currentFileName && fileName === currentFileName)
Expand All @@ -57,6 +64,8 @@ export const validateFileName = (
}),
),
);
} else if (baseName.length === 0) {
dispatch(setNameError(t("filePanel.errors.emptyFileName")));
Comment on lines 64 to +68
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The emptyFileName branch runs after the unsupported-extension branch. For truly blank inputs like "" or ".", extension is "" so this function will currently dispatch unsupportedExtension rather than the new emptyFileName message. Consider checking fileName.trim().length === 0 / baseName.length === 0 before extension validation (or explicitly handling extension === "") so empty inputs consistently show the empty-name error.

Copilot uses AI. Check for mistakes.
} else {
dispatch(setNameError(t("filePanel.errors.generalError")));
}
Expand Down
Loading