From e8eb67d97dbfb1e41df3a58229041c434a28ce23 Mon Sep 17 00:00:00 2001 From: Alex Acebo <44811138+aacebo@users.noreply.github.com> Date: Tue, 17 Dec 2019 11:36:43 -0500 Subject: [PATCH] Aacebo/file upload button on push (#343) * remove console logs from spec file * move button markup to html files * fix typings and markup * make on-push * unit test file-button * remove unused var --- .../components/button/button.component.html | 8 + .../lib/components/button/button.component.ts | 15 +- .../button/file-button.component.html | 43 ++++++ .../button/file-button.component.spec.ts | 101 ++++++++++-- .../button/file-button.component.ts | 145 +++++++----------- .../toggle/toggle.component.spec.ts | 11 +- 6 files changed, 202 insertions(+), 121 deletions(-) create mode 100644 projects/swimlane/ngx-ui/src/lib/components/button/button.component.html create mode 100644 projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.html diff --git a/projects/swimlane/ngx-ui/src/lib/components/button/button.component.html b/projects/swimlane/ngx-ui/src/lib/components/button/button.component.html new file mode 100644 index 000000000..88d822f9c --- /dev/null +++ b/projects/swimlane/ngx-ui/src/lib/components/button/button.component.html @@ -0,0 +1,8 @@ + diff --git a/projects/swimlane/ngx-ui/src/lib/components/button/button.component.ts b/projects/swimlane/ngx-ui/src/lib/components/button/button.component.ts index 3f1719c39..8566bafc5 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/button/button.component.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/button/button.component.ts @@ -7,8 +7,7 @@ import { ButtonState } from './button-state.enum'; @Component({ selector: 'ngx-button', exportAs: 'ngxButton', - encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './button.component.html', styleUrls: ['./button.component.scss'], host: { class: 'ngx-button', @@ -18,16 +17,8 @@ import { ButtonState } from './button-state.enum'; '[class.fail]': 'fail$.value', '[class.disabled-button]': 'disabled' }, - template: ` - - ` + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush }) export class ButtonComponent implements OnInit, OnChanges { @Input() promise?: Promise; diff --git a/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.html b/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.html new file mode 100644 index 000000000..9cbef26cd --- /dev/null +++ b/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.html @@ -0,0 +1,43 @@ +
+ + +
+ +
+
+ + + + {{ fileName }} + +
+
+ +
diff --git a/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.spec.ts b/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.spec.ts index 4b065786a..262f7c8d6 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.spec.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.spec.ts @@ -1,31 +1,112 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { NO_ERRORS_SCHEMA, NgZone } from '@angular/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { FileUploader } from '@swimlane/ng2-file-upload'; + import { FileButtonComponent } from './file-button.component'; -import { FileButtonStyleType } from './file-button-style.type'; + describe('FileButtonComponent', () => { let component: FileButtonComponent; let fixture: ComponentFixture; + beforeEach(() => { TestBed.configureTestingModule({ schemas: [NO_ERRORS_SCHEMA], declarations: [FileButtonComponent] }); + }); + + beforeEach(() => { fixture = TestBed.createComponent(FileButtonComponent); component = fixture.componentInstance; + component.uploader = new FileUploader({ }); + component.disabled = false; + component.multiple = false; + fixture.detectChanges(); }); + it('can load instance', () => { expect(component).toBeTruthy(); }); - it('styleType defaults to: FileButtonStyleType.standard', () => { - expect(component.styleType).toEqual(FileButtonStyleType.standard); + + describe('ngOnInit', () => { + beforeEach(() => { + fixture = TestBed.createComponent(FileButtonComponent); + component = fixture.componentInstance; + }); + + it('should throw error if !uploader and !options', () => { + let err: Error; + + try { + fixture.detectChanges() + } catch (ex) { + err = ex; + } + + expect(err).toBeDefined(); + }); + + it('should create new uploader if !uploader and options', () => { + component.options = { }; + fixture.detectChanges(); + expect(component.uploader).toBeDefined(); + }); + }); + + describe('onAfterAddingFile', () => { + it('should set filename and emit event', () => { + const spy = spyOn(component.afterAddingFile, 'emit'); + component.onAfterAddingFile({ file: { name: 'test' } } as any); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('onBeforeUploadItem', () => { + it('should emit event', () => { + const spy = spyOn(component.beforeUploadItem, 'emit'); + component.onBeforeUploadItem({ } as any); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('onErrorItem', () => { + it('should emit event', () => { + const spy = spyOn(component.errorItem, 'emit'); + component.onErrorItem('test', 500, { }); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('onProgressAll', () => { + it('should change progress and emit event', () => { + const spy = spyOn(component.progressAll, 'emit'); + component.onProgressAll(100); + expect(component.progress).toEqual(100); + expect(spy).toHaveBeenCalled(); + }); }); - it('isItemSuccessful defaults to: false', () => { - expect(component.isItemSuccessful).toEqual(false); + + describe('onSuccessItem', () => { + it('should emit event', () => { + const spy = spyOn(component.successItem, 'emit'); + component.onSuccessItem({ }, 'test', 200, { }); + expect(spy).toHaveBeenCalled(); + }); }); - it('progress defaults to: 0%', () => { - expect(component.progress).toEqual('0%'); + + describe('fileOverBase', () => { + it('should set dropzone state', () => { + component.fileOverBase(true); + expect(component.fileOverDropzone).toBeTruthy(); + component.fileOverBase(false); + expect(component.fileOverDropzone).toBeFalsy(); + }); }); - it('fileOverDropzone defaults to: false', () => { - expect(component.fileOverDropzone).toEqual(false); + + describe('clearInput', () => { + it('should clear input value', () => { + component.clearInput(); + expect(component.fileInput.nativeElement.value).toBe(''); + }); }); }); diff --git a/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.ts b/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.ts index f5642bd38..ee6b8344f 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/button/file-button.component.ts @@ -9,97 +9,54 @@ import { ContentChild, TemplateRef, ViewChild, - ElementRef + ElementRef, + ChangeDetectionStrategy } from '@angular/core'; -import { FileUploaderOptions, FileUploader } from '@swimlane/ng2-file-upload'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { FileUploaderOptions, FileUploader, FileItem } from '@swimlane/ng2-file-upload'; + import { FileButtonStyleType } from './file-button-style.type'; let nextId = 0; @Component({ + exportAs: 'ngxFileButton', selector: 'ngx-file-button', - encapsulation: ViewEncapsulation.None, + templateUrl: './file-button.component.html', styleUrls: ['./file-button.component.scss'], - template: ` -
- - -
- -
-
- - - - {{ fileName }} - -
-
- -
- ` + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush }) export class FileButtonComponent implements OnInit { - @Input() - id: string = `input-${++nextId}`; - @Input() - name: string; - @Input() - disabled: boolean; - @Input() - multiple: boolean; - @Input() - styleType: FileButtonStyleType = FileButtonStyleType.standard; + @Input() id: string = `input-${++nextId}`; + @Input() name: string; + @Input() styleType = FileButtonStyleType.standard; + @Input() uploader: FileUploader; + @Input() options: FileUploaderOptions; - // you can pass either options - // or a instance of the uploader @Input() - uploader: FileUploader; + get disabled() { return this._disabled; } + set disabled(disabled) { + this._disabled = coerceBooleanProperty(disabled); + } + @Input() - options: FileUploaderOptions; - - @Output() - afterAddingFile = new EventEmitter(); - @Output() - beforeUploadItem = new EventEmitter(); - @Output() - successItem = new EventEmitter(); - @Output() - errorItem = new EventEmitter(); - @Output() - progressAll = new EventEmitter(); + get multiple() { return this._multiple; } + set multiple(multiple) { + this._multiple = coerceBooleanProperty(multiple); + } + + @Output() afterAddingFile = new EventEmitter<{ fileItem: FileItem }>(); + @Output() beforeUploadItem = new EventEmitter<{ fileItem: FileItem }>(); + @Output() successItem = new EventEmitter<{ item: any; response: string; status: number; headers: any }>(); + @Output() errorItem = new EventEmitter<{ response: string; status: number; headers: any }>(); + @Output() progressAll = new EventEmitter<{ progress: number }>(); @ContentChild('dropzoneTemplate', { static: false }) - dropzoneTemplate: TemplateRef; + readonly dropzoneTemplate: TemplateRef; + @ViewChild('fileInput', { static: false }) - fileInput: ElementRef; + readonly fileInput?: ElementRef; get isDisabled(): boolean { return this.disabled || this.uploader.isUploading; @@ -111,20 +68,24 @@ export class FileButtonComponent implements OnInit { 'standard-style': this.styleType === FileButtonStyleType.standard, 'progress-style': this.styleType === FileButtonStyleType.progress, 'show-progress': this.uploader && this.uploader.options.isHTML5, - success: this.isItemSuccessful, + success: this._isItemSuccessful, active: this.uploader && this.uploader.isUploading }; } - isItemSuccessful: boolean = false; - progress: string = '0%'; + readonly FileButtonStyleType = FileButtonStyleType; + progress: number = 0; fileName: string = ''; fileOverDropzone: boolean = false; - constructor(private ngZone: NgZone) {} + private _isItemSuccessful: boolean = false; + private _disabled: boolean = false; + private _multiple: boolean = false; + + constructor(private readonly _ngZone: NgZone) { } ngOnInit(): void { - this.ngZone.run(() => { + this._ngZone.run(() => { if (!this.uploader && !this.options) { throw new Error('You must pass either an uploader instance or options.'); } @@ -145,15 +106,15 @@ export class FileButtonComponent implements OnInit { }); } - onAfterAddingFile(fileItem): void { - this.ngZone.run(() => { + onAfterAddingFile(fileItem: FileItem): void { + this._ngZone.run(() => { this.fileName = fileItem.file.name; this.afterAddingFile.emit({ fileItem }); }); } - onBeforeUploadItem(fileItem) { - this.ngZone.run(() => { + onBeforeUploadItem(fileItem: FileItem) { + this._ngZone.run(() => { this.beforeUploadItem.emit({ fileItem }); }); } @@ -162,27 +123,27 @@ export class FileButtonComponent implements OnInit { this.errorItem.emit({ response, status, headers }); } - onProgressAll(progress): void { - this.ngZone.run(() => { - this.progress = progress + '%'; + onProgressAll(progress: number): void { + this._ngZone.run(() => { + this.progress = progress; this.progressAll.emit({ progress }); }); } - onSuccessItem(item, response, status, headers): void { - this.ngZone.run(() => { - this.isItemSuccessful = true; + onSuccessItem(item: any, response: string, status: number, headers: any): void { + this._ngZone.run(() => { + this._isItemSuccessful = true; setTimeout(() => { this.fileName = ''; - this.isItemSuccessful = false; + this._isItemSuccessful = false; }, 2500); this.successItem.emit({ item, response, status, headers }); }); } - fileOverBase(event) { + fileOverBase(event: boolean) { this.fileOverDropzone = event; } diff --git a/projects/swimlane/ngx-ui/src/lib/components/toggle/toggle.component.spec.ts b/projects/swimlane/ngx-ui/src/lib/components/toggle/toggle.component.spec.ts index 25155ac03..fab1a6779 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/toggle/toggle.component.spec.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/toggle/toggle.component.spec.ts @@ -53,10 +53,9 @@ describe('ToggleComponent', () => { expect(component.getDisabled).toEqual('disabled'); }); - it('can register on change callback', () => { - spyOn(console, 'log').and.callThrough(); + it('can register on change callback', (done) => { const changeCallback = () => { - console.log('changed'); + done(); }; component.registerOnChange(changeCallback); @@ -71,15 +70,13 @@ describe('ToggleComponent', () => { expect(component.value).toEqual(false); }); - it('onBlur calls registered touch callback', () => { - spyOn(console, 'log').and.callThrough(); + it('onBlur calls registered touch callback', (done) => { const touchCallback = () => { - console.log('changed'); + done(); }; component.registerOnTouched(touchCallback); component.onBlur(); - expect(console.log).toHaveBeenCalled(); }); it('toggle flips the toggle value', () => {