Update dependency react-dropzone to v14 (#49243)
* Update dependency react-dropzone to v14 * Changes needed for react-dropzone v13 * feat(FileDropzone): update props to be backward compatible * refactor(filedropzone): clean up component story Co-authored-by: Renovate Bot <bot@renovateapp.com> Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com> Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
This commit is contained in:
co-authored by
Renovate Bot
Ashley Harrison
Levente Balogh
Jack Westbrook
parent
62531715b3
commit
a0ffb9093c
@@ -73,7 +73,7 @@
|
||||
"react-colorful": "5.5.1",
|
||||
"react-custom-scrollbars-2": "4.5.0",
|
||||
"react-dom": "17.0.2",
|
||||
"react-dropzone": "12.0.4",
|
||||
"react-dropzone": "14.2.1",
|
||||
"react-highlight-words": "0.18.0",
|
||||
"react-hook-form": "7.5.3",
|
||||
"react-inlinesvg": "3.0.0",
|
||||
|
||||
@@ -16,15 +16,18 @@ export default {
|
||||
page: mdx,
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
onLoad: { action: 'onLoad' },
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
export const Basic: Story<FileDropzoneProps> = (args) => {
|
||||
return <FileDropzone {...args} />;
|
||||
const Template: Story<FileDropzoneProps> = (args) => <FileDropzone {...args} />;
|
||||
|
||||
export const Basic = Template.bind({});
|
||||
|
||||
export const WithCustomFileList = Template.bind({});
|
||||
WithCustomFileList.args = {
|
||||
fileListRenderer: (file) => <div>Custom rendered item {file.file.name}</div>,
|
||||
};
|
||||
|
||||
export const WithCustomFileList: Story<FileDropzoneProps> = () => {
|
||||
return <FileDropzone fileListRenderer={(file) => <div>Custom rendered item {file.file.name}</div>} />;
|
||||
export const OnlyAcceptingCertainFiles = Template.bind({});
|
||||
OnlyAcceptingCertainFiles.args = {
|
||||
options: { accept: { 'application/json': ['.json'] } },
|
||||
};
|
||||
|
||||
@@ -27,18 +27,24 @@ describe('The FileDropzone component', () => {
|
||||
expect(screen.getByText('Upload file')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show accepted file type when passed in the options as a string', () => {
|
||||
it('should show the accepted file type(s) when passed in as a string', () => {
|
||||
render(<FileDropzone options={{ accept: '.json' }} />);
|
||||
|
||||
expect(screen.getByText('Accepted file type: .json')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show accepted file types when passed in the options as a string array', () => {
|
||||
it('should show the accepted file type(s) when passed in as a array of strings', () => {
|
||||
render(<FileDropzone options={{ accept: ['.json', '.txt'] }} />);
|
||||
|
||||
expect(screen.getByText('Accepted file types: .json, .txt')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show the accepted file type(s) when passed in as an `Accept` object', () => {
|
||||
render(<FileDropzone options={{ accept: { 'text/*': ['.json', '.txt'] } }} />);
|
||||
|
||||
expect(screen.getByText('Accepted file types: .json, .txt')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should handle file removal from the list', async () => {
|
||||
render(<FileDropzone />);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { uniqueId } from 'lodash';
|
||||
import { uniqueId, isString } from 'lodash';
|
||||
import React, { ReactNode, useCallback, useState } from 'react';
|
||||
import { DropEvent, DropzoneOptions, FileRejection, useDropzone } from 'react-dropzone';
|
||||
import { DropEvent, DropzoneOptions, FileRejection, useDropzone, Accept } from 'react-dropzone';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
|
||||
@@ -10,6 +10,11 @@ import { Icon } from '../Icon/Icon';
|
||||
|
||||
import { FileListItem } from './FileListItem';
|
||||
|
||||
type BackwardsCompatibleDropzoneOptions = Omit<DropzoneOptions, 'accept'> & {
|
||||
// For backward compatibility we are still allowing the old `string | string[]` format for adding accepted file types (format changed in v13.0.0)
|
||||
accept?: string | string[] | Accept;
|
||||
};
|
||||
|
||||
export interface FileDropzoneProps {
|
||||
/**
|
||||
* Use the children property to have custom dropzone view.
|
||||
@@ -25,7 +30,7 @@ export interface FileDropzoneProps {
|
||||
* maxFiles: 0,
|
||||
* }
|
||||
*/
|
||||
options?: DropzoneOptions;
|
||||
options?: BackwardsCompatibleDropzoneOptions;
|
||||
/**
|
||||
* Use this to change the FileReader's read.
|
||||
*/
|
||||
@@ -145,6 +150,7 @@ export function FileDropzone({ options, children, readAs, onLoad, fileListRender
|
||||
...options,
|
||||
useFsAccessApi: false,
|
||||
onDrop,
|
||||
accept: transformAcceptToNewFormat(options?.accept),
|
||||
});
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme, isDragActive);
|
||||
@@ -162,13 +168,41 @@ export function FileDropzone({ options, children, readAs, onLoad, fileListRender
|
||||
{children ?? <FileDropzoneDefaultChildren primaryText={getPrimaryText(files, options)} />}
|
||||
</div>
|
||||
{options?.accept && (
|
||||
<small className={cx(styles.small, styles.acceptMargin)}>{getAcceptedFileTypeText(options)}</small>
|
||||
<small className={cx(styles.small, styles.acceptMargin)}>{getAcceptedFileTypeText(options.accept)}</small>
|
||||
)}
|
||||
{fileList}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function getMimeTypeByExtension(ext: string) {
|
||||
if (['txt', 'json', 'csv', 'xls', 'yml'].some((e) => ext.match(e))) {
|
||||
return 'text/plain';
|
||||
}
|
||||
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
||||
export function transformAcceptToNewFormat(accept?: string | string[] | Accept): Accept | undefined {
|
||||
if (isString(accept)) {
|
||||
return {
|
||||
[getMimeTypeByExtension(accept)]: [accept],
|
||||
};
|
||||
}
|
||||
|
||||
if (Array.isArray(accept)) {
|
||||
return accept.reduce((prev: Record<string, string[]>, current) => {
|
||||
const mime = getMimeTypeByExtension(current);
|
||||
|
||||
prev[mime] = prev[mime] ? [...prev[mime], current] : [current];
|
||||
|
||||
return prev;
|
||||
}, {});
|
||||
}
|
||||
|
||||
return accept;
|
||||
}
|
||||
|
||||
export function FileDropzoneDefaultChildren({
|
||||
primaryText = 'Upload file',
|
||||
secondaryText = 'Drag and drop here or browse',
|
||||
@@ -184,19 +218,25 @@ export function FileDropzoneDefaultChildren({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function getPrimaryText(files: DropzoneFile[], options?: DropzoneOptions) {
|
||||
function getPrimaryText(files: DropzoneFile[], options?: BackwardsCompatibleDropzoneOptions) {
|
||||
if (options?.multiple === undefined || options?.multiple) {
|
||||
return 'Upload file';
|
||||
}
|
||||
return files.length ? 'Replace file' : 'Upload file';
|
||||
}
|
||||
|
||||
function getAcceptedFileTypeText(options: DropzoneOptions) {
|
||||
if (Array.isArray(options.accept)) {
|
||||
return `Accepted file types: ${options.accept.join(', ')}`;
|
||||
function getAcceptedFileTypeText(accept: string | string[] | Accept) {
|
||||
if (isString(accept)) {
|
||||
return `Accepted file type: ${accept}`;
|
||||
}
|
||||
|
||||
return `Accepted file type: ${options.accept}`;
|
||||
if (Array.isArray(accept)) {
|
||||
return `Accepted file types: ${accept.join(', ')}`;
|
||||
}
|
||||
|
||||
// react-dropzone has updated the type of the "accept" parameter since v13.0.0:
|
||||
// https://github.com/react-dropzone/react-dropzone/blob/master/src/index.js#L95
|
||||
return `Accepted file types: ${Object.values(accept).flat().join(', ')}`;
|
||||
}
|
||||
|
||||
function mapToCustomFile(file: File): DropzoneFile {
|
||||
|
||||
Reference in New Issue
Block a user