From c1d1a249a8175b35532358b892f4e9cfdc726f2f Mon Sep 17 00:00:00 2001 From: Elizabeth Letourneau Date: Tue, 18 Dec 2018 17:06:06 -0500 Subject: [PATCH] reset inputHTMLElement to allow duplicate files --- UNRELEASED.md | 2 + src/components/DropZone/DropZone.tsx | 1 + .../DropZone/tests/DropZone.test.tsx | 177 ++++++++++-------- 3 files changed, 104 insertions(+), 76 deletions(-) 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..fddd52c8387 100755 --- a/src/components/DropZone/tests/DropZone.test.tsx +++ b/src/components/DropZone/tests/DropZone.test.tsx @@ -5,87 +5,73 @@ 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', + }, +]; +const acceptedFiles = [files[0]]; +const rejectedFiles = [files[1]]; +const origGetBoundingClientRect = Element.prototype.getBoundingClientRect; +const widths = { + small: 99, + medium: 159, + large: 299, + extraLarge: 1024, +}; + describe('', () => { let spy: jest.Mock; - let files: {}[]; - let acceptedFiles: {}[]; - let rejectedFiles: {}[]; - let createEvent: any; - let setBoundingClientRect: any; - let origGetBoundingClientRect: any; - const widths = { - small: 99, - medium: 159, - large: 299, - extraLarge: 1024, - }; - - const fireEvent = (eventType: string, element: any) => { - spy.mockReset(); - const event = createEvent(eventType); - element.getDOMNode().dispatchEvent(event); - }; - - const triggerDragEnter = (element: ReactWrapper) => { - const event = createEvent('dragenter'); - element.getDOMNode().dispatchEvent(event); - clock.tick(50); - element.update(); - }; 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(() => { - return { - width: widths[size], - height: 100, - top: 0, - left: 0, - bottom: 0, - right: 0, - }; - }); - }; }); afterEach(() => { clock.restore(); + }); + + afterAll(() => { Element.prototype.getBoundingClientRect = origGetBoundingClientRect; }); 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 +80,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 +89,7 @@ describe('', () => { const dropZone = mountWithAppProvider( , ); - const event = createEvent('drop'); + const event = createEvent('drop', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalledWith(acceptedFiles); }); @@ -112,28 +98,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 +131,7 @@ describe('', () => { const dropZone = mountWithAppProvider( , ); - const event = createEvent('drop'); + const event = createEvent('drop', files); dropZone.getDOMNode().dispatchEvent(event); expect(spy).toBeCalledWith(files, acceptedFiles, rejectedFiles); }); @@ -162,13 +148,13 @@ describe('', () => { onDragOver={spy} />, ); - fireEvent('drop', dropZone); + fireEvent('drop', dropZone, spy); expect(spy).not.toBeCalled(); - fireEvent('dragenter', dropZone); + fireEvent('dragenter', dropZone, spy); expect(spy).not.toBeCalled(); - fireEvent('dragleave', dropZone); + fireEvent('dragleave', dropZone, spy); expect(spy).not.toBeCalled(); - fireEvent('dragover', dropZone); + fireEvent('dragover', dropZone, spy); expect(spy).not.toBeCalled(); }); @@ -185,17 +171,17 @@ describe('', () => { ); // Initial event to populate zone with data (should succeed) - fireEvent('drop', dropZone); + fireEvent('drop', dropZone, spy); expect(spy).toBeCalledWith(files, acceptedFiles, rejectedFiles); // All events should now be ignored - fireEvent('drop', dropZone); + fireEvent('drop', dropZone, spy); expect(spy).not.toBeCalled(); - fireEvent('dragenter', dropZone); + fireEvent('dragenter', dropZone, spy); expect(spy).not.toBeCalled(); - fireEvent('dragleave', dropZone); + fireEvent('dragleave', dropZone, spy); expect(spy).not.toBeCalled(); - fireEvent('dragover', dropZone); + fireEvent('dragover', dropZone, spy); expect(spy).not.toBeCalled(); }); @@ -334,3 +320,42 @@ describe('', () => { }); }); }); + +function createEvent(name: string, files: any) { + const evt = new CustomEvent(name); + Object.defineProperty(evt, 'dataTransfer', { + enumerable: true, + value: {files}, + }); + return evt; +} + +function setBoundingClientRect(size: keyof typeof widths) { + Element.prototype.getBoundingClientRect = jest.fn(() => { + return { + width: widths[size], + height: 100, + top: 0, + left: 0, + bottom: 0, + right: 0, + }; + }); +} + +function triggerDragEnter(element: ReactWrapper) { + const event = createEvent('dragenter', files); + element.getDOMNode().dispatchEvent(event); + clock.tick(50); + element.update(); +} + +function fireEvent( + eventType: string, + element: ReactWrapper, + spy: jest.Mock, +) { + spy.mockReset(); + const event = createEvent(eventType, files); + element.getDOMNode().dispatchEvent(event); +}