diff --git a/UNRELEASED.md b/UNRELEASED.md index 5910393f12d..bd2811b0709 100644 --- a/UNRELEASED.md +++ b/UNRELEASED.md @@ -8,6 +8,8 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f ### Bug fixes +- Fixed `DropZone` which was unable to add a duplicate file back to back or add a file again once removed [#782](https://github.com/Shopify/polaris-react/pull/782) + ### Documentation ### Development workflow diff --git a/src/components/DropZone/DropZone.tsx b/src/components/DropZone/DropZone.tsx index f2b039df349..342de6044c8 100755 --- a/src/components/DropZone/DropZone.tsx +++ b/src/components/DropZone/DropZone.tsx @@ -477,6 +477,7 @@ export class DropZone extends React.Component { if (onDropRejected && rejectedFiles.length) { onDropRejected(rejectedFiles); } + (event.target as HTMLInputElement).value = ''; } @autobind diff --git a/src/components/DropZone/tests/DropZone.test.tsx b/src/components/DropZone/tests/DropZone.test.tsx index f16c716776b..feee98e2ffd 100755 --- a/src/components/DropZone/tests/DropZone.test.tsx +++ b/src/components/DropZone/tests/DropZone.test.tsx @@ -5,12 +5,40 @@ import {Label, Labelled, DisplayText, Caption} from 'components'; import {mountWithAppProvider} from 'test-utilities'; import DropZone from '../DropZone'; +const files = [ + { + name: 'jpeg file', + type: 'image/jpeg', + }, + { + name: 'svg file', + type: 'image/svg', + }, +]; +const duplicateFiles = [ + { + name: 'jpegs files', + type: 'image/jpeg', + }, + { + name: 'svg file', + type: 'image/svg', + }, +]; + +function createEvent(name: string, files: any) { + const evt = new CustomEvent(name); + Object.defineProperty(evt, 'dataTransfer', { + enumerable: true, + value: {files}, + }); + return evt; +} + describe('', () => { let spy: jest.Mock; - let files: {}[]; let acceptedFiles: {}[]; let rejectedFiles: {}[]; - let createEvent: any; let setBoundingClientRect: any; let origGetBoundingClientRect: any; const widths = { @@ -22,12 +50,12 @@ describe('', () => { const fireEvent = (eventType: string, element: any) => { spy.mockReset(); - const event = createEvent(eventType); + const event = createEvent(eventType, files); element.getDOMNode().dispatchEvent(event); }; const triggerDragEnter = (element: ReactWrapper) => { - const event = createEvent('dragenter'); + const event = createEvent('dragenter', files); element.getDOMNode().dispatchEvent(event); clock.tick(50); element.update(); @@ -36,26 +64,10 @@ describe('', () => { beforeEach(() => { spy = jest.fn(); clock.mock(); - files = [ - { - name: 'jpeg file', - type: 'image/jpeg', - }, - { - name: 'svg file', - type: 'image/svg', - }, - ]; + acceptedFiles = [files[0]]; rejectedFiles = [files[1]]; - createEvent = (name: string) => { - const evt = new CustomEvent(name); - Object.defineProperty(evt, 'dataTransfer', { - enumerable: true, - value: {files}, - }); - return evt; - }; + origGetBoundingClientRect = Element.prototype.getBoundingClientRect; setBoundingClientRect = (size: keyof typeof widths) => { Element.prototype.getBoundingClientRect = jest.fn(() => { @@ -78,14 +90,25 @@ describe('', () => { it('calls the onDrop callback when a drop event is fired', () => { const dropZone = mountWithAppProvider(); - const event = createEvent('drop'); + const event = createEvent('drop', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalledWith(files, files, []); }); + it('calls the onDrop callback when a drop event is fired on document twice when a duplicate file is added consecutively', () => { + const dropZone = mountWithAppProvider(); + const event1 = createEvent('drop', files); + dropZone.getDOMNode().dispatchEvent(event1); + expect(spy).toBeCalledWith(files, files, []); + + const event2 = createEvent('drop', duplicateFiles); + dropZone.getDOMNode().dispatchEvent(event2); + expect(spy).toBeCalledWith(duplicateFiles, duplicateFiles, []); + }); + it('calls the onDrop callback when a drop event is fired on document', () => { mountWithAppProvider(); - const event = createEvent('drop'); + const event = createEvent('drop', files); document.dispatchEvent(event); expect(spy).toBeCalledWith(files, files, []); }); @@ -94,7 +117,7 @@ describe('', () => { const dropZone = mountWithAppProvider( , ); - const event = createEvent('drop'); + const event = createEvent('drop', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalledWith(files, acceptedFiles, rejectedFiles); }); @@ -103,7 +126,7 @@ describe('', () => { const dropZone = mountWithAppProvider( , ); - const event = createEvent('drop'); + const event = createEvent('drop', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalledWith(acceptedFiles); }); @@ -112,28 +135,28 @@ describe('', () => { const dropZone = mountWithAppProvider( , ); - const event = createEvent('drop'); + const event = createEvent('drop', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalledWith(rejectedFiles); }); it('calls the onDragEnter callback when a dragEnter event is fired', () => { const dropZone = mountWithAppProvider(); - const event = createEvent('dragenter'); + const event = createEvent('dragenter', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalled(); }); it('calls the onDragOver callback when a dragOver event is fired', () => { const dropZone = mountWithAppProvider(); - const event = createEvent('dragover'); + const event = createEvent('dragover', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalled(); }); it('calls the onDragLeave callback when a dragLeave event is fired', () => { const dropZone = mountWithAppProvider(); - const event = createEvent('dragleave'); + const event = createEvent('dragleave', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalled(); }); @@ -145,7 +168,7 @@ describe('', () => { const dropZone = mountWithAppProvider( , ); - const event = createEvent('drop'); + const event = createEvent('drop', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalledWith(files, acceptedFiles, rejectedFiles); });