Skip to content

Commit

Permalink
feat: support paste upload file
Browse files Browse the repository at this point in the history
  • Loading branch information
madocto committed Jan 19, 2024
1 parent 6027d4c commit acde511
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 15 deletions.
41 changes: 26 additions & 15 deletions src/AjaxUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,31 +66,41 @@ class AjaxUploader extends Component<UploadProps> {
}
};

onFileDrop = (e: React.DragEvent<HTMLDivElement>) => {
const { multiple } = this.props;

onFileDropOrPaste = (
e: React.DragEvent<HTMLDivElement> | React.ClipboardEvent<HTMLDivElement>,
) => {
e.preventDefault();

if (e.type === 'dragover') {
return;
}

if (this.props.directory) {
traverseFileTree(
Array.prototype.slice.call(e.dataTransfer.items),
this.uploadFiles,
(_file: RcFile) => attrAccept(_file, this.props.accept),
const { multiple, accept, directory } = this.props;
let items: DataTransferItem[] = [];
let files: File[] = [];

if (e.type === 'drop') {
const dataTransfer = (e as React.DragEvent<HTMLDivElement>).dataTransfer;
items = [...(dataTransfer.items || [])];
files = [...(dataTransfer.files || [])];
} else if (e.type === 'paste') {
const clipboardData = (e as React.ClipboardEvent<HTMLDivElement>).clipboardData;
items = [...(clipboardData.items || [])];
files = [...(clipboardData.files || [])];
}

if (directory) {
traverseFileTree(Array.prototype.slice.call(items), this.uploadFiles, (_file: RcFile) =>
attrAccept(_file, accept),
);
} else {
let files = [...e.dataTransfer.files].filter((file: RcFile) =>
attrAccept(file, this.props.accept),
);
let acceptFiles = [...files].filter((file: RcFile) => attrAccept(file, accept));

if (multiple === false) {
files = files.slice(0, 1);
acceptFiles = files.slice(0, 1);
}

this.uploadFiles(files);
this.uploadFiles(acceptFiles);
}
};

Expand Down Expand Up @@ -298,8 +308,9 @@ class AjaxUploader extends Component<UploadProps> {
onKeyDown: openFileDialogOnClick ? this.onKeyDown : () => {},
onMouseEnter,
onMouseLeave,
onDrop: this.onFileDrop,
onDragOver: this.onFileDrop,
onDrop: this.onFileDropOrPaste,
onDragOver: this.onFileDropOrPaste,
onPaste: this.onFileDropOrPaste,
tabIndex: hasControlInside ? undefined : '0',
};
return (
Expand Down
96 changes: 96 additions & 0 deletions tests/uploader.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,102 @@ describe('uploader', () => {
}, 100);
});

it('paste to upload', done => {
const input = uploader.container.querySelector('input')!;

const files = [
{
name: 'success.png',
toString() {
return this.name;
},
},
];
(files as any).item = (i: number) => files[i];

handlers.onSuccess = (ret, file) => {
expect(ret[1]).toEqual(file.name);
expect(file).toHaveProperty('uid');
done();
};

handlers.onError = err => {
done(err);
};

fireEvent.paste(input, {
clipboardData: { files },
});

setTimeout(() => {
requests[0].respond(200, {}, `["","${files[0].name}"]`);
}, 100);
});

it('paste unaccepted type files to upload will not trigger onStart', done => {
const input = uploader.container.querySelector('input')!;
const files = [
{
name: 'success.jpg',
toString() {
return this.name;
},
},
];
(files as any).item = (i: number) => files[i];

fireEvent.paste(input, {
clipboardData: { files },
});
const mockStart = jest.fn();
handlers.onStart = mockStart;
setTimeout(() => {
expect(mockStart.mock.calls.length).toBe(0);
done();
}, 100);
});

it('paste files with multiple false', done => {
const { container } = render(<Upload {...props} multiple={false} />);
const input = container.querySelector('input')!;
const files = [
new File([''], 'success.png', { type: 'image/png' }),
new File([''], 'filtered.png', { type: 'image/png' }),
];
Object.defineProperty(files, 'item', {
value: i => files[i],
});

// Only can trigger once
let triggerTimes = 0;
handlers.onStart = () => {
triggerTimes += 1;
};
handlers.onSuccess = (ret, file) => {
try {
expect(ret[1]).toEqual(file.name);
expect(file).toHaveProperty('uid');
expect(triggerTimes).toEqual(1);
done();
} catch (error) {
done(error);
}
};
handlers.onError = error => {
done(error);
};

Object.defineProperty(input, 'files', {
value: files,
});

fireEvent.paste(input, { clipboardData: { files } });

setTimeout(() => {
handlers.onSuccess!(['', files[0].name] as any, files[0] as any, null!);
}, 100);
});

it('support action and data is function returns Promise', async () => {
const action: any = () => {
return new Promise(resolve => {
Expand Down

0 comments on commit acde511

Please sign in to comment.