diff --git a/src/AjaxUploader.tsx b/src/AjaxUploader.tsx index e88c291..aaeb4c4 100644 --- a/src/AjaxUploader.tsx +++ b/src/AjaxUploader.tsx @@ -66,31 +66,41 @@ class AjaxUploader extends Component { } }; - onFileDrop = (e: React.DragEvent) => { - const { multiple } = this.props; - + onFileDropOrPaste = ( + e: React.DragEvent | React.ClipboardEvent, + ) => { 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).dataTransfer; + items = [...(dataTransfer.items || [])]; + files = [...(dataTransfer.files || [])]; + } else if (e.type === 'paste') { + const clipboardData = (e as React.ClipboardEvent).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); } }; @@ -298,8 +308,9 @@ class AjaxUploader extends Component { 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 ( diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index b7e060c..6917599 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -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(); + 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 => {