Skip to content

Commit 8f14359

Browse files
authored
fix(ui): bulk uploads silently skips files without filenames (#14621)
### What? Fixes an issue where bulk uploading files with missing filenames would silently skip without providing error feedback to the user. ### Why? Files without filenames would attempt to upload, fail on the server, but show no error to the user. Only the network tab would show the failure. ### How? Check for missing filenames before upload and skip those files with an error count. Trigger validation after upload to show "A file name is required" error message. Failed files remain in the drawer for users to fix. Fixes #14512
1 parent 61c61fe commit 8f14359

File tree

3 files changed

+101
-2
lines changed

3 files changed

+101
-2
lines changed

packages/ui/src/elements/BulkUpload/FormsManager/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,21 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
333333
for (let i = 0; i < currentForms.length; i++) {
334334
try {
335335
const form = currentForms[i]
336+
const fileValue = form.formState?.file?.value
337+
338+
// Skip upload if file is missing a filename
339+
if (
340+
fileValue &&
341+
typeof fileValue === 'object' &&
342+
'name' in fileValue &&
343+
(!fileValue.name || fileValue.name === '')
344+
) {
345+
currentForms[i] = {
346+
...currentForms[i],
347+
errorCount: 1,
348+
}
349+
continue
350+
}
336351

337352
setLoadingText(t('general:uploadingBulk', { current: i + 1, total: currentForms.length }))
338353

test/uploads/e2e.spec.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,90 @@ describe('Uploads', () => {
12691269
// should show add files dropzone view
12701270
await expect(fieldBulkUploadDrawer.locator('.bulk-upload--add-files')).toBeVisible()
12711271
})
1272+
1273+
test('should show error when bulk uploading files with missing filenames and allow retry after fixing', async () => {
1274+
await page.goto(uploadsOne.create)
1275+
1276+
await page.setInputFiles(
1277+
'.file-field input[type="file"]',
1278+
path.resolve(dirname, './image.png'),
1279+
)
1280+
const filename = page.locator('.file-field__filename')
1281+
await expect(filename).toHaveValue('image.png')
1282+
1283+
const bulkUploadButton = page.locator('#field-hasManyUpload button', {
1284+
hasText: exactText('Create New'),
1285+
})
1286+
await bulkUploadButton.click()
1287+
1288+
const bulkUploadModal = page.locator('#hasManyUpload-bulk-upload-drawer-slug-1')
1289+
await expect(bulkUploadModal).toBeVisible()
1290+
1291+
await bulkUploadModal
1292+
.locator('.dropzone input[type="file"]')
1293+
.setInputFiles([
1294+
path.resolve(dirname, './image.png'),
1295+
path.resolve(dirname, './test-image.png'),
1296+
])
1297+
1298+
await bulkUploadModal
1299+
.locator('.bulk-upload--file-manager .render-fields #field-prefix')
1300+
.fill('prefix-one')
1301+
1302+
// Clear the filename from the first file
1303+
await bulkUploadModal.locator('.file-field__filename').clear()
1304+
1305+
const nextImageChevronButton = bulkUploadModal.locator(
1306+
'.bulk-upload--actions-bar__controls button:nth-of-type(2)',
1307+
)
1308+
await nextImageChevronButton.click()
1309+
1310+
await bulkUploadModal
1311+
.locator('.bulk-upload--file-manager .render-fields #field-prefix')
1312+
.fill('prefix-two')
1313+
1314+
const saveButton = bulkUploadModal.locator('.bulk-upload--actions-bar__saveButtons button')
1315+
await saveButton.click()
1316+
1317+
// Should show error message for failed files
1318+
await expect(page.locator('.payload-toast-container')).toContainText('Failed to save 1 files')
1319+
await expect(page.locator('.payload-toast-container')).toContainText(
1320+
'Successfully saved 1 files',
1321+
)
1322+
1323+
const errorCount = bulkUploadModal.locator('.file-selections .error-pill__count').first()
1324+
await expect(errorCount).toHaveText('1')
1325+
1326+
await expect(bulkUploadModal).toBeVisible()
1327+
1328+
// Navigate back to first file to fix it
1329+
const prevImageChevronButton = bulkUploadModal.locator(
1330+
'.bulk-upload--actions-bar__controls button:nth-of-type(1)',
1331+
)
1332+
await prevImageChevronButton.click()
1333+
1334+
// Should show "A file name is required" error message
1335+
await expect(bulkUploadModal.locator('.field-error')).toContainText('A file name is required')
1336+
1337+
// Filename field should be empty (as we cleared it)
1338+
await expect(bulkUploadModal.locator('.file-field__filename')).toHaveValue('')
1339+
1340+
// Add the filename back
1341+
await bulkUploadModal.locator('.file-field__filename').fill('fixed-filename.png')
1342+
1343+
await saveButton.click()
1344+
1345+
await expect(page.locator('.payload-toast-container')).toContainText(
1346+
'Successfully saved 1 files',
1347+
)
1348+
1349+
await expect(bulkUploadModal).toBeHidden()
1350+
1351+
const items = page.locator('#field-hasManyUpload .upload--has-many__dragItem')
1352+
await expect(items).toHaveCount(2)
1353+
1354+
await saveDocAndAssert(page)
1355+
})
12721356
})
12731357

12741358
describe('remote url fetching', () => {

test/uploads/payload-types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3787,6 +3787,6 @@ export interface Auth {
37873787

37883788

37893789
declare module 'payload' {
3790-
// @ts-ignore
3790+
// @ts-ignore
37913791
export interface GeneratedTypes extends Config {}
3792-
}
3792+
}

0 commit comments

Comments
 (0)