From f7cf56fedb4043ddaa3bbf9d0456c0de18f60e22 Mon Sep 17 00:00:00 2001 From: mfrances Date: Mon, 25 Nov 2024 11:17:52 -0500 Subject: [PATCH 1/2] feat(FileUpload): expose dropdown error types --- .../src/components/FileUpload/FileUpload.tsx | 8 +++++--- .../FileUpload/examples/FileUpload.md | 2 +- .../examples/FileUploadTextWithRestrictions.tsx | 17 +++++++++++------ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/react-core/src/components/FileUpload/FileUpload.tsx b/packages/react-core/src/components/FileUpload/FileUpload.tsx index ce4c8e04f33..ed9b1c10288 100644 --- a/packages/react-core/src/components/FileUpload/FileUpload.tsx +++ b/packages/react-core/src/components/FileUpload/FileUpload.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { DropzoneInputProps, DropzoneOptions, FileRejection, useDropzone } from 'react-dropzone'; +import { DropzoneInputProps, DropzoneOptions, FileRejection, useDropzone, ErrorCode } from 'react-dropzone'; import { FileUploadField, FileUploadFieldProps } from './FileUploadField'; import { readFile, fileReaderType } from '../../helpers/fileUtils'; import { DropEvent } from '../../helpers/typeUtils'; @@ -84,6 +84,8 @@ export interface FileUploadProps onTextChange?: (event: React.ChangeEvent, text: string) => void; } +export { ErrorCode as DropzoneErrorCode }; // FileInvalidType, FileTooLarge, FileTooSmall, TooManyFiles + export const FileUpload: React.FunctionComponent = ({ id, type, @@ -123,8 +125,8 @@ export const FileUpload: React.FunctionComponent = ({ dropzoneProps.onDropAccepted && dropzoneProps.onDropAccepted(acceptedFiles, event); }; - const onDropRejected = (rejectedFiles: FileRejection[], event: DropEvent) => { - dropzoneProps.onDropRejected && dropzoneProps.onDropRejected(rejectedFiles, event); + const onDropRejected = (rejectedFiles: FileRejection[], event: DropEvent, error: ErrorCode) => { + dropzoneProps.onDropRejected && dropzoneProps.onDropRejected(rejectedFiles, event, error); }; const onClearButtonClick = (event: React.MouseEvent) => { diff --git a/packages/react-core/src/components/FileUpload/examples/FileUpload.md b/packages/react-core/src/components/FileUpload/examples/FileUpload.md index b6f8efbd109..4280d6a22c3 100644 --- a/packages/react-core/src/components/FileUpload/examples/FileUpload.md +++ b/packages/react-core/src/components/FileUpload/examples/FileUpload.md @@ -40,7 +40,7 @@ Typing/pasting text in the box will call `onTextChange` with a string, and a str ### Restricting file size and type -Any [props accepted by `react-dropzone`'s `Dropzone` component](https://react-dropzone.js.org/#!/Dropzone) can be passed as a `dropzoneProps` object in order to customize the behavior of the Dropzone, such as restricting the size and type of files allowed. The following example will only accept CSV files smaller than 1 KB. Note that file type determination is not reliable across platforms (see the note on react-dropzone's docs about the `accept` prop), so be sure to test the behavior of your file upload restriction on all browsers and operating systems targeted by your application. +Any [props accepted by `react-dropzone`'s `Dropzone` component](https://react-dropzone.js.org/#!/Dropzone) can be passed as a `dropzoneProps` object in order to customize the behavior of the Dropzone, such as restricting the size and type of files allowed. You can also capture and act upon native `react-dropzone` errors using the exposed `DropzoneErrorCode` enum. The following example will only accept CSV files smaller than 1 KB. Note that file type determination is not reliable across platforms (see the note on react-dropzone's docs about the `accept` prop), so be sure to test the behavior of your file upload restriction on all browsers and operating systems targeted by your application. #### IMPORTANT: A note about security diff --git a/packages/react-core/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx b/packages/react-core/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx index 15858108488..9250f705442 100644 --- a/packages/react-core/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx +++ b/packages/react-core/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { FileUpload, + DropzoneErrorCode, FileUploadHelperText, Form, FormGroup, @@ -9,13 +10,13 @@ import { DropEvent, Icon } from '@patternfly/react-core'; -import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; export const TextFileUploadWithRestrictions: React.FunctionComponent = () => { const [value, setValue] = React.useState(''); const [filename, setFilename] = React.useState(''); const [isLoading, setIsLoading] = React.useState(false); const [isRejected, setIsRejected] = React.useState(false); + const [message, setMessage] = React.useState('Must be a CSV file no larger than 1 KB'); const handleFileInputChange = (_, file: File) => { setFilename(file.name); @@ -66,7 +67,13 @@ export const TextFileUploadWithRestrictions: React.FunctionComponent = () => { dropzoneProps={{ accept: { 'text/csv': ['.csv'] }, maxSize: 1024, - onDropRejected: handleFileRejected + onDropRejected: (rejections) => { + const error = rejections[0].errors[0]; + if (error.code === DropzoneErrorCode.FileInvalidType) { + setMessage('File must be a valid CSV file'); + } + handleFileRejected(); + } }} validated={isRejected ? 'error' : 'default'} browseButtonText="Upload" @@ -77,10 +84,8 @@ export const TextFileUploadWithRestrictions: React.FunctionComponent = () => { {isRejected ? ( <> - - - - Must be a CSV file no larger than 1 KB + {/* */} + {message} ) : ( 'Upload a CSV file' From f5d9764d82c57e64f4da6508899a73af1d76ab2a Mon Sep 17 00:00:00 2001 From: mfrances Date: Mon, 25 Nov 2024 13:48:31 -0500 Subject: [PATCH 2/2] updated due to dropzone changes and fixed legacy bug in example --- .../src/components/FileUpload/FileUpload.tsx | 4 ++-- .../FileUploadTextWithRestrictions.tsx | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/react-core/src/components/FileUpload/FileUpload.tsx b/packages/react-core/src/components/FileUpload/FileUpload.tsx index ed9b1c10288..4aa06c62332 100644 --- a/packages/react-core/src/components/FileUpload/FileUpload.tsx +++ b/packages/react-core/src/components/FileUpload/FileUpload.tsx @@ -125,8 +125,8 @@ export const FileUpload: React.FunctionComponent = ({ dropzoneProps.onDropAccepted && dropzoneProps.onDropAccepted(acceptedFiles, event); }; - const onDropRejected = (rejectedFiles: FileRejection[], event: DropEvent, error: ErrorCode) => { - dropzoneProps.onDropRejected && dropzoneProps.onDropRejected(rejectedFiles, event, error); + const onDropRejected = (rejectedFiles: FileRejection[], event: DropEvent) => { + dropzoneProps.onDropRejected && dropzoneProps.onDropRejected(rejectedFiles, event); }; const onClearButtonClick = (event: React.MouseEvent) => { diff --git a/packages/react-core/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx b/packages/react-core/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx index 9250f705442..a942654f600 100644 --- a/packages/react-core/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx +++ b/packages/react-core/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx @@ -30,16 +30,25 @@ export const TextFileUploadWithRestrictions: React.FunctionComponent = () => { setValue(value); }; - const handleClear = (_event: React.MouseEvent) => { + const reset = () => { setFilename(''); setValue(''); + }; + + const handleClear = (_event: React.MouseEvent) => { + reset(); setIsRejected(false); }; const handleFileRejected = () => { + reset(); setIsRejected(true); }; + const handleFileAccepted = () => { + setIsRejected(false); + }; + const handleFileReadStarted = (_event: DropEvent, _fileHandle: File) => { setIsLoading(true); }; @@ -69,11 +78,14 @@ export const TextFileUploadWithRestrictions: React.FunctionComponent = () => { maxSize: 1024, onDropRejected: (rejections) => { const error = rejections[0].errors[0]; - if (error.code === DropzoneErrorCode.FileInvalidType) { - setMessage('File must be a valid CSV file'); + if (error.code === DropzoneErrorCode.FileTooLarge) { + setMessage('File is too big'); + } else if (error.code === DropzoneErrorCode.FileInvalidType) { + setMessage('File is not a CSV file'); } handleFileRejected(); - } + }, + onDropAccepted: handleFileAccepted }} validated={isRejected ? 'error' : 'default'} browseButtonText="Upload" @@ -84,7 +96,7 @@ export const TextFileUploadWithRestrictions: React.FunctionComponent = () => { {isRejected ? ( <> - {/* */} + {message} ) : (