react-dropzone
Advanced tools
Comparing version 5.1.1 to 6.0.0
@@ -20,3 +20,3 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
import PropTypes from 'prop-types'; | ||
import { supportMultiple, fileAccepted, allFilesAccepted, fileMatchSize, onDocumentDragOver, getDataTransferItems as defaultGetDataTransferItem, isIeOrEdge } from './utils'; | ||
import { supportMultiple, fileAccepted, allFilesAccepted, fileMatchSize, onDocumentDragOver, getDataTransferItems as defaultGetDataTransferItem, isIeOrEdge, hasFiles } from './utils'; | ||
import styles from './utils/styles'; | ||
@@ -119,5 +119,9 @@ | ||
value: function onDragStart(evt) { | ||
if (this.props.onDragStart) { | ||
this.props.onDragStart.call(this, evt); | ||
} | ||
var _this2 = this; | ||
Promise.resolve(this.props.getDataTransferItems(evt)).then(function (draggedFiles) { | ||
if (hasFiles(draggedFiles) && _this2.props.onDragStart) { | ||
_this2.props.onDragStart.call(_this2, evt); | ||
} | ||
}); | ||
} | ||
@@ -127,3 +131,3 @@ }, { | ||
value: function onDragEnter(evt) { | ||
var _this2 = this; | ||
var _this3 = this; | ||
@@ -140,10 +144,13 @@ evt.preventDefault(); | ||
Promise.resolve(this.props.getDataTransferItems(evt)).then(function (draggedFiles) { | ||
_this2.setState({ | ||
isDragActive: true, // Do not rely on files for the drag state. It doesn't work in Safari. | ||
draggedFiles: draggedFiles | ||
}); | ||
if (hasFiles(draggedFiles)) { | ||
_this3.setState({ | ||
isDragActive: true, // Do not rely on files for the drag state. It doesn't work in Safari. | ||
draggedFiles: draggedFiles | ||
}); | ||
if (_this3.props.onDragEnter) { | ||
_this3.props.onDragEnter.call(_this3, evt); | ||
} | ||
} | ||
}); | ||
if (this.props.onDragEnter) { | ||
this.props.onDragEnter.call(this, evt); | ||
} | ||
} | ||
@@ -153,2 +160,4 @@ }, { | ||
value: function onDragOver(evt) { | ||
var _this4 = this; | ||
// eslint-disable-line class-methods-use-this | ||
@@ -166,5 +175,8 @@ evt.preventDefault(); | ||
if (this.props.onDragOver) { | ||
this.props.onDragOver.call(this, evt); | ||
} | ||
Promise.resolve(this.props.getDataTransferItems(evt)).then(function (draggedFiles) { | ||
if (hasFiles(draggedFiles) && _this4.props.onDragOver) { | ||
_this4.props.onDragOver.call(_this4, evt); | ||
} | ||
}); | ||
return false; | ||
@@ -175,3 +187,3 @@ } | ||
value: function onDragLeave(evt) { | ||
var _this3 = this; | ||
var _this5 = this; | ||
@@ -182,3 +194,3 @@ evt.preventDefault(); | ||
this.dragTargets = this.dragTargets.filter(function (el) { | ||
return el !== evt.target && _this3.node.contains(el); | ||
return el !== evt.target && _this5.node.contains(el); | ||
}); | ||
@@ -195,5 +207,7 @@ if (this.dragTargets.length > 0) { | ||
if (this.props.onDragLeave) { | ||
this.props.onDragLeave.call(this, evt); | ||
} | ||
Promise.resolve(this.props.getDataTransferItems(evt)).then(function (draggedFiles) { | ||
if (hasFiles(draggedFiles) && _this5.props.onDragLeave) { | ||
_this5.props.onDragLeave.call(_this5, evt); | ||
} | ||
}); | ||
} | ||
@@ -203,3 +217,3 @@ }, { | ||
value: function onDrop(evt) { | ||
var _this4 = this; | ||
var _this6 = this; | ||
@@ -250,3 +264,3 @@ var _props = this.props, | ||
if (fileAccepted(file, accept) && fileMatchSize(file, _this4.props.maxSize, _this4.props.minSize)) { | ||
if (fileAccepted(file, accept) && fileMatchSize(file, _this6.props.maxSize, _this6.props.minSize)) { | ||
acceptedFiles.push(file); | ||
@@ -264,12 +278,12 @@ } else { | ||
if (onDrop) { | ||
onDrop.call(_this4, acceptedFiles, rejectedFiles, evt); | ||
if (hasFiles(fileList) && onDrop) { | ||
onDrop.call(_this6, acceptedFiles, rejectedFiles, evt); | ||
} | ||
if (rejectedFiles.length > 0 && onDropRejected) { | ||
onDropRejected.call(_this4, rejectedFiles, evt); | ||
onDropRejected.call(_this6, rejectedFiles, evt); | ||
} | ||
if (acceptedFiles.length > 0 && onDropAccepted) { | ||
onDropAccepted.call(_this4, acceptedFiles, evt); | ||
onDropAccepted.call(_this6, acceptedFiles, evt); | ||
} | ||
@@ -280,3 +294,3 @@ | ||
// values | ||
_this4.setState({ acceptedFiles: acceptedFiles, rejectedFiles: rejectedFiles }); | ||
_this6.setState({ acceptedFiles: acceptedFiles, rejectedFiles: rejectedFiles }); | ||
}); | ||
@@ -319,3 +333,3 @@ } | ||
value: function onFileDialogCancel() { | ||
var _this5 = this; | ||
var _this7 = this; | ||
@@ -328,9 +342,9 @@ // timeout will not recognize context of this method | ||
setTimeout(function () { | ||
if (_this5.fileInputEl != null) { | ||
if (_this7.fileInputEl != null) { | ||
// Returns an object as FileList | ||
var files = _this5.fileInputEl.files; | ||
var files = _this7.fileInputEl.files; | ||
if (!files.length) { | ||
_this5.isFileDialogActive = false; | ||
_this7.isFileDialogActive = false; | ||
} | ||
@@ -337,0 +351,0 @@ } |
@@ -15,3 +15,11 @@ import accepts from 'attr-accept'; | ||
// but Chrome implements some drag store, which is accesible via dataTransfer.items | ||
dataTransferItemsList = dt.items; | ||
// Map the items to File objects, | ||
// and filter non-File items | ||
// see https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/getAsFile | ||
var files = Array.prototype.map.call(dt.items, function (item) { | ||
return item.getAsFile(); | ||
}); | ||
dataTransferItemsList = Array.prototype.filter.call(files, function (file) { | ||
return file !== null; | ||
}); | ||
} | ||
@@ -42,2 +50,10 @@ } else if (event.target && event.target.files) { | ||
export function hasFiles(files) { | ||
// Allow only files and retun the items as a list of File, | ||
// see https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem for details | ||
return Array.isArray(files) && files.length > 0 && Array.prototype.every.call(files, function (file) { | ||
return file instanceof File; | ||
}); | ||
} | ||
// allow the entire document to be a drag target | ||
@@ -44,0 +60,0 @@ export function onDocumentDragOver(evt) { |
@@ -113,2 +113,3 @@ { | ||
"eslint-plugin-react": "7.x", | ||
"html5-file-selector": "^2.1.0", | ||
"husky": "^0.14.3", | ||
@@ -146,3 +147,3 @@ "imagemin-cli": "^3.0.0", | ||
}, | ||
"version": "5.1.1", | ||
"version": "6.0.0", | ||
"engines": { | ||
@@ -149,0 +150,0 @@ "node": ">= 6" |
@@ -13,3 +13,4 @@ /* global process */ | ||
getDataTransferItems as defaultGetDataTransferItem, | ||
isIeOrEdge | ||
isIeOrEdge, | ||
hasFiles | ||
} from './utils' | ||
@@ -88,5 +89,7 @@ import styles from './utils/styles' | ||
onDragStart(evt) { | ||
if (this.props.onDragStart) { | ||
this.props.onDragStart.call(this, evt) | ||
} | ||
Promise.resolve(this.props.getDataTransferItems(evt)).then(draggedFiles => { | ||
if (hasFiles(draggedFiles) && this.props.onDragStart) { | ||
this.props.onDragStart.call(this, evt) | ||
} | ||
}) | ||
} | ||
@@ -105,10 +108,13 @@ | ||
Promise.resolve(this.props.getDataTransferItems(evt)).then(draggedFiles => { | ||
this.setState({ | ||
isDragActive: true, // Do not rely on files for the drag state. It doesn't work in Safari. | ||
draggedFiles | ||
}) | ||
if (hasFiles(draggedFiles)) { | ||
this.setState({ | ||
isDragActive: true, // Do not rely on files for the drag state. It doesn't work in Safari. | ||
draggedFiles | ||
}) | ||
if (this.props.onDragEnter) { | ||
this.props.onDragEnter.call(this, evt) | ||
} | ||
} | ||
}) | ||
if (this.props.onDragEnter) { | ||
this.props.onDragEnter.call(this, evt) | ||
} | ||
} | ||
@@ -129,5 +135,8 @@ | ||
if (this.props.onDragOver) { | ||
this.props.onDragOver.call(this, evt) | ||
} | ||
Promise.resolve(this.props.getDataTransferItems(evt)).then(draggedFiles => { | ||
if (hasFiles(draggedFiles) && this.props.onDragOver) { | ||
this.props.onDragOver.call(this, evt) | ||
} | ||
}) | ||
return false | ||
@@ -151,5 +160,7 @@ } | ||
if (this.props.onDragLeave) { | ||
this.props.onDragLeave.call(this, evt) | ||
} | ||
Promise.resolve(this.props.getDataTransferItems(evt)).then(draggedFiles => { | ||
if (hasFiles(draggedFiles) && this.props.onDragLeave) { | ||
this.props.onDragLeave.call(this, evt) | ||
} | ||
}) | ||
} | ||
@@ -218,3 +229,3 @@ | ||
if (onDrop) { | ||
if (hasFiles(fileList) && onDrop) { | ||
onDrop.call(this, acceptedFiles, rejectedFiles, evt) | ||
@@ -221,0 +232,0 @@ } |
@@ -18,3 +18,14 @@ /* eslint jsx-a11y/click-events-have-key-events: 0 */ | ||
const createFile = (name, size, type) => { | ||
const file = new File([], name, { type }) | ||
Object.defineProperty(file, 'size', { | ||
get() { | ||
return size | ||
} | ||
}) | ||
return file | ||
} | ||
let files | ||
let nonFileItems | ||
let images | ||
@@ -37,22 +48,15 @@ | ||
beforeEach(() => { | ||
files = [ | ||
files = [createFile('file1.pdf', 1111, 'application/pdf')] | ||
nonFileItems = [ | ||
{ | ||
name: 'file1.pdf', | ||
size: 1111, | ||
type: 'application/pdf' | ||
kind: 'string', | ||
type: 'text/plain', | ||
getAsFile() { | ||
return null | ||
} | ||
} | ||
] | ||
images = [ | ||
{ | ||
name: 'cats.gif', | ||
size: 1234, | ||
type: 'image/gif' | ||
}, | ||
{ | ||
name: 'dogs.jpg', | ||
size: 2345, | ||
type: 'image/jpeg' | ||
} | ||
] | ||
images = [createFile('cats.gif', 1234, 'image/gif'), createFile('dogs.gif', 2345, 'image/jpeg')] | ||
}) | ||
@@ -303,4 +307,4 @@ | ||
describe('drag-n-drop', () => { | ||
it('should override onDrag* methods', () => { | ||
describe('drag-n-drop', async () => { | ||
it('should override onDrag* methods', async () => { | ||
const props = { | ||
@@ -313,13 +317,21 @@ onDragStart: jest.fn(), | ||
const component = mount(<Dropzone {...props} />) | ||
component.simulate('dragStart') | ||
component.simulate('dragStart', { dataTransfer: { files } }) | ||
await flushPromises(component) | ||
expect(props.onDragStart).toHaveBeenCalled() | ||
component.simulate('dragEnter', { dataTransfer: { items: files } }) | ||
await component.simulate('dragEnter', { dataTransfer: { files } }) | ||
await flushPromises(component) | ||
expect(props.onDragEnter).toHaveBeenCalled() | ||
component.simulate('dragOver', { dataTransfer: { items: files } }) | ||
await component.simulate('dragOver', { dataTransfer: { files } }) | ||
await flushPromises(component) | ||
expect(props.onDragOver).toHaveBeenCalled() | ||
component.simulate('dragLeave', { dataTransfer: { items: files } }) | ||
await component.simulate('dragLeave', { dataTransfer: { files } }) | ||
await flushPromises(component) | ||
expect(props.onDragLeave).toHaveBeenCalled() | ||
}) | ||
it('should guard dropEffect in onDragOver for IE', () => { | ||
it('should guard dropEffect in onDragOver for IE', async () => { | ||
const props = { | ||
@@ -333,38 +345,82 @@ onDragStart: jest.fn(), | ||
// Using Proxy we'll emulate IE throwing when setting dataTransfer.dropEffect | ||
const eventProxy = new Proxy( | ||
{}, | ||
{ | ||
get: (target, prop) => { | ||
switch (prop) { | ||
case 'dataTransfer': | ||
throw new Error('IE does not support rrror') | ||
default: | ||
return function noop() {} | ||
const eventProxy = { | ||
preventDefault() {}, | ||
stopPropagation() {}, | ||
dataTransfer: new Proxy( | ||
{}, | ||
{ | ||
set: (target, prop) => { | ||
switch (prop) { | ||
case 'dropEffect': | ||
throw new Error('IE does not support setting {dropEffect}') | ||
default: | ||
break | ||
} | ||
} | ||
} | ||
} | ||
) | ||
) | ||
} | ||
// And using then we'll call the onDragOver with the proxy instead of event | ||
const componentOnDragOver = component.instance().onDragOver | ||
const instance = component.instance() | ||
const componentOnDragOver = instance.onDragOver | ||
const onDragOver = jest | ||
.spyOn(component.instance(), 'onDragOver') | ||
.spyOn(instance, 'onDragOver') | ||
.mockImplementation(() => componentOnDragOver(eventProxy)) | ||
component.simulate('dragStart', { dataTransfer: { items: files } }) | ||
component.simulate('dragStart', { dataTransfer: { files } }) | ||
await flushPromises(component) | ||
expect(props.onDragStart).toHaveBeenCalled() | ||
component.simulate('dragEnter', { dataTransfer: { items: files } }) | ||
component.simulate('dragEnter', { dataTransfer: { files } }) | ||
await flushPromises(component) | ||
expect(props.onDragEnter).toHaveBeenCalled() | ||
component.simulate('dragLeave', { dataTransfer: { items: files } }) | ||
component.simulate('dragLeave', { dataTransfer: { files } }) | ||
await flushPromises(component) | ||
expect(props.onDragLeave).toHaveBeenCalled() | ||
// It should not throw the error | ||
component.simulate('dragOver', { dataTransfer: { items: files } }) | ||
component.simulate('dragOver', { dataTransfer: { files } }) | ||
await flushPromises(component) | ||
expect(onDragOver).not.toThrow() | ||
}) | ||
it('should not call onDrag* if there are no files', async () => { | ||
const props = { | ||
onDragStart: jest.fn(), | ||
onDragEnter: jest.fn(), | ||
onDragOver: jest.fn(), | ||
onDragLeave: jest.fn(), | ||
onDrop: jest.fn() | ||
} | ||
const component = mount(<Dropzone {...props} />) | ||
component.simulate('dragStart', { dataTransfer: { items: nonFileItems } }) | ||
await flushPromises(component) | ||
expect(props.onDragStart).not.toHaveBeenCalled() | ||
component.simulate('dragEnter', { dataTransfer: { items: nonFileItems } }) | ||
await flushPromises(component) | ||
expect(props.onDragEnter).not.toHaveBeenCalled() | ||
component.simulate('dragOver', { dataTransfer: { items: nonFileItems } }) | ||
await flushPromises(component) | ||
expect(props.onDragOver).not.toHaveBeenCalled() | ||
component.simulate('dragLeave', { dataTransfer: { items: nonFileItems } }) | ||
await flushPromises(component) | ||
expect(props.onDragLeave).not.toHaveBeenCalled() | ||
component.simulate('drop', { dataTransfer: { items: nonFileItems } }) | ||
await flushPromises(component) | ||
expect(props.onDrop).not.toHaveBeenCalled() | ||
}) | ||
it('should set proper dragActive state on dragEnter', async () => { | ||
const dropzone = mount(<Dropzone>{props => <DummyChildComponent {...props} />}</Dropzone>) | ||
dropzone.simulate('dragEnter', { dataTransfer: { files } }) | ||
const component = mount(<Dropzone>{props => <DummyChildComponent {...props} />}</Dropzone>) | ||
component.simulate('dragEnter', { dataTransfer: { files } }) | ||
const updatedDropzone = await flushPromises(dropzone) | ||
const updatedDropzone = await flushPromises(component) | ||
const child = updatedDropzone.find(DummyChildComponent) | ||
@@ -610,3 +666,3 @@ | ||
dropzone.simulate('dragLeave', { dataTransfer: { files } }) | ||
await dropzone.simulate('dragLeave', { dataTransfer: { files } }) | ||
expect(dropzone.find(DragActiveComponent).children()).toHaveLength(0) | ||
@@ -774,9 +830,3 @@ expect(dropzone.find(ChildComponent)).toHaveProp('isDragAccept', false) | ||
) | ||
const bogusImages = [ | ||
{ | ||
name: 'bogus.gif', | ||
size: 1234, | ||
type: 'application/x-moz-file' | ||
} | ||
] | ||
const bogusImages = [createFile('bogus.gif', 1234, 'application/x-moz-file')] | ||
@@ -783,0 +833,0 @@ await dropzone.simulate('drop', { dataTransfer: { files: bogusImages } }) |
@@ -18,3 +18,7 @@ import accepts from 'attr-accept' | ||
// but Chrome implements some drag store, which is accesible via dataTransfer.items | ||
dataTransferItemsList = dt.items | ||
// Map the items to File objects, | ||
// and filter non-File items | ||
// see https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/getAsFile | ||
const files = Array.prototype.map.call(dt.items, item => item.getAsFile()) | ||
dataTransferItemsList = Array.prototype.filter.call(files, file => file !== null) | ||
} | ||
@@ -43,2 +47,12 @@ } else if (event.target && event.target.files) { | ||
export function hasFiles(files) { | ||
// Allow only files and retun the items as a list of File, | ||
// see https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem for details | ||
return ( | ||
Array.isArray(files) && | ||
files.length > 0 && | ||
Array.prototype.every.call(files, file => file instanceof File) | ||
) | ||
} | ||
// allow the entire document to be a drag target | ||
@@ -45,0 +59,0 @@ export function onDocumentDragOver(evt) { |
@@ -1,2 +0,2 @@ | ||
import { getDataTransferItems, isIeOrEdge } from './' | ||
import { getDataTransferItems, isIeOrEdge, hasFiles } from './' | ||
@@ -21,2 +21,29 @@ const files = [ | ||
const nonFileItems = [ | ||
{ | ||
kind: 'string', | ||
type: 'text/plain', | ||
getAsFile() { | ||
return null | ||
} | ||
} | ||
] | ||
const json = JSON.stringify({ | ||
ping: true | ||
}) | ||
const file = new File([json], 'test.json', { | ||
type: 'application/json' | ||
}) | ||
const fileItems = [ | ||
{ | ||
kind: 'file', | ||
type: 'application/json', | ||
getAsFile() { | ||
return file | ||
} | ||
} | ||
] | ||
describe('getDataTransferItems', () => { | ||
@@ -49,3 +76,3 @@ it('should return an array', () => { | ||
dataTransfer: { | ||
items: files | ||
items: fileItems | ||
} | ||
@@ -55,5 +82,19 @@ } | ||
expect(res).toBeInstanceOf(Array) | ||
expect(res).toHaveLength(3) | ||
expect(res).toHaveLength(1) | ||
}) | ||
it('should ignore dataTransfer.items that are not of kind "file"', () => { | ||
const event = { | ||
target: { | ||
files: [{}] | ||
}, | ||
dataTransfer: { | ||
items: nonFileItems | ||
} | ||
} | ||
const res = getDataTransferItems(event) | ||
expect(res).toBeInstanceOf(Array) | ||
expect(res).toHaveLength(0) | ||
}) | ||
it('should use event.target if dataTransfer is not defined', () => { | ||
@@ -70,3 +111,3 @@ const event = { | ||
it('should prioritize dataTransfer.files over .files', () => { | ||
it('should prioritize dataTransfer.files over .items', () => { | ||
const event = { | ||
@@ -123,1 +164,10 @@ dataTransfer: { | ||
}) | ||
describe('hasFiles', () => { | ||
it('should only return true for an Array of File objects', () => { | ||
expect(hasFiles([file])).toBe(true) | ||
expect(hasFiles(['domNode'])).toBe(false) | ||
expect(hasFiles([])).toBe(false) | ||
expect(hasFiles(null)).toBe(false) | ||
}) | ||
}) |
@@ -46,2 +46,18 @@ /* eslint import/no-extraneous-dependencies: 0 */ | ||
content: 'examples/Fullscreen/Readme.md' | ||
}, | ||
{ | ||
name: 'Extending Dropzone', | ||
context: { | ||
Dropzone: './src/index' | ||
}, | ||
sections: [ | ||
{ | ||
name: 'Using third-party plugins', | ||
content: 'examples/PluginArchitecture/Readme.md' | ||
}, | ||
{ | ||
name: 'Dropzone for folders', | ||
content: 'examples/Folders/Readme.md' | ||
} | ||
] | ||
} | ||
@@ -48,0 +64,0 @@ ] |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
215685
41
3374
55