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 @@ -54,6 +54,8 @@ export interface MultipleFileUploadStatusItemProps extends React.HTMLProps<HTMLL
progressAriaLiveMessage?: string | ((loadPercentage: number) => 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<MultipleFileUploadStatusItemProps> = ({
Expand All @@ -75,6 +77,7 @@ export const MultipleFileUploadStatusItem: React.FunctionComponent<MultipleFileU
progressId,
progressAriaLiveMessage,
buttonAriaLabel = 'Remove from list',
progressHelperText,
...props
}: MultipleFileUploadStatusItemProps) => {
const [loadPercentage, setLoadPercentage] = React.useState(0);
Expand Down Expand Up @@ -129,6 +132,9 @@ export const MultipleFileUploadStatusItem: React.FunctionComponent<MultipleFileU
return `${Math.round(size)}${prefixes[prefixUnit]}B`;
};

const value = progressValue || loadPercentage;
const variant = progressVariant || loadResult;

const title = (
<span className={styles.multipleFileUploadStatusItemProgress}>
<span className={styles.multipleFileUploadStatusItemProgressText}>{fileName || file?.name || ''}</span>
Expand All @@ -151,11 +157,12 @@ export const MultipleFileUploadStatusItem: React.FunctionComponent<MultipleFileU
</div>
<Progress
title={title}
value={progressValue || loadPercentage}
variant={progressVariant || loadResult}
value={value}
variant={variant}
aria-label={progressAriaLabel}
aria-labelledby={progressAriaLabelledBy}
id={progressId}
helperText={progressHelperText}
/>
</div>
<div className={styles.multipleFileUploadStatusItemClose}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<MultipleFileUploadStatusItem
file={testFile}
buttonAriaLabel="buttonAriaLabel"
progressAriaLabel="progressAriaLabel"
progressAriaLabelledBy="progressAriaLabelledBy"
progressId="test-progress-id"
/>
);

const helperText = screen.queryByText('Test helper text');

expect(helperText).not.toBeInTheDocument();
});

test('renders helper text', () => {
const testFile = new File(['foo'], 'testFile.txt');
render(
<MultipleFileUploadStatusItem
file={testFile}
buttonAriaLabel="buttonAriaLabel"
progressAriaLabel="progressAriaLabel"
progressAriaLabelledBy="progressAriaLabelledBy"
progressId="test-progress-id"
progressHelperText="Test helper text"
/>
);

const helperText = screen.getByText('Test helper text');

expect(helperText).toBeVisible();
});
Original file line number Diff line number Diff line change
Expand Up @@ -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"
```
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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<File[]>([]);
const [readFileData, setReadFileData] = React.useState<readFile[]>([]);
const [showStatus, setShowStatus] = React.useState(false);
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 (
<HelperText isLiveRegion>
<HelperTextItem variant={'error'}>{fileResult.loadError.toString()}</HelperTextItem>
</HelperText>
);
}
};

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

return (
Expand Down Expand Up @@ -108,6 +134,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
onClearClick={() => removeFiles([file.name])}
onReadSuccess={handleReadSuccess}
onReadFail={handleReadFail}
progressHelperText={createHelperText(file)}
/>
))}
</MultipleFileUploadStatus>
Expand All @@ -119,6 +146,12 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
isChecked={isHorizontal}
onChange={() => setIsHorizontal(!isHorizontal)}
/>
<Checkbox
id="upload-should-fail-checkbox"
label="Demonstrate error reporting by forcing uploads to fail"
isChecked={fileUploadShouldFail}
onChange={() => setFileUploadShouldFail(!fileUploadShouldFail)}
/>
</>
);
};