From d7ddb447d5c6a32766a7bb7ff448fec89cd19ed1 Mon Sep 17 00:00:00 2001 From: emyarod Date: Tue, 22 Sep 2020 16:15:24 -0500 Subject: [PATCH] feat(FileUploaderDropContainer): validate filetypes from OS file picker (#6868) * feat(FileUploaderDropContainer): validate filetypes from OS file picker * docs(drop-container): add file type validation logic * chore: lint * docs(drag-and-drop-file-uploader): add file type validation logic Co-authored-by: Alessandra Davila --- .../drag-and-drop-file-uploader/src/index.js | 41 +++++++++++++------ .../FileUploader/FileUploaderDropContainer.js | 37 +++++++++-------- .../FileUploader/stories/drop-container.js | 19 +++++++++ 3 files changed, 69 insertions(+), 28 deletions(-) diff --git a/packages/react/examples/drag-and-drop-file-uploader/src/index.js b/packages/react/examples/drag-and-drop-file-uploader/src/index.js index f3ac4445d476..23208669618f 100644 --- a/packages/react/examples/drag-and-drop-file-uploader/src/index.js +++ b/packages/react/examples/drag-and-drop-file-uploader/src/index.js @@ -24,10 +24,10 @@ const { prefix } = settings; function ExampleDropContainerApp(props) { const [files, setFiles] = useState([]); - const handleDrop = e => { + const handleDrop = (e) => { e.preventDefault(); }; - const handleDragover = e => { + const handleDragover = (e) => { e.preventDefault(); }; useEffect(() => { @@ -38,7 +38,7 @@ function ExampleDropContainerApp(props) { document.removeEventListener('dragover', handleDragover); }; }, []); - const uploadFile = async fileToUpload => { + const uploadFile = async (fileToUpload) => { // file size validation if (fileToUpload.filesize > 512000) { const updatedFile = { @@ -49,8 +49,25 @@ function ExampleDropContainerApp(props) { errorSubject: 'File size exceeds limit', errorBody: '500kb max file size. Select a new file and try again.', }; - setFiles(files => - files.map(file => + setFiles((files) => + files.map((file) => + file.uuid === fileToUpload.uuid ? updatedFile : file + ) + ); + return; + } + // file type validation + if (fileToUpload.invalidFileType) { + const updatedFile = { + ...fileToUpload, + status: 'edit', + iconDescription: 'Delete file', + invalid: true, + errorSubject: 'Invalid file type', + errorBody: `"${fileToUpload.name}" does not have a valid file type.`, + }; + setFiles((files) => + files.map((file) => file.uuid === fileToUpload.uuid ? updatedFile : file ) ); @@ -73,8 +90,8 @@ function ExampleDropContainerApp(props) { status: 'complete', iconDescription: 'Upload complete', }; - setFiles(files => - files.map(file => + setFiles((files) => + files.map((file) => file.uuid === fileToUpload.uuid ? updatedFile : file ) ); @@ -86,8 +103,8 @@ function ExampleDropContainerApp(props) { status: 'edit', iconDescription: 'Remove file', }; - setFiles(files => - files.map(file => + setFiles((files) => + files.map((file) => file.uuid === fileToUpload.uuid ? updatedFile : file ) ); @@ -99,8 +116,8 @@ function ExampleDropContainerApp(props) { iconDescription: 'Upload failed', invalid: true, }; - setFiles(files => - files.map(file => + setFiles((files) => + files.map((file) => file.uuid === fileToUpload.uuid ? updatedFile : file ) ); @@ -110,7 +127,7 @@ function ExampleDropContainerApp(props) { const onAddFiles = useCallback( (evt, { addedFiles }) => { evt.stopPropagation(); - const newFiles = addedFiles.map(file => ({ + const newFiles = addedFiles.map((file) => ({ uuid: uid(), name: file.name, filesize: file.size, diff --git a/packages/react/src/components/FileUploader/FileUploaderDropContainer.js b/packages/react/src/components/FileUploader/FileUploaderDropContainer.js index 35e61a3b0733..31f17d741f54 100644 --- a/packages/react/src/components/FileUploader/FileUploaderDropContainer.js +++ b/packages/react/src/components/FileUploader/FileUploaderDropContainer.js @@ -43,23 +43,28 @@ function FileUploaderDropContainer({ * @param {Event} event - Event object, used to get the list of files added */ function validateFiles(event) { - if (event.type === 'drop') { - const transferredFiles = [...event.dataTransfer.files]; - if (!accept.length) { - return transferredFiles; - } - const acceptedTypes = new Set(accept); - return transferredFiles.filter(({ name, type: mimeType = '' }) => { - const fileExtensionRegExp = new RegExp(/\.[0-9a-z]+$/, 'i'); - const hasFileExtension = fileExtensionRegExp.test(name); - if (!hasFileExtension) { - return false; - } - const [fileExtension] = name.match(fileExtensionRegExp); - return acceptedTypes.has(mimeType) || acceptedTypes.has(fileExtension); - }); + const transferredFiles = + event.type === 'drop' + ? [...event.dataTransfer.files] + : [...event.target.files]; + if (!accept.length) { + return transferredFiles; } - return [...event.target.files]; + const acceptedTypes = new Set(accept); + return transferredFiles.reduce((acc, curr) => { + const { name, type: mimeType = '' } = curr; + const fileExtensionRegExp = new RegExp(/\.[0-9a-z]+$/, 'i'); + const hasFileExtension = fileExtensionRegExp.test(name); + if (!hasFileExtension) { + return acc; + } + const [fileExtension] = name.match(fileExtensionRegExp); + if (acceptedTypes.has(mimeType) || acceptedTypes.has(fileExtension)) { + return acc.concat([curr]); + } + curr.invalidFileType = true; + return acc.concat([curr]); + }, []); } function handleChange(event) { diff --git a/packages/react/src/components/FileUploader/stories/drop-container.js b/packages/react/src/components/FileUploader/stories/drop-container.js index 164bf958dce7..14e6cd4dbdec 100644 --- a/packages/react/src/components/FileUploader/stories/drop-container.js +++ b/packages/react/src/components/FileUploader/stories/drop-container.js @@ -49,6 +49,24 @@ const ExampleDropContainerApp = (props) => { return; } + // file type validation + if (fileToUpload.invalidFileType) { + const updatedFile = { + ...fileToUpload, + status: 'edit', + iconDescription: 'Delete file', + invalid: true, + errorSubject: 'Invalid file type', + errorBody: `"${fileToUpload.name}" does not have a valid file type.`, + }; + setFiles((files) => + files.map((file) => + file.uuid === fileToUpload.uuid ? updatedFile : file + ) + ); + return; + } + // simulate network request time const rand = Math.random() * 1000; setTimeout(() => { @@ -87,6 +105,7 @@ const ExampleDropContainerApp = (props) => { filesize: file.size, status: 'uploading', iconDescription: 'Uploading', + invalidFileType: file.invalidFileType, })); // eslint-disable-next-line react/prop-types if (props.multiple) {