diff --git a/docs/validation.md b/docs/validation.md index a62a16194..50cdc2826 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -10,13 +10,9 @@ For more details see the [MDN accept attribute reference](https://developer.mozi ## Custom validation -Both components provide callback arguments with which you may implement custom validation of chosen files. +Both `` and `` components accept the `@filter` argument with which you may implement custom validation of chosen files. -`` may be passed `@onSelect` and `` may be passed `@onDrop`. - -These are called *after* files have been chosen and *before* `@onFileAdd` is called by the queue. - -To implement validation, you may (optionally) return a subset of files from these callbacks to restrict which files are queued for upload. +This are called *after* files have been chosen and *before* `fileAdded` is called by the queue. See the example below where the same validation callback is used for both selection methods. @@ -24,15 +20,14 @@ Commonly validated file properties are `type`, `name` and `size`. For more detai ```hbs ... ... @@ -50,8 +45,8 @@ const allowedTypes = [ export default class ExampleComponent extends Component { ... - validateFiles(files) { - return files.filter((file) => allowedTypes.includes(file.type)); + validateFile(file) { + return allowedTypes.includes(file.type); } } ``` diff --git a/ember-file-upload/addon/components/file-dropzone.ts b/ember-file-upload/addon/components/file-dropzone.ts index aefbc29c1..8b2bd8038 100644 --- a/ember-file-upload/addon/components/file-dropzone.ts +++ b/ember-file-upload/addon/components/file-dropzone.ts @@ -36,7 +36,7 @@ interface FileDropzoneArgs { /** * Called when files have entered the dropzone. */ - filesEnter?: ( + onDragEnter?: ( files: File[] | DataTransferItem[], dataTransfer: DataTransferWrapper ) => void; @@ -44,7 +44,7 @@ interface FileDropzoneArgs { /** * Called when files have left the dropzone. */ - filesLeave?: ( + onDragLeave?: ( files: File[] | DataTransferItem[], dataTransfer: DataTransferWrapper ) => void; @@ -52,7 +52,7 @@ interface FileDropzoneArgs { /** * Called when file have been dropped on the dropzone. */ - filesDropped?: (files: UploadFile[]) => void; + onDrop?: (files: UploadFile[], dataTransfer: DataTransferWrapper) => void; // old/deprecated API @@ -85,31 +85,7 @@ interface FileDropzoneArgs { for?: string; /** - * @deprecated use `filesEnter()` instead - */ - onDragEnter?: ( - files: File[] | DataTransferItem[], - dataTransfer: DataTransferWrapper - ) => void; - - /** - * @deprecated use `filesLeave()` instead - */ - onDragLeave?: ( - files: File[] | DataTransferItem[], - dataTransfer: DataTransferWrapper - ) => void; - - /** - * @deprecated use `filesDropped()` instead - */ - onDrop?: ( - files: File[] | DataTransferItem[], - dataTransfer: DataTransferWrapper - ) => void; - - /** - * @deprecated use `filesDropped()` instead + * @deprecated use `onDrop()` instead */ onFileAdd: (file: UploadFile) => void; } @@ -205,7 +181,6 @@ export default class FileDropzoneComponent extends Component { this.active = true; this.args.onDragEnter?.(this.files, this.dataTransferWrapper); - this.args.filesEnter?.(this.files, this.dataTransferWrapper); } } @@ -219,7 +194,6 @@ export default class FileDropzoneComponent extends Component { event.dataTransfer.dropEffect = this.cursor; } this.args.onDragLeave?.(this.files, this.dataTransferWrapper); - this.args.filesLeave?.(this.files, this.dataTransferWrapper); this.dataTransferWrapper = undefined; @@ -326,13 +300,8 @@ export default class FileDropzoneComponent extends Component { // } if (this.dataTransferWrapper) { - // @TODO stop filtering files based on the output of onDrop - // in favor of `filter()` - it never was officially public API - const files = - this.args.onDrop?.(this.files, this.dataTransferWrapper) ?? this.files; - - const addedFiles = this.addFiles(files); - this.args.filesDropped?.(addedFiles); + const addedFiles = this.addFiles(this.files); + this.args.onDrop?.(addedFiles, this.dataTransferWrapper); this.active = false; this.dataTransferWrapper = undefined; diff --git a/ember-file-upload/addon/components/file-upload.ts b/ember-file-upload/addon/components/file-upload.ts index 8fc400202..e0a690e2e 100644 --- a/ember-file-upload/addon/components/file-upload.ts +++ b/ember-file-upload/addon/components/file-upload.ts @@ -50,13 +50,6 @@ interface FileUploadArgs { * @deprecated use `filesSelected()` instead */ onFileAdd: (file: UploadFile) => void; - - // @TODO remove `onSelect` in favor of `filter()` - it never was officially - // of public API - /** - * @deprecated use `filter()` instead - */ - onSelect?: (files: UploadFile[]) => UploadFile[]; } /** diff --git a/ember-file-upload/tests/integration/components/file-dropzone-test.js b/ember-file-upload/tests/integration/components/file-dropzone-test.js index ed5cbc6ea..e81b677ee 100644 --- a/ember-file-upload/tests/integration/components/file-dropzone-test.js +++ b/ember-file-upload/tests/integration/components/file-dropzone-test.js @@ -18,49 +18,30 @@ module('Integration | Component | FileDropzone', function (hooks) { this.queue = new Queue({ name: 'test', fileQueue: fileQueueService }); }); - test('filesEnter', async function (assert) { - this.filesEnter = (files) => - files.forEach((file) => assert.step(file.name)); - await render(hbs` - - `); - - await dragEnter('.test-dropzone', new File([], 'dingus.txt')); - - assert.verifySteps(['dingus.txt']); - }); + test('onDragEnter is called when a file is dragged over', async function (assert) { + this.onDragEnter = () => assert.step('onDragEnter'); - test('filesLeave', async function (assert) { - this.filesLeave = (files) => - files.forEach((file) => assert.step(file.name)); await render(hbs` + @name="test" + @onDragEnter={{this.onDragEnter}} /> `); - await dragEnter('.test-dropzone', new File([], 'dingus.txt')); - await dragLeave('.test-dropzone', new File([], 'dingus.txt')); + await dragEnter('.test-dropzone'); - assert.verifySteps(['dingus.txt']); + assert.verifySteps(['onDragEnter']); }); - test('filter and filesDropped', async function (assert) { + test('filter and onDrop', async function (assert) { this.filter = (file) => file.name.includes('.txt'); - this.filesDropped = (files) => - files.forEach((file) => assert.step(file.name)); + this.onDrop = (files) => files.forEach((file) => assert.step(file.name)); await render(hbs` `); @@ -73,9 +54,7 @@ module('Integration | Component | FileDropzone', function (hooks) { assert.verifySteps(['dingus.txt', 'dongus.txt']); }); - }); - module('deprecated api', function () { test('dropping a file calls onDrop', async function (assert) { this.onDrop = (files) => files.forEach((file) => assert.step(file.name)); @@ -91,35 +70,55 @@ module('Integration | Component | FileDropzone', function (hooks) { assert.verifySteps(['dingus.txt']); }); - // @TODO stop filtering files based on the output of onDrop - // in favor of `filter()` - it never was officially public API - test('only calls onFileAdd for files returned from onDrop', async function (assert) { - this.onDrop = (files) => { - assert.step(`onDrop: ${files.mapBy('name').join(',')}`); - return files.filter((f) => f.type.split('/')[0] === 'text'); - }; - this.onFileAdd = (file) => assert.step(`onFileAdd: ${file.name}`); + test('onDragLeave is called when a file is dragged out', async function (assert) { + this.onDragLeave = () => assert.step('onDragLeave'); await render(hbs` + @onDragLeave={{this.onDragLeave}} /> + `); + + await dragEnter('.test-dropzone', new File([], 'dingus.txt')); + await dragLeave('.test-dropzone', new File([], 'dingus.txt')); + + assert.verifySteps(['onDragLeave']); + }); + + test('yielded properties', async function (assert) { + await render(hbs` + +
{{dropzone.supported}}
+
{{dropzone.active}}
+
{{queue.name}}
+
+ `); + + assert.dom('.supported').hasText('true'); + assert.dom('.active').hasText('false'); + assert.dom('.queue-name').hasText('test'); + }); + }); + + module('deprecated api', function () { + test('dropping multiple files calls onFileAdd with each file', async function (assert) { + this.onFileAdd = (file) => assert.step(file.name); + + await render(hbs` + `); await dragAndDrop( '.test-dropzone', - new File([], 'dingus.html', { type: 'text/html' }), - new File([], 'dingus.png', { type: 'image/png' }) + new File([], 'dingus.txt'), + new File([], 'dingus.png') ); - assert.verifySteps([ - 'onDrop: dingus.html,dingus.png', - 'onFileAdd: dingus.html', - ]); + assert.verifySteps(['dingus.txt', 'dingus.png']); }); test('dropping multiple files calls onDrop with both files', async function (assert) { @@ -180,50 +179,5 @@ module('Integration | Component | FileDropzone', function (hooks) { assert.verifySteps(['dingus.txt']); }); - - test('onDragEnter is called when a file is dragged over', async function (assert) { - this.onDragEnter = () => assert.step('onDragEnter'); - - await render(hbs` - - `); - - await dragEnter('.test-dropzone'); - - assert.verifySteps(['onDragEnter']); - }); - - test('onDragLeave is called when a file is dragged out', async function (assert) { - this.onDragLeave = () => assert.step('onDragLeave'); - - await render(hbs` - - `); - - await dragEnter('.test-dropzone', new File([], 'dingus.txt')); - await dragLeave('.test-dropzone', new File([], 'dingus.txt')); - - assert.verifySteps(['onDragLeave']); - }); - - test('yielded properties', async function (assert) { - await render(hbs` - -
{{dropzone.supported}}
-
{{dropzone.active}}
-
{{queue.name}}
-
- `); - - assert.dom('.supported').hasText('true'); - assert.dom('.active').hasText('false'); - assert.dom('.queue-name').hasText('test'); - }); }); });