diff --git a/js/DropZone/DropZoneComponent.js b/js/DropZone/DropZoneComponent.js index faf19ab36f..7ffa915eb1 100644 --- a/js/DropZone/DropZoneComponent.js +++ b/js/DropZone/DropZoneComponent.js @@ -138,6 +138,20 @@ class DropZoneComponent { input.value = ''; input.type = ''; input.type = 'file'; + + let rawFiles = this.instanceManager.getFiles(id); + const dataTransfer = new DataTransfer(); + let wasFile = false; + for (let i = 0; i < rawFiles.length; i++) { + if (rawFiles[i].raw instanceof File){ + dataTransfer.items.add(rawFiles[i].raw); + wasFile = true; + } + } + if (wasFile){ + // this guard exists as some js tests do not provide a file type as the input value. + input.files = dataTransfer.files; + } }); // visually hide input - this should ideally be done in the CSS also to prevent a diff --git a/tests/js/web/DropZone/DropZoneComponentTest.js b/tests/js/web/DropZone/DropZoneComponentTest.js index 790f127520..779799c0f9 100644 --- a/tests/js/web/DropZone/DropZoneComponentTest.js +++ b/tests/js/web/DropZone/DropZoneComponentTest.js @@ -148,6 +148,49 @@ describe('DropZoneComponent', () => { expect($fileInput.val()).to.equal(''); }); + it('should retain the input node files on change', () => { + // we need to explicitly set type="file" here. Originally it is configured as type="text" presumably to avoid the same security issue explained below + $fileInput = $(''); + const change = new Event('change'); + const getDTFileList = (fileInput, ...appendFiles) => { + const dataTransfer = new DataTransfer(); + if (fileInput.files && fileInput.files.length > 0) { + // add all our File objects to prepopulate the DataTransfer + for (let i = 0; i < fileInput.files.length; i++) { + dataTransfer.items.add(fileInput.files[i]); + } + } + appendFiles.forEach((appendFile)=>{ + dataTransfer.items.add(appendFile); + }); + return dataTransfer.files; + } + /** + * There is actually no way to programatically add a File object to Input.files other than to duplicate the DataTransfer process... + * This is due to strict browser security measures. Yes it is a bit of a hack, but there are simply no other options for initalising our input. + * Feel free to refactor this should a future version of ES add this functionality in some way. + */ + const file = new File(['Lorem ipsum dolor'], 'example.txt', { type: 'text/plain' }); + $fileInput[0].files = getDTFileList($fileInput[0], file); + + let rawFiles = []; + for (let i = 0; i < $fileInput[0].files.length; i++) { + $fileInput[0].files[i] + rawFiles.push({'raw': $fileInput[0].files[i]}) + } + + instanceManager.getFiles.returns(rawFiles); + dropZoneComponent.processInputNode($fileInput[0], 0, options.showInputNode); + $fileInput[0].dispatchEvent(change); + //debugger; + const dataTransferFile = getDTFileList($fileInput[0])[0]; + const inputFile = $fileInput[0].files[0]; + expect(inputFile.name).to.equal(dataTransferFile.name); + expect(inputFile.lastModified).to.equal(dataTransferFile.lastModified); + expect(inputFile.size).to.equal(dataTransferFile.size); + expect(inputFile.type).to.equal(dataTransferFile.type); + }); + it('should not hide the input if specified in options', () => { const options = { showInputNode: true, inputNodeId: 'fileInput' }; const display = $fileInput.css('display');