diff --git a/lib/content-services/src/lib/upload/directives/file-draggable.directive.spec.ts b/lib/content-services/src/lib/upload/directives/file-draggable.directive.spec.ts index 08f59cae4e..52925a9d9c 100644 --- a/lib/content-services/src/lib/upload/directives/file-draggable.directive.spec.ts +++ b/lib/content-services/src/lib/upload/directives/file-draggable.directive.spec.ts @@ -15,108 +15,176 @@ * limitations under the License. */ -import { ElementRef } from '@angular/core'; -import { FileDraggableDirective } from '../directives/file-draggable.directive'; +import { Component, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FileDraggableDirective, INPUT_FOCUS_CSS_CLASS } from '../directives/file-draggable.directive'; + +@Component({ + selector: 'adf-test-component', + template: ` +
+
+
+ ` +}) +class TestComponent { + @ViewChild(FileDraggableDirective, { static: true }) + directive: FileDraggableDirective; +} describe('FileDraggableDirective', () => { + let fixture: ComponentFixture; + let component: TestComponent; + let directive: FileDraggableDirective; - let component: FileDraggableDirective; + let testContainer: HTMLDivElement; + let testContent: HTMLDivElement; - beforeEach( () => { - const el = new ElementRef(null); - component = new FileDraggableDirective(el, null); + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + TestComponent, + FileDraggableDirective + ] + }); + + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + directive = component.directive; + + fixture.detectChanges(); + testContainer = fixture.nativeElement.querySelector('#test-container'); + testContent = fixture.nativeElement.querySelector('#test-content'); }); + afterEach(() => { + fixture.destroy(); + }); + + function createEvent(eventName: string): DragEvent { + return new DragEvent(eventName, { + bubbles: true, + cancelable: true, + dataTransfer: new DataTransfer() + }); + } + + function raiseEvent(eventName: string): DragEvent { + const event = createEvent(eventName); + testContent.dispatchEvent(event); + return event; + } + it('should always be enabled by default', () => { - expect(component.enabled).toBeTruthy(); + expect(directive.enabled).toBeTruthy(); }); - it('should not allow drag and drop when disabled', () => { - component.enabled = false; - const event = new DragEvent('custom-event'); - spyOn(event, 'preventDefault').and.stub(); - component.onDropFiles(event); - component.onDragEnter(event); - component.onDragLeave(event); - expect(event.preventDefault).not.toHaveBeenCalled(); + it('should add focus style on [dragenter]', () => { + raiseEvent('dragenter'); + expect(testContainer.classList.contains(INPUT_FOCUS_CSS_CLASS)).toBeTruthy(); }); - /* - it('should emit onFolderEntityDropped event when a folder is dragged with Chrome' , (done) => { + it('should add focus style on [dragenter] only if not already handled', () => { + const dragEvent = createEvent('dragenter'); + dragEvent.preventDefault(); - let itemEntity = { - fullPath: '/folder-fake', - isDirectory: true, - isFile: false, - name: 'folder-fake' - }; - let fakeEvent = { - dataTransfer: { - items: [{ - webkitGetAsEntry: () => { - return itemEntity; - } - }] - }, - stopPropagation: jasmine.createSpy('stopPropagation'), - preventDefault: jasmine.createSpy('preventDefault') - }; - - component.onFolderEntityDropped.subscribe(files => { - expect(files).toEqual(itemEntity); - expect(component.getInputFocus()).toBe(false); - done(); - }); - - component.onDropFiles(fakeEvent); + testContainer.dispatchEvent(dragEvent); + expect(testContainer.classList.contains(INPUT_FOCUS_CSS_CLASS)).toBeFalsy(); }); - it('should emit onFilesDropped event when a file is dragged not with Chrome' , (done) => { - let file = {name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json'}; - let fakeEvent = { - dataTransfer: { - files: [file] - }, - stopPropagation: jasmine.createSpy('stopPropagation'), - preventDefault: jasmine.createSpy('preventDefault') - }; + it('should add focus style only on [dragover]', () => { + raiseEvent('dragover'); + expect(testContainer.classList.contains(INPUT_FOCUS_CSS_CLASS)).toBeTruthy(); - component.onFilesDropped.subscribe(files => { - expect(files).toEqual([file]); - expect(component.getInputFocus()).toBe(false); - done(); - }); - - component.onDropFiles(fakeEvent); + raiseEvent('dragleave'); + expect(testContainer.classList.contains(INPUT_FOCUS_CSS_CLASS)).toBeFalsy(); }); - it('should emit onFilesDropped event when a file is dragged with Chrome', (done) => { - let file = {name: 'fake-name-2', size: 10, webkitRelativePath: 'fake-folder1/fake-name-2.json'}; - let fakeEvent = { - dataTransfer: { - items: [''], - files: [file] - }, - stopPropagation: jasmine.createSpy('stopPropagation'), - preventDefault: jasmine.createSpy('preventDefault') - }; + it('should remove focus style after [drop] of files', () => { + raiseEvent('dragover'); + expect(testContainer.classList.contains(INPUT_FOCUS_CSS_CLASS)).toBeTruthy(); - component.onFilesDropped.subscribe(files => { - expect(files).toEqual([file]); - expect(component.getInputFocus()).toBe(false); - done(); - }); - - component.onDropFiles(fakeEvent); + raiseEvent('drop'); + expect(testContainer.classList.contains(INPUT_FOCUS_CSS_CLASS)).toBeFalsy(); }); - it('should take the focus when the drag enter is called', () => { - let mockEvent = new Event('dragstart'); - spyOn(mockEvent, 'preventDefault'); + it('should add focus style on [dragover] only if not already handled', () => { + const dragEvent = createEvent('dragover'); + dragEvent.preventDefault(); - expect(component.getInputFocus()).toBe(false); - component.onDragEnter(mockEvent); - expect(component.getInputFocus()).toBe(true); + testContainer.dispatchEvent(dragEvent); + expect(testContainer.classList.contains(INPUT_FOCUS_CSS_CLASS)).toBeFalsy(); + }); + + it('should prevent [drop] event propagation after handling', () => { + const dragEvent = createEvent('drop'); + expect(dragEvent.defaultPrevented).toBeFalsy(); + + testContainer.dispatchEvent(dragEvent); + expect(dragEvent.defaultPrevented).toBeTruthy(); + }); + + it('should not prevent [drop] event propagation if component is disabled', () => { + component.directive.enabled = false; + + const dragEvent = createEvent('drop'); + expect(dragEvent.defaultPrevented).toBeFalsy(); + + testContainer.dispatchEvent(dragEvent); + expect(dragEvent.defaultPrevented).toBeFalsy(); + }); + + it('should prevent [dragenter] event propagation after handling', () => { + const dragEvent = createEvent('dragenter'); + expect(dragEvent.defaultPrevented).toBeFalsy(); + + testContainer.dispatchEvent(dragEvent); + expect(dragEvent.defaultPrevented).toBeTruthy(); + }); + + it('should not prevent [dragenter] event propagation if component is disabled', () => { + component.directive.enabled = false; + + const dragEvent = createEvent('dragenter'); + expect(dragEvent.defaultPrevented).toBeFalsy(); + + testContainer.dispatchEvent(dragEvent); + expect(dragEvent.defaultPrevented).toBeFalsy(); + }); + + it('should prevent [dragleave] event propagation after hanlding', () => { + const dragEvent = createEvent('dragleave'); + expect(dragEvent.defaultPrevented).toBeFalsy(); + + testContainer.dispatchEvent(dragEvent); + expect(dragEvent.defaultPrevented).toBeTruthy(); + }); + + it('should not prevent [dragleave] event propagation if component is disabled', () => { + component.directive.enabled = false; + + const dragEvent = createEvent('dragleave'); + expect(dragEvent.defaultPrevented).toBeFalsy(); + + testContainer.dispatchEvent(dragEvent); + expect(dragEvent.defaultPrevented).toBeFalsy(); + }); + + it('should prevent [dragover] event propagation after handling', () => { + const dragEvent = createEvent('dragover'); + expect(dragEvent.defaultPrevented).toBeFalsy(); + + testContainer.dispatchEvent(dragEvent); + expect(dragEvent.defaultPrevented).toBeTruthy(); + }); + + it('should not prevent [dragover] event propagation if component is disabled', () => { + component.directive.enabled = false; + + const dragEvent = createEvent('dragover'); + expect(dragEvent.defaultPrevented).toBeFalsy(); + + testContainer.dispatchEvent(dragEvent); + expect(dragEvent.defaultPrevented).toBeFalsy(); }); - */ }); diff --git a/lib/content-services/src/lib/upload/directives/file-draggable.directive.ts b/lib/content-services/src/lib/upload/directives/file-draggable.directive.ts index f9db3e9c03..ea5e81de74 100644 --- a/lib/content-services/src/lib/upload/directives/file-draggable.directive.ts +++ b/lib/content-services/src/lib/upload/directives/file-draggable.directive.ts @@ -20,6 +20,9 @@ import { FileUtils } from '@alfresco/adf-core'; import { Directive, ElementRef, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from '@angular/core'; +export const INPUT_FOCUS_CSS_CLASS = 'adf-file-draggable-input-focus'; +export const DROP_EFFECT = 'copy'; + @Directive({ selector: '[adf-file-draggable]' }) @@ -39,7 +42,6 @@ export class FileDraggableDirective implements OnInit, OnDestroy { @Output() folderEntityDropped = new EventEmitter(); - private cssClassName = 'adf-file-draggable__input-focus'; private element: HTMLElement; constructor(el: ElementRef, private ngZone: NgZone) { @@ -71,7 +73,7 @@ export class FileDraggableDirective implements OnInit, OnDestroy { this.preventDefault(event); // Chrome, Edge, Firefox, Opera (Files + Folders) - const items = event.dataTransfer.items; + const items = event.dataTransfer?.items; if (items) { const files: File[] = []; @@ -95,13 +97,13 @@ export class FileDraggableDirective implements OnInit, OnDestroy { if (files.length > 0) { this.filesDropped.emit(files); } - } else { + } else if (event.dataTransfer?.files) { // IE, Safari, Chrome, Edge, Firefox, Opera (Files only) const files = FileUtils.toFileArray(event.dataTransfer.files); this.filesDropped.emit(files); } - this.element.classList.remove(this.cssClassName); + this.element.classList.remove(INPUT_FOCUS_CSS_CLASS); } } @@ -113,8 +115,12 @@ export class FileDraggableDirective implements OnInit, OnDestroy { onDragEnter(event: DragEvent): void { if (this.enabled && !event.defaultPrevented) { this.preventDefault(event); - event.dataTransfer.dropEffect = 'copy'; - this.element.classList.add(this.cssClassName); + + if (event.dataTransfer) { + event.dataTransfer.dropEffect = DROP_EFFECT; + } + + this.element.classList.add(INPUT_FOCUS_CSS_CLASS); } } @@ -126,7 +132,7 @@ export class FileDraggableDirective implements OnInit, OnDestroy { onDragLeave(event: Event): void { if (this.enabled && !event.defaultPrevented) { this.preventDefault(event); - this.element.classList.remove(this.cssClassName); + this.element.classList.remove(INPUT_FOCUS_CSS_CLASS); } } @@ -138,8 +144,12 @@ export class FileDraggableDirective implements OnInit, OnDestroy { onDragOver(event: DragEvent): void { if (this.enabled && !event.defaultPrevented) { this.preventDefault(event); - event.dataTransfer.dropEffect = 'copy'; - this.element.classList.add(this.cssClassName); + + if (event.dataTransfer) { + event.dataTransfer.dropEffect = DROP_EFFECT; + } + + this.element.classList.add(INPUT_FOCUS_CSS_CLASS); } }