Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface MultipleFileUploadProps extends Omit<React.HTMLProps<HTMLDivEle
/** Flag setting the component to horizontal styling mode */
isHorizontal?: boolean;
/** When files are dropped or uploaded this callback will be called with all accepted files */
onFileDrop?: (data: File[]) => void;
onFileDrop?: (event: DropEvent, data: File[]) => void;
}

export const MultipleFileUploadContext = React.createContext({
Expand All @@ -34,7 +34,7 @@ export const MultipleFileUpload: React.FunctionComponent<MultipleFileUploadProps
...props
}: MultipleFileUploadProps) => {
const onDropAccepted = (acceptedFiles: File[], event: DropEvent) => {
onFileDrop(acceptedFiles);
onFileDrop(event, acceptedFiles);
// allow users to set a custom drop accepted handler rather than using on data change
dropzoneProps.onDropAccepted && dropzoneProps.onDropAccepted(acceptedFiles, event);
};
Expand All @@ -47,7 +47,7 @@ export const MultipleFileUpload: React.FunctionComponent<MultipleFileUploadProps

const rootProps = getRootProps({
...props,
onClick: event => event.preventDefault() // Prevents clicking TextArea from opening file dialog
onClick: (event) => event.preventDefault() // Prevents clicking TextArea from opening file dialog
});

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,18 @@ The "Show as horizontal" checkbox can be used to easily toggle the `isHorizontal
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"

```

## Types

Multiple file upload uses both the `DropEvent` and `DropzoneOptions` types from react-dropzone. The `DropEvent` type is a union comprised of the following types:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good! Thanks for adding that.


```noLive
React.DragEvent<HTMLElement>
| React.ChangeEvent<HTMLInputElement>
| DragEvent
| Event
```

`DropzoneOptions` is comprised of additional props with their own types. For more information on using `DropzoneOptions` visit [react-dropzone props and methods](https://react-dropzone.js.org/#src).
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
HelperText,
HelperTextItem
} from '@patternfly/react-core';
import { DropEvent } from 'react-dropzone';
import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon';

interface readFile {
Expand Down Expand Up @@ -68,7 +69,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
};

// callback that will be called by the react dropzone with the newly dropped file objects
const handleFileDrop = (droppedFiles: File[]) => {
const handleFileDrop = (_event: DropEvent, droppedFiles: File[]) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker for m since this is a limitation of the documentation framework. DropEvent is not documented so the consumer doe not know what that is. I also notice DropzonOptions is not documented either.
Maybe we can add descriptions of both here until the doc framework is updated.

// identify what, if any, files are re-uploads of already uploaded files
const currentFileNames = currentFiles.map((file) => file.name);
const reUploads = droppedFiles.filter((droppedFile) => currentFileNames.includes(droppedFile.name));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Modal,
Checkbox
} from '@patternfly/react-core';
import { DropEvent } from 'react-dropzone';
import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon';

interface readFile {
Expand All @@ -34,7 +35,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
React.useEffect(() => {
if (readFileData.length < currentFiles.length) {
setStatusIcon('inProgress');
} else if (readFileData.every(file => file.loadResult === 'success')) {
} else if (readFileData.every((file) => file.loadResult === 'success')) {
setStatusIcon('success');
} else {
setStatusIcon('danger');
Expand All @@ -44,39 +45,39 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
// remove files from both state arrays based on their name
const removeFiles = (namesOfFilesToRemove: string[]) => {
const newCurrentFiles = currentFiles.filter(
currentFile => !namesOfFilesToRemove.some(fileName => fileName === currentFile.name)
(currentFile) => !namesOfFilesToRemove.some((fileName) => fileName === currentFile.name)
);

setCurrentFiles(newCurrentFiles);

const newReadFiles = readFileData.filter(
readFile => !namesOfFilesToRemove.some(fileName => fileName === readFile.fileName)
(readFile) => !namesOfFilesToRemove.some((fileName) => fileName === readFile.fileName)
);

setReadFileData(newReadFiles);
};

// callback that will be called by the react dropzone with the newly dropped file objects
const handleFileDrop = (droppedFiles: File[]) => {
const handleFileDrop = (_event: DropEvent, droppedFiles: File[]) => {
// identify what, if any, files are re-uploads of already uploaded files
const currentFileNames = currentFiles.map(file => file.name);
const reUploads = droppedFiles.filter(droppedFile => currentFileNames.includes(droppedFile.name));
const currentFileNames = currentFiles.map((file) => file.name);
const reUploads = droppedFiles.filter((droppedFile) => currentFileNames.includes(droppedFile.name));

/** this promise chain is needed because if the file removal is done at the same time as the file adding react
* 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(() => removeFiles(reUploads.map((file) => file.name)))
.then(() => setCurrentFiles((prevFiles) => [...prevFiles, ...droppedFiles]));
};

// callback called by the status item when a file is successfully read with the built-in file reader
const handleReadSuccess = (data: string, file: File) => {
setReadFileData(prevReadFiles => [...prevReadFiles, { data, fileName: file.name, loadResult: 'success' }]);
setReadFileData((prevReadFiles) => [...prevReadFiles, { data, fileName: file.name, loadResult: 'success' }]);
};

// callback called by the status item when a file encounters an error while being read with the built-in file reader
const handleReadFail = (error: DOMException, file: File) => {
setReadFileData(prevReadFiles => [
setReadFileData((prevReadFiles) => [
...prevReadFiles,
{ loadError: error, fileName: file.name, loadResult: 'danger' }
]);
Expand All @@ -95,7 +96,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
}
};

const successfullyReadFileCount = readFileData.filter(fileData => fileData.loadResult === 'success').length;
const successfullyReadFileCount = readFileData.filter((fileData) => fileData.loadResult === 'success').length;

return (
<>
Expand Down Expand Up @@ -123,7 +124,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
statusToggleText={`${successfullyReadFileCount} of ${currentFiles.length} files uploaded`}
statusToggleIcon={statusIcon}
>
{currentFiles.map(file => (
{currentFiles.map((file) => (
<MultipleFileUploadStatusItem
file={file}
key={file.name}
Expand Down