diff --git a/packages/react-core/src/components/MultipleFileUpload/MultipleFileUploadStatusItem.tsx b/packages/react-core/src/components/MultipleFileUpload/MultipleFileUploadStatusItem.tsx index 24f437facf9..1794664c698 100644 --- a/packages/react-core/src/components/MultipleFileUpload/MultipleFileUploadStatusItem.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/MultipleFileUploadStatusItem.tsx @@ -54,6 +54,8 @@ export interface MultipleFileUploadStatusItemProps extends React.HTMLProps string); /** Unique identifier for progress. Generated if not specified. */ progressId?: string; + /** @beta Additional content related to the status item. */ + progressHelperText?: React.ReactNode; } export const MultipleFileUploadStatusItem: React.FunctionComponent = ({ @@ -75,6 +77,7 @@ export const MultipleFileUploadStatusItem: React.FunctionComponent { const [loadPercentage, setLoadPercentage] = React.useState(0); @@ -129,6 +132,9 @@ export const MultipleFileUploadStatusItem: React.FunctionComponent {fileName || file?.name || ''} @@ -151,11 +157,12 @@ export const MultipleFileUploadStatusItem: React.FunctionComponent
diff --git a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx index 2b7b0a947c6..7f59631f14c 100644 --- a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx @@ -99,3 +99,38 @@ describe('MultipleFileUploadStatusItem', () => { expect(asFragment()).toMatchSnapshot(); }); }); + +test('does not render helper text by default', () => { + const testFile = new File(['foo'], 'testFile.txt'); + render( + + ); + + const helperText = screen.queryByText('Test helper text'); + + expect(helperText).not.toBeInTheDocument(); +}); + +test('renders helper text', () => { + const testFile = new File(['foo'], 'testFile.txt'); + render( + + ); + + const helperText = screen.getByText('Test helper text'); + + expect(helperText).toBeVisible(); +}); diff --git a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUpload.md b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUpload.md index c6c9474aa45..2f1c7116706 100644 --- a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUpload.md +++ b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUpload.md @@ -42,5 +42,11 @@ File upload - multiple is designed in a composable manner to make customization ### Basic +The below example demonstrates a typical application of file upload - multiple, with a few tweaks from that typical application to enhance the convenience of the example. + +The "Show as horizontal" checkbox can be used to easily toggle the `isHorizontal` prop, showing our available styling variations. + +The "Demonstrate error reporting by forcing uploads to fail" checkbox shows how our `progressHelperText` prop can be used to provide status messages to users, such as when a file fails to upload. While this checkbox is checked it will cause any file uploaded to automatically fail the file reading process, and helper text will be dynamically rendered which informs the user of that error. + ```ts file="./MultipleFileUploadBasic.tsx" ``` diff --git a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx index f18a8ace5c9..7687f60b0ad 100644 --- a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx @@ -4,7 +4,9 @@ import { MultipleFileUploadMain, MultipleFileUploadStatus, MultipleFileUploadStatusItem, - Checkbox + Checkbox, + HelperText, + HelperTextItem } from '@patternfly/react-core'; import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon'; @@ -17,6 +19,7 @@ interface readFile { export const MultipleFileUploadBasic: React.FunctionComponent = () => { const [isHorizontal, setIsHorizontal] = React.useState(false); + const [fileUploadShouldFail, setFileUploadShouldFail] = React.useState(false); const [currentFiles, setCurrentFiles] = React.useState([]); const [readFileData, setReadFileData] = React.useState([]); const [showStatus, setShowStatus] = React.useState(false); @@ -53,6 +56,17 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { setReadFileData(newReadFiles); }; + /** Forces uploaded files to become corrupted if "Demonstrate error reporting by forcing uploads to fail" is selected in the example, + * only used in this example for demonstration purposes */ + const updateCurrentFiles = (files: File[]) => { + if (fileUploadShouldFail) { + const corruptedFiles = files.map(file => ({ ...file, lastModified: ('foo' as unknown) as number })); + setCurrentFiles(prevFiles => [...prevFiles, ...corruptedFiles]); + } else { + setCurrentFiles(prevFiles => [...prevFiles, ...files]); + } + }; + // callback that will be called by the react dropzone with the newly dropped file objects const handleFileDrop = (droppedFiles: File[]) => { // identify what, if any, files are re-uploads of already uploaded files @@ -63,7 +77,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { * won't realize that the status items for the re-uploaded files needs to be re-rendered */ Promise.resolve() .then(() => removeFiles(reUploads.map(file => file.name))) - .then(() => setCurrentFiles(prevFiles => [...prevFiles, ...droppedFiles])); + .then(() => updateCurrentFiles(droppedFiles)); }; // callback called by the status item when a file is successfully read with the built-in file reader @@ -79,6 +93,18 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { ]); }; + // add helper text to a status item showing any error encountered during the file reading process + const createHelperText = (file: File) => { + const fileResult = readFileData.find(readFile => readFile.fileName === file.name); + if (fileResult?.loadError) { + return ( + + {fileResult.loadError.toString()} + + ); + } + }; + const successfullyReadFileCount = readFileData.filter(fileData => fileData.loadResult === 'success').length; return ( @@ -108,6 +134,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { onClearClick={() => removeFiles([file.name])} onReadSuccess={handleReadSuccess} onReadFail={handleReadFail} + progressHelperText={createHelperText(file)} /> ))} @@ -119,6 +146,12 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { isChecked={isHorizontal} onChange={() => setIsHorizontal(!isHorizontal)} /> + setFileUploadShouldFail(!fileUploadShouldFail)} + /> ); };