From d513d6ec17880d74478379617272a06a17dd919d Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Mon, 22 Feb 2021 04:44:09 -0300 Subject: [PATCH] fix(upload): apply accept attribute (#558) Co-authored-by: Philipp Fritsche --- src/__tests__/upload.js | 52 +++++++++++++++++++++++++++++++++++++++++ src/upload.js | 30 ++++++++++++++++++++---- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/__tests__/upload.js b/src/__tests__/upload.js index f09a6ae2..fb6a2b7d 100644 --- a/src/__tests__/upload.js +++ b/src/__tests__/upload.js @@ -163,3 +163,55 @@ test('should call onChange/input bubbling up the event when a file is selected', expect(onInputInput).toHaveBeenCalledTimes(1) expect(onInputForm).toHaveBeenCalledTimes(1) }) + +test('should upload file with accepted format', () => { + const file = new File(['hello'], 'hello.png', {type: 'image/png'}) + const {element} = setup('') + + userEvent.upload(element, file) + + expect(element.files).toHaveLength(1) +}) + +test('should upload multiple files with accepted format', () => { + const files = [ + new File(['hello'], 'hello.png', {type: 'image/png'}), + new File(['there'], 'there.jpg', {type: 'audio/mp3'}), + new File(['there'], 'there.csv', {type: 'text/csv'}), + new File(['there'], 'there.jpg', {type: 'video/mp4'}), + ] + const {element} = setup(` + + `) + + userEvent.upload(element, files) + + expect(element.files).toHaveLength(3) +}) + +test('should not upload file with unaccepted format', () => { + const file = new File(['hello'], 'hello.png', {type: 'image/png'}) + const {element} = setup('') + + userEvent.upload(element, file) + + expect(element.files).toHaveLength(0) +}) + +test('should not upload multiple files with unaccepted formats', () => { + const files = [ + new File(['hello'], 'hello.txt', {type: 'text/plain'}), + new File(['there'], 'there.pdf', {type: 'application/pdf'}), + new File(['there'], 'there.png', {type: 'image/png'}), + ] + const {element} = setup(` + + `) + + userEvent.upload(element, files) + + expect(element.files).toHaveLength(0) +}) diff --git a/src/upload.js b/src/upload.js index a252b438..7a3b990a 100644 --- a/src/upload.js +++ b/src/upload.js @@ -10,16 +10,20 @@ function upload(element, fileOrFiles, init) { const input = element.tagName === 'LABEL' ? element.control : element - const files = (Array.isArray(fileOrFiles) - ? fileOrFiles - : [fileOrFiles] - ).slice(0, input.multiple ? undefined : 1) + const files = (Array.isArray(fileOrFiles) ? fileOrFiles : [fileOrFiles]) + .filter(file => isAcceptableFile(file, element.accept)) + .slice(0, input.multiple ? undefined : 1) // blur fires when the file selector pops up blur(element, init) // focus fires when they make their selection focus(element, init) + // treat empty array as if the user just closed the file upload dialog + if (files.length === 0) { + return + } + // the event fired in the browser isn't actually an "input" or "change" event // but a new Event with a type set to "input" and "change" // Kinda odd... @@ -46,4 +50,22 @@ function upload(element, fileOrFiles, init) { }) } +function isAcceptableFile(file, accept) { + if (!accept) { + return true + } + + const wildcards = ['audio/*', 'image/*', 'video/*'] + + return accept.split(',').some(acceptToken => { + if (acceptToken[0] === '.') { + // tokens starting with a dot represent a file extension + return file.name.endsWith(acceptToken) + } else if (wildcards.includes(acceptToken)) { + return file.type.startsWith(acceptToken.substr(0, acceptToken.length - 1)) + } + return file.type === acceptToken + }) +} + export {upload}