react-dropzone 11.5.2 → 11.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +1 -3
- package/.eslintrc +1 -1
- package/.husky/commit-msg +5 -0
- package/.husky/pre-commit +5 -0
- package/.nvmrc +1 -1
- package/README.md +77 -29
- package/dist/es/index.js +168 -124
- package/dist/es/utils/index.js +39 -3
- package/dist/index.js +16 -2
- package/examples/events/README.md +3 -1
- package/examples/no-jsx/README.md +32 -0
- package/package.json +78 -75
- package/rollup.config.js +9 -6
- package/src/.eslintrc +7 -3
- package/src/__snapshots__/index.spec.js.snap +1 -1
- package/src/index.js +186 -134
- package/src/index.spec.js +306 -0
- package/src/utils/index.js +38 -4
- package/src/utils/index.spec.js +92 -29
- package/styleguide.config.js +4 -0
- package/typings/.eslintrc +28 -0
- package/tslint.json +0 -101
package/src/index.spec.js
CHANGED
|
@@ -1159,6 +1159,20 @@ describe('useDropzone() hook', () => {
|
|
|
1159
1159
|
})
|
|
1160
1160
|
|
|
1161
1161
|
describe('onClick', () => {
|
|
1162
|
+
let currentShowOpenFilePicker;
|
|
1163
|
+
|
|
1164
|
+
beforeEach(() => {
|
|
1165
|
+
currentShowOpenFilePicker = window.showOpenFilePicker
|
|
1166
|
+
})
|
|
1167
|
+
|
|
1168
|
+
afterEach(() => {
|
|
1169
|
+
if (currentShowOpenFilePicker) {
|
|
1170
|
+
window.showOpenFilePicker = currentShowOpenFilePicker
|
|
1171
|
+
} else {
|
|
1172
|
+
delete window.showOpenFilePicker
|
|
1173
|
+
}
|
|
1174
|
+
})
|
|
1175
|
+
|
|
1162
1176
|
it('should proxy the click event to the input', () => {
|
|
1163
1177
|
const activeRef = createRef()
|
|
1164
1178
|
const active = <span ref={activeRef}>I am active</span>
|
|
@@ -1303,6 +1317,251 @@ describe('useDropzone() hook', () => {
|
|
|
1303
1317
|
jest.useRealTimers()
|
|
1304
1318
|
isIeOrEdgeSpy.mockClear()
|
|
1305
1319
|
})
|
|
1320
|
+
|
|
1321
|
+
it('should not use showOpenFilePicker() if supported and {useFsAccessApi} is not true', async () => {
|
|
1322
|
+
jest.useFakeTimers()
|
|
1323
|
+
|
|
1324
|
+
const activeRef = createRef()
|
|
1325
|
+
const active = <span ref={activeRef}>I am active</span>
|
|
1326
|
+
const onClickSpy = jest.spyOn(HTMLInputElement.prototype, 'click')
|
|
1327
|
+
|
|
1328
|
+
const handlers = files.map(f => createFileSystemFileHandle(f))
|
|
1329
|
+
const showOpenFilePickerMock = jest.fn().mockReturnValue(Promise.resolve(handlers))
|
|
1330
|
+
|
|
1331
|
+
window.showOpenFilePicker = showOpenFilePickerMock
|
|
1332
|
+
|
|
1333
|
+
const onDropSpy = jest.fn()
|
|
1334
|
+
const onFileDialogOpenSpy = jest.fn()
|
|
1335
|
+
|
|
1336
|
+
const ui = (
|
|
1337
|
+
<Dropzone
|
|
1338
|
+
onDrop={onDropSpy}
|
|
1339
|
+
onFileDialogOpen={onFileDialogOpenSpy}
|
|
1340
|
+
accept="application/pdf"
|
|
1341
|
+
multiple
|
|
1342
|
+
>
|
|
1343
|
+
{({ getRootProps, getInputProps, isFileDialogActive }) => (
|
|
1344
|
+
<div {...getRootProps()}>
|
|
1345
|
+
<input {...getInputProps()} />
|
|
1346
|
+
{isFileDialogActive && active}
|
|
1347
|
+
</div>
|
|
1348
|
+
)}
|
|
1349
|
+
</Dropzone>
|
|
1350
|
+
)
|
|
1351
|
+
|
|
1352
|
+
const { container, rerender } = render(ui)
|
|
1353
|
+
|
|
1354
|
+
const dropzone = container.querySelector('div')
|
|
1355
|
+
|
|
1356
|
+
fireEvent.click(dropzone)
|
|
1357
|
+
|
|
1358
|
+
await flushPromises(rerender, ui)
|
|
1359
|
+
|
|
1360
|
+
dispatchEvt(document.body, 'focus')
|
|
1361
|
+
drainTimers()
|
|
1362
|
+
|
|
1363
|
+
expect(onFileDialogOpenSpy).toHaveBeenCalled()
|
|
1364
|
+
|
|
1365
|
+
expect(activeRef.current).toBeNull()
|
|
1366
|
+
expect(dropzone).not.toContainElement(activeRef.current)
|
|
1367
|
+
expect(onClickSpy).toHaveBeenCalled()
|
|
1368
|
+
expect(showOpenFilePickerMock).not.toHaveBeenCalled()
|
|
1369
|
+
expect(onDropSpy).not.toHaveBeenCalled()
|
|
1370
|
+
|
|
1371
|
+
jest.useRealTimers()
|
|
1372
|
+
})
|
|
1373
|
+
|
|
1374
|
+
it('should use showOpenFilePicker() if supported and {useFsAccessApi} is true, and not trigger click on input', async () => {
|
|
1375
|
+
const activeRef = createRef()
|
|
1376
|
+
const active = <span ref={activeRef}>I am active</span>
|
|
1377
|
+
const onClickSpy = jest.spyOn(HTMLInputElement.prototype, 'click')
|
|
1378
|
+
|
|
1379
|
+
const handlers = files.map(f => createFileSystemFileHandle(f))
|
|
1380
|
+
const thenable = createThenable()
|
|
1381
|
+
const showOpenFilePickerMock = jest.fn().mockReturnValue(thenable.promise)
|
|
1382
|
+
|
|
1383
|
+
window.showOpenFilePicker = showOpenFilePickerMock
|
|
1384
|
+
|
|
1385
|
+
const onDropSpy = jest.fn()
|
|
1386
|
+
const onFileDialogOpenSpy = jest.fn()
|
|
1387
|
+
|
|
1388
|
+
const ui = (
|
|
1389
|
+
<Dropzone
|
|
1390
|
+
onDrop={onDropSpy}
|
|
1391
|
+
onFileDialogOpen={onFileDialogOpenSpy}
|
|
1392
|
+
accept="application/pdf"
|
|
1393
|
+
multiple
|
|
1394
|
+
useFsAccessApi
|
|
1395
|
+
>
|
|
1396
|
+
{({ getRootProps, getInputProps, isFileDialogActive }) => (
|
|
1397
|
+
<div {...getRootProps()}>
|
|
1398
|
+
<input {...getInputProps()} />
|
|
1399
|
+
{isFileDialogActive && active}
|
|
1400
|
+
</div>
|
|
1401
|
+
)}
|
|
1402
|
+
</Dropzone>
|
|
1403
|
+
)
|
|
1404
|
+
|
|
1405
|
+
const { container, rerender } = render(ui)
|
|
1406
|
+
|
|
1407
|
+
const dropzone = container.querySelector('div')
|
|
1408
|
+
|
|
1409
|
+
fireEvent.click(dropzone)
|
|
1410
|
+
|
|
1411
|
+
await flushPromises(rerender, ui)
|
|
1412
|
+
|
|
1413
|
+
expect(activeRef.current).not.toBeNull()
|
|
1414
|
+
expect(dropzone).toContainElement(activeRef.current)
|
|
1415
|
+
expect(onFileDialogOpenSpy).toHaveBeenCalled()
|
|
1416
|
+
|
|
1417
|
+
thenable.done(handlers)
|
|
1418
|
+
await flushPromises(rerender, ui)
|
|
1419
|
+
|
|
1420
|
+
expect(activeRef.current).toBeNull() // We expect the dialog to be closed at this point
|
|
1421
|
+
expect(dropzone).not.toContainElement(activeRef.current)
|
|
1422
|
+
expect(onClickSpy).not.toHaveBeenCalled()
|
|
1423
|
+
expect(showOpenFilePickerMock).toHaveBeenCalledWith({
|
|
1424
|
+
multiple: true,
|
|
1425
|
+
types: [{
|
|
1426
|
+
description: 'everything',
|
|
1427
|
+
accept: {'application/pdf': []}
|
|
1428
|
+
}]
|
|
1429
|
+
})
|
|
1430
|
+
expect(onDropSpy).toHaveBeenCalledWith(files, [], null)
|
|
1431
|
+
})
|
|
1432
|
+
|
|
1433
|
+
test('if showOpenFilePicker() is supported and {useFsAccessApi} is true, it should work without the <input>', async () => {
|
|
1434
|
+
const activeRef = createRef()
|
|
1435
|
+
const active = <span ref={activeRef}>I am active</span>
|
|
1436
|
+
|
|
1437
|
+
const handlers = files.map(f => createFileSystemFileHandle(f))
|
|
1438
|
+
const showOpenFilePickerMock = jest.fn().mockReturnValue(Promise.resolve(handlers))
|
|
1439
|
+
|
|
1440
|
+
window.showOpenFilePicker = showOpenFilePickerMock
|
|
1441
|
+
|
|
1442
|
+
const onDropSpy = jest.fn()
|
|
1443
|
+
const onFileDialogOpenSpy = jest.fn()
|
|
1444
|
+
|
|
1445
|
+
const ui = (
|
|
1446
|
+
<Dropzone
|
|
1447
|
+
onDrop={onDropSpy}
|
|
1448
|
+
onFileDialogOpen={onFileDialogOpenSpy}
|
|
1449
|
+
useFsAccessApi
|
|
1450
|
+
>
|
|
1451
|
+
{({ getRootProps, isFileDialogActive }) => (
|
|
1452
|
+
<div {...getRootProps()}>
|
|
1453
|
+
{isFileDialogActive && active}
|
|
1454
|
+
</div>
|
|
1455
|
+
)}
|
|
1456
|
+
</Dropzone>
|
|
1457
|
+
)
|
|
1458
|
+
|
|
1459
|
+
const { container, rerender } = render(ui)
|
|
1460
|
+
|
|
1461
|
+
const dropzone = container.querySelector('div')
|
|
1462
|
+
|
|
1463
|
+
fireEvent.click(dropzone)
|
|
1464
|
+
await flushPromises(rerender, ui)
|
|
1465
|
+
const ref = activeRef.current
|
|
1466
|
+
expect(ref).toBeNull() // We expect the dialog to be closed at this point
|
|
1467
|
+
expect(dropzone).not.toContainElement(ref)
|
|
1468
|
+
expect(showOpenFilePickerMock).toHaveBeenCalled()
|
|
1469
|
+
expect(onDropSpy).toHaveBeenCalledWith(files, [], null)
|
|
1470
|
+
expect(onFileDialogOpenSpy).toHaveBeenCalled()
|
|
1471
|
+
})
|
|
1472
|
+
|
|
1473
|
+
test('if showOpenFilePicker() is supported and {useFsAccessApi} is true, and the user cancels it should call onFileDialogCancel', async () => {
|
|
1474
|
+
const activeRef = createRef()
|
|
1475
|
+
const active = <span ref={activeRef}>I am active</span>
|
|
1476
|
+
|
|
1477
|
+
const showOpenFilePickerMock = jest.fn().mockReturnValue(Promise.reject(new DOMException("user aborted request", "AbortError")))
|
|
1478
|
+
|
|
1479
|
+
window.showOpenFilePicker = showOpenFilePickerMock
|
|
1480
|
+
|
|
1481
|
+
const onDropSpy = jest.fn()
|
|
1482
|
+
const onFileDialogCancelSpy = jest.fn()
|
|
1483
|
+
|
|
1484
|
+
const ui = (
|
|
1485
|
+
<Dropzone
|
|
1486
|
+
onDrop={onDropSpy}
|
|
1487
|
+
onFileDialogCancel={onFileDialogCancelSpy}
|
|
1488
|
+
useFsAccessApi
|
|
1489
|
+
>
|
|
1490
|
+
{({ getRootProps, isFileDialogActive }) => (
|
|
1491
|
+
<div {...getRootProps()}>
|
|
1492
|
+
{isFileDialogActive && active}
|
|
1493
|
+
</div>
|
|
1494
|
+
)}
|
|
1495
|
+
</Dropzone>
|
|
1496
|
+
)
|
|
1497
|
+
|
|
1498
|
+
const { container, rerender } = render(ui)
|
|
1499
|
+
|
|
1500
|
+
const dropzone = container.querySelector('div')
|
|
1501
|
+
|
|
1502
|
+
fireEvent.click(dropzone)
|
|
1503
|
+
await flushPromises(rerender, ui)
|
|
1504
|
+
const ref = activeRef.current
|
|
1505
|
+
expect(ref).toBeNull() // We expect the dialog to be closed at this point
|
|
1506
|
+
expect(dropzone).not.toContainElement(ref)
|
|
1507
|
+
expect(showOpenFilePickerMock).toHaveBeenCalled()
|
|
1508
|
+
expect(onDropSpy).not.toHaveBeenCalled()
|
|
1509
|
+
expect(onFileDialogCancelSpy).toHaveBeenCalled()
|
|
1510
|
+
})
|
|
1511
|
+
|
|
1512
|
+
test('window focus evt is not bound if showOpenFilePicker() is supported and {useFsAccessApi} is true', async () => {
|
|
1513
|
+
jest.useFakeTimers()
|
|
1514
|
+
|
|
1515
|
+
const activeRef = createRef()
|
|
1516
|
+
const active = <span ref={activeRef}>I am active</span>
|
|
1517
|
+
const onFileDialogCancelSpy = jest.fn()
|
|
1518
|
+
|
|
1519
|
+
const thenable = createThenable()
|
|
1520
|
+
const showOpenFilePickerMock = jest.fn().mockReturnValue(thenable.promise)
|
|
1521
|
+
|
|
1522
|
+
window.showOpenFilePicker = showOpenFilePickerMock
|
|
1523
|
+
|
|
1524
|
+
const ui = (
|
|
1525
|
+
<Dropzone
|
|
1526
|
+
onFileDialogCancel={onFileDialogCancelSpy}
|
|
1527
|
+
noClick
|
|
1528
|
+
useFsAccessApi
|
|
1529
|
+
>
|
|
1530
|
+
{({ getRootProps, isFileDialogActive, open }) => (
|
|
1531
|
+
<div {...getRootProps()}>
|
|
1532
|
+
{isFileDialogActive && active}
|
|
1533
|
+
<button type="button" onClick={open}>
|
|
1534
|
+
Open
|
|
1535
|
+
</button>
|
|
1536
|
+
</div>
|
|
1537
|
+
)}
|
|
1538
|
+
</Dropzone>
|
|
1539
|
+
)
|
|
1540
|
+
|
|
1541
|
+
const { container, rerender } = render(ui)
|
|
1542
|
+
|
|
1543
|
+
const dropzone = container.querySelector('div')
|
|
1544
|
+
const btn = container.querySelector('button')
|
|
1545
|
+
|
|
1546
|
+
btn.click()
|
|
1547
|
+
drainTimers()
|
|
1548
|
+
await flushPromises(rerender, ui)
|
|
1549
|
+
|
|
1550
|
+
const ref = activeRef.current
|
|
1551
|
+
expect(ref).not.toBeNull()
|
|
1552
|
+
expect(dropzone).toContainElement(ref)
|
|
1553
|
+
|
|
1554
|
+
thenable.cancel(new DOMException('user aborted request', 'AbortError'))
|
|
1555
|
+
|
|
1556
|
+
dispatchEvt(document.body, 'focus')
|
|
1557
|
+
drainTimers()
|
|
1558
|
+
await flushPromises(rerender, ui)
|
|
1559
|
+
|
|
1560
|
+
expect(onFileDialogCancelSpy).toHaveBeenCalledTimes(1)
|
|
1561
|
+
expect(dropzone).not.toContainElement(ref)
|
|
1562
|
+
|
|
1563
|
+
jest.useRealTimers()
|
|
1564
|
+
})
|
|
1306
1565
|
})
|
|
1307
1566
|
|
|
1308
1567
|
describe('onKeyDown', () => {
|
|
@@ -2803,6 +3062,34 @@ describe('useDropzone() hook', () => {
|
|
|
2803
3062
|
], expect.anything())
|
|
2804
3063
|
})
|
|
2805
3064
|
})
|
|
3065
|
+
|
|
3066
|
+
describe('accessibility', () => {
|
|
3067
|
+
it('sets the role attribute to button by default on the root', () => {
|
|
3068
|
+
const { container } = render(
|
|
3069
|
+
<Dropzone>
|
|
3070
|
+
{({ getRootProps }) => (
|
|
3071
|
+
<div id="root" {...getRootProps()} />
|
|
3072
|
+
)}
|
|
3073
|
+
</Dropzone>
|
|
3074
|
+
)
|
|
3075
|
+
const root = container.querySelector('#root')
|
|
3076
|
+
|
|
3077
|
+
expect(root).toHaveAttribute('role', 'button')
|
|
3078
|
+
})
|
|
3079
|
+
|
|
3080
|
+
test('users can override the default role attribute on the root', () => {
|
|
3081
|
+
const { container } = render(
|
|
3082
|
+
<Dropzone>
|
|
3083
|
+
{({ getRootProps }) => (
|
|
3084
|
+
<div id="root" {...getRootProps({role: 'generic'})} />
|
|
3085
|
+
)}
|
|
3086
|
+
</Dropzone>
|
|
3087
|
+
)
|
|
3088
|
+
const root = container.querySelector('#root')
|
|
3089
|
+
|
|
3090
|
+
expect(root).toHaveAttribute('role', 'generic')
|
|
3091
|
+
})
|
|
3092
|
+
})
|
|
2806
3093
|
})
|
|
2807
3094
|
|
|
2808
3095
|
async function flushPromises(rerender, ui) {
|
|
@@ -2854,6 +3141,10 @@ function dispatchEvt(node, type, data) {
|
|
|
2854
3141
|
fireEvent(node, event)
|
|
2855
3142
|
}
|
|
2856
3143
|
|
|
3144
|
+
function createFileSystemFileHandle(file) {
|
|
3145
|
+
return {getFile: () => Promise.resolve(file)}
|
|
3146
|
+
}
|
|
3147
|
+
|
|
2857
3148
|
function createFile(name, size, type) {
|
|
2858
3149
|
const file = new File([], name, { type })
|
|
2859
3150
|
Object.defineProperty(file, 'size', {
|
|
@@ -2863,3 +3154,18 @@ function createFile(name, size, type) {
|
|
|
2863
3154
|
})
|
|
2864
3155
|
return file
|
|
2865
3156
|
}
|
|
3157
|
+
|
|
3158
|
+
function createThenable() {
|
|
3159
|
+
let done, cancel;
|
|
3160
|
+
|
|
3161
|
+
const promise = new Promise((resolve, reject) => {
|
|
3162
|
+
done = resolve
|
|
3163
|
+
cancel = reject
|
|
3164
|
+
})
|
|
3165
|
+
|
|
3166
|
+
return {
|
|
3167
|
+
promise,
|
|
3168
|
+
done,
|
|
3169
|
+
cancel
|
|
3170
|
+
}
|
|
3171
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -26,14 +26,14 @@ export const getInvalidTypeRejectionErr = accept => {
|
|
|
26
26
|
export const getTooLargeRejectionErr = maxSize => {
|
|
27
27
|
return {
|
|
28
28
|
code: FILE_TOO_LARGE,
|
|
29
|
-
message: `File is larger than ${maxSize} bytes`
|
|
29
|
+
message: `File is larger than ${maxSize} ${maxSize === 1 ? 'byte' : 'bytes'}`
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export const getTooSmallRejectionErr = minSize => {
|
|
34
34
|
return {
|
|
35
35
|
code: FILE_TOO_SMALL,
|
|
36
|
-
message: `File is smaller than ${minSize} bytes`
|
|
36
|
+
message: `File is smaller than ${minSize} ${minSize === 1 ? 'byte' : 'bytes'}`
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -66,8 +66,8 @@ function isDefined(value) {
|
|
|
66
66
|
return value !== undefined && value !== null
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
export function allFilesAccepted({
|
|
70
|
-
if ((!multiple && files.length > 1) || (multiple && maxFiles >= 1 &&
|
|
69
|
+
export function allFilesAccepted({files, accept, minSize, maxSize, multiple, maxFiles}) {
|
|
70
|
+
if ((!multiple && files.length > 1) || (multiple && maxFiles >= 1 && files.length > maxFiles)) {
|
|
71
71
|
return false;
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -142,3 +142,37 @@ export function composeEventHandlers(...fns) {
|
|
|
142
142
|
return isPropagationStopped(event)
|
|
143
143
|
})
|
|
144
144
|
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* canUseFileSystemAccessAPI checks if the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API)
|
|
148
|
+
* is supported by the browser.
|
|
149
|
+
* @returns {boolean}
|
|
150
|
+
*/
|
|
151
|
+
export function canUseFileSystemAccessAPI() {
|
|
152
|
+
return 'showOpenFilePicker' in window;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* filePickerOptionsTypes returns the {types} option for https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
|
|
157
|
+
* based on the accept attr (see https://github.com/react-dropzone/attr-accept)
|
|
158
|
+
* E.g: converts ['image/*', 'text/*'] to {'image/*': [], 'text/*': []}
|
|
159
|
+
* @param {string|string[]} accept
|
|
160
|
+
*/
|
|
161
|
+
export function filePickerOptionsTypes(accept) {
|
|
162
|
+
accept = typeof accept === 'string' ? accept.split(',') : accept
|
|
163
|
+
return [{
|
|
164
|
+
description: 'everything',
|
|
165
|
+
// TODO: Need to handle filtering more elegantly than this!
|
|
166
|
+
accept: Array.isArray(accept)
|
|
167
|
+
// Accept just MIME types as per spec
|
|
168
|
+
// NOTE: accept can be https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers
|
|
169
|
+
? accept.filter(item =>
|
|
170
|
+
item === 'audio/*' ||
|
|
171
|
+
item === 'video/*' ||
|
|
172
|
+
item === 'image/*' ||
|
|
173
|
+
item === 'text/*' ||
|
|
174
|
+
/\w+\/[-+.\w]+/g.test(item)
|
|
175
|
+
).reduce((a, b) => ({...a, [b]: []}), {})
|
|
176
|
+
: {},
|
|
177
|
+
}];
|
|
178
|
+
}
|
package/src/utils/index.spec.js
CHANGED
|
@@ -2,12 +2,10 @@ beforeEach(() => {
|
|
|
2
2
|
jest.resetModules()
|
|
3
3
|
})
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
describe('fileMatchSize()', () => {
|
|
7
6
|
let utils
|
|
8
|
-
beforeEach(async
|
|
7
|
+
beforeEach(async () => {
|
|
9
8
|
utils = await import('./index')
|
|
10
|
-
done()
|
|
11
9
|
})
|
|
12
10
|
|
|
13
11
|
it('should return true if the file object doesn\'t have a {size} property', () => {
|
|
@@ -42,9 +40,8 @@ describe('fileMatchSize()', () => {
|
|
|
42
40
|
|
|
43
41
|
describe('isIeOrEdge', () => {
|
|
44
42
|
let utils
|
|
45
|
-
beforeEach(async
|
|
43
|
+
beforeEach(async () => {
|
|
46
44
|
utils = await import('./index')
|
|
47
|
-
done()
|
|
48
45
|
})
|
|
49
46
|
|
|
50
47
|
it('should return true for IE10', () => {
|
|
@@ -89,9 +86,8 @@ describe('isPropagationStopped()', () => {
|
|
|
89
86
|
const trueFn = jest.fn(() => true)
|
|
90
87
|
|
|
91
88
|
let utils
|
|
92
|
-
beforeEach(async
|
|
89
|
+
beforeEach(async () => {
|
|
93
90
|
utils = await import('./index')
|
|
94
|
-
done()
|
|
95
91
|
})
|
|
96
92
|
|
|
97
93
|
it('should return result of isPropagationStopped() if isPropagationStopped exists', () => {
|
|
@@ -109,9 +105,8 @@ describe('isPropagationStopped()', () => {
|
|
|
109
105
|
|
|
110
106
|
describe('isEvtWithFiles()', () => {
|
|
111
107
|
let utils
|
|
112
|
-
beforeEach(async
|
|
108
|
+
beforeEach(async () => {
|
|
113
109
|
utils = await import('./index')
|
|
114
|
-
done()
|
|
115
110
|
})
|
|
116
111
|
|
|
117
112
|
it('should return true if some dragged types are files', () => {
|
|
@@ -145,9 +140,8 @@ describe('isEvtWithFiles()', () => {
|
|
|
145
140
|
|
|
146
141
|
describe('composeEventHandlers', () => {
|
|
147
142
|
let utils
|
|
148
|
-
beforeEach(async
|
|
143
|
+
beforeEach(async () => {
|
|
149
144
|
utils = await import('./index')
|
|
150
|
-
done()
|
|
151
145
|
})
|
|
152
146
|
|
|
153
147
|
it('returns a fn', () => {
|
|
@@ -193,9 +187,8 @@ describe('composeEventHandlers', () => {
|
|
|
193
187
|
|
|
194
188
|
describe('fileAccepted', () => {
|
|
195
189
|
let utils
|
|
196
|
-
beforeEach(async
|
|
190
|
+
beforeEach(async () => {
|
|
197
191
|
utils = await import('./index')
|
|
198
|
-
done()
|
|
199
192
|
})
|
|
200
193
|
|
|
201
194
|
it('accepts bogus firefox file', () => {
|
|
@@ -232,9 +225,8 @@ it('rejects file when single accept criteria as array', () => {
|
|
|
232
225
|
|
|
233
226
|
describe('allFilesAccepted()', () => {
|
|
234
227
|
let utils
|
|
235
|
-
beforeEach(async
|
|
228
|
+
beforeEach(async () => {
|
|
236
229
|
utils = await import('./index')
|
|
237
|
-
done()
|
|
238
230
|
})
|
|
239
231
|
it('rejects file when multiple accept criteria', () => {
|
|
240
232
|
const files = [createFile('hamster.pdf', 100, 'application/pdf'),createFile('fish.pdf', 100, 'application/pdf')];
|
|
@@ -242,29 +234,18 @@ describe('allFilesAccepted()', () => {
|
|
|
242
234
|
expect(utils.allFilesAccepted({ files, multiple: true})).toEqual(true)
|
|
243
235
|
expect(utils.allFilesAccepted({ files, multiple: true, maxFiles: 10 })).toEqual(true)
|
|
244
236
|
expect(utils.allFilesAccepted({ files, multiple: false, maxFiles: 10 })).toEqual(false)
|
|
245
|
-
expect(utils.allFilesAccepted({ files, multiple: true, accept:'image/jpeg' })).toEqual(false)
|
|
246
|
-
expect(utils.allFilesAccepted({ files: images, multiple: true,accept:'image/*' })).toEqual(true)
|
|
237
|
+
expect(utils.allFilesAccepted({ files, multiple: true, accept:'image/jpeg' })).toEqual(false)
|
|
238
|
+
expect(utils.allFilesAccepted({ files: images, multiple: true,accept:'image/*' })).toEqual(true)
|
|
247
239
|
expect(utils.allFilesAccepted({ files, multiple: true, minSize: 110 })).toEqual(false)
|
|
248
240
|
expect(utils.allFilesAccepted({ files, multiple: true, maxSize: 99 })).toEqual(false)
|
|
249
241
|
expect(utils.allFilesAccepted({ files, multiple: true, maxFiles: 1 })).toEqual(false)
|
|
250
242
|
})
|
|
251
243
|
})
|
|
252
244
|
|
|
253
|
-
function createFile(name, size, type) {
|
|
254
|
-
const file = new File([], name, { type })
|
|
255
|
-
Object.defineProperty(file, 'size', {
|
|
256
|
-
get() {
|
|
257
|
-
return size
|
|
258
|
-
}
|
|
259
|
-
})
|
|
260
|
-
return file
|
|
261
|
-
}
|
|
262
|
-
|
|
263
245
|
describe('ErrorCode', () => {
|
|
264
246
|
let utils
|
|
265
|
-
beforeEach(async
|
|
247
|
+
beforeEach(async () => {
|
|
266
248
|
utils = await import('./index')
|
|
267
|
-
done()
|
|
268
249
|
})
|
|
269
250
|
|
|
270
251
|
it('should exist and have known error code properties', () => {
|
|
@@ -275,3 +256,85 @@ describe('ErrorCode', () => {
|
|
|
275
256
|
})
|
|
276
257
|
})
|
|
277
258
|
|
|
259
|
+
describe('canUseFileSystemAccessAPI()', () => {
|
|
260
|
+
let utils
|
|
261
|
+
beforeEach(async () => {
|
|
262
|
+
utils = await import('./index')
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('should return false if not', () => {
|
|
266
|
+
expect(utils.canUseFileSystemAccessAPI()).toBe(false)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('should return true if yes', () => {
|
|
270
|
+
// TODO: If we use these in other tests, restore once test is done
|
|
271
|
+
window.showOpenFilePicker = jest.fn()
|
|
272
|
+
expect(utils.canUseFileSystemAccessAPI()).toBe(true)
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
describe('filePickerOptionsTypes()', () => {
|
|
277
|
+
let utils
|
|
278
|
+
beforeEach(async () => {
|
|
279
|
+
utils = await import('./index')
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it('should return proper types when the arg is a MIME type', () => {
|
|
283
|
+
expect(utils.filePickerOptionsTypes('application/zip')).toEqual([{
|
|
284
|
+
description: 'everything',
|
|
285
|
+
accept: {'application/zip': []}
|
|
286
|
+
}])
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
it('should return proper types when the arg is an array of MIME types', () => {
|
|
290
|
+
expect(utils.filePickerOptionsTypes(['application/zip', 'application/json'])).toEqual([{
|
|
291
|
+
description: 'everything',
|
|
292
|
+
accept: {
|
|
293
|
+
'application/zip': [],
|
|
294
|
+
'application/json': []
|
|
295
|
+
}
|
|
296
|
+
}])
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it("should exclude anything that's not a MIME type", () => {
|
|
300
|
+
expect(utils.filePickerOptionsTypes(['audio/*', 'video/*', 'image/*', '.txt', 'text/*'])).toEqual([{
|
|
301
|
+
description: 'everything',
|
|
302
|
+
accept: {
|
|
303
|
+
'audio/*': [],
|
|
304
|
+
'video/*': [],
|
|
305
|
+
'image/*': [],
|
|
306
|
+
'text/*': [],
|
|
307
|
+
}
|
|
308
|
+
}])
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it("should work with comma separated string of MIME types", () => {
|
|
312
|
+
expect(utils.filePickerOptionsTypes('audio/*,video/*,image/*,.txt,text/*,application/zip')).toEqual([{
|
|
313
|
+
description: 'everything',
|
|
314
|
+
accept: {
|
|
315
|
+
'audio/*': [],
|
|
316
|
+
'video/*': [],
|
|
317
|
+
'image/*': [],
|
|
318
|
+
'text/*': [],
|
|
319
|
+
'application/zip': []
|
|
320
|
+
}
|
|
321
|
+
}])
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('should return empty otherwise', () => {
|
|
325
|
+
expect(utils.filePickerOptionsTypes('')).toEqual([{
|
|
326
|
+
description: 'everything',
|
|
327
|
+
accept: {}
|
|
328
|
+
}])
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
function createFile(name, size, type) {
|
|
333
|
+
const file = new File([], name, { type })
|
|
334
|
+
Object.defineProperty(file, 'size', {
|
|
335
|
+
get() {
|
|
336
|
+
return size
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
return file
|
|
340
|
+
}
|
package/styleguide.config.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"parser": "@typescript-eslint/parser",
|
|
4
|
+
"env": {
|
|
5
|
+
"es6": true,
|
|
6
|
+
"browser": true
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
"import",
|
|
10
|
+
"prettier",
|
|
11
|
+
"react",
|
|
12
|
+
"react-hooks",
|
|
13
|
+
"@typescript-eslint"
|
|
14
|
+
],
|
|
15
|
+
"extends": [
|
|
16
|
+
"eslint:recommended",
|
|
17
|
+
"plugin:@typescript-eslint/recommended",
|
|
18
|
+
"prettier"
|
|
19
|
+
],
|
|
20
|
+
"settings": {
|
|
21
|
+
"react": {
|
|
22
|
+
"version": "detect"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"rules": {
|
|
26
|
+
"@typescript-eslint/no-explicit-any": "off"
|
|
27
|
+
}
|
|
28
|
+
}
|