diff options
Diffstat (limited to 'Timeline/ClientApp/src/app/utilities')
3 files changed, 174 insertions, 0 deletions
diff --git a/Timeline/ClientApp/src/app/utilities/debounce-click.directive.spec.ts b/Timeline/ClientApp/src/app/utilities/debounce-click.directive.spec.ts new file mode 100644 index 00000000..75710d0c --- /dev/null +++ b/Timeline/ClientApp/src/app/utilities/debounce-click.directive.spec.ts @@ -0,0 +1,124 @@ +import { Component, ViewChild } from '@angular/core'; +import { async, TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; + +import { DebounceClickDirective } from './debounce-click.directive'; + +interface TestComponent { + clickHandler: () => void; +} + +@Component({ + selector: 'app-default-test', + template: '<button (appDebounceClick)="clickHandler()"></button>' +}) +class DefaultDebounceTimeTestComponent { + @ViewChild(DebounceClickDirective) + directive: DebounceClickDirective; + + clickHandler: () => void = () => { }; +} + +@Component({ + selector: 'app-default-test', + template: '<button (appDebounceClick)="clickHandler()" [appDebounceClickTime]="debounceTime"></button>' +}) +class CustomDebounceTimeTestComponent { + debounceTime: number; + + @ViewChild(DebounceClickDirective) + directive: DebounceClickDirective; + + clickHandler: () => void = () => { }; +} + + +describe('DebounceClickDirective', () => { + let counter: number; + + function initComponent(component: TestComponent) { + component.clickHandler = () => counter++; + } + + beforeEach(() => { + counter = 0; + }); + + describe('default debounce time', () => { + let component: DefaultDebounceTimeTestComponent; + let componentFixture: ComponentFixture<DefaultDebounceTimeTestComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [DebounceClickDirective, DefaultDebounceTimeTestComponent] + }).compileComponents(); + })); + + beforeEach(() => { + componentFixture = TestBed.createComponent(DefaultDebounceTimeTestComponent); + component = componentFixture.componentInstance; + initComponent(component); + }); + + it('should create an instance', () => { + componentFixture.detectChanges(); + expect(component.directive).toBeTruthy(); + }); + + it('should work well', fakeAsync(() => { + function click() { + (<HTMLButtonElement>componentFixture.debugElement.query(By.css('button')).nativeElement).dispatchEvent(new MouseEvent('click')); + } + componentFixture.detectChanges(); + expect(counter).toBe(0); + click(); + tick(300); + expect(counter).toBe(0); + click(); + tick(); + expect(counter).toBe(0); + tick(500); + expect(counter).toBe(1); + })); + }); + + + describe('custom debounce time', () => { + let component: CustomDebounceTimeTestComponent; + let componentFixture: ComponentFixture<CustomDebounceTimeTestComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [DebounceClickDirective, CustomDebounceTimeTestComponent] + }).compileComponents(); + })); + + beforeEach(() => { + componentFixture = TestBed.createComponent(CustomDebounceTimeTestComponent); + component = componentFixture.componentInstance; + initComponent(component); + component.debounceTime = 600; + }); + + it('should create an instance', () => { + componentFixture.detectChanges(); + expect(component.directive).toBeTruthy(); + }); + + it('should work well', fakeAsync(() => { + function click() { + (<HTMLButtonElement>componentFixture.debugElement.query(By.css('button')).nativeElement).dispatchEvent(new MouseEvent('click')); + } + componentFixture.detectChanges(); + expect(counter).toBe(0); + click(); + tick(300); + expect(counter).toBe(0); + click(); + tick(); + expect(counter).toBe(0); + tick(600); + expect(counter).toBe(1); + })); + }); +}); diff --git a/Timeline/ClientApp/src/app/utilities/debounce-click.directive.ts b/Timeline/ClientApp/src/app/utilities/debounce-click.directive.ts new file mode 100644 index 00000000..feb0404e --- /dev/null +++ b/Timeline/ClientApp/src/app/utilities/debounce-click.directive.ts @@ -0,0 +1,39 @@ +import { Directive, Output, Input, EventEmitter, ElementRef, OnInit, OnDestroy } from '@angular/core'; +import { fromEvent, Subscription } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; + +@Directive({ + selector: '[appDebounceClick]' +}) +export class DebounceClickDirective implements OnInit, OnDestroy { + + private subscription: Subscription; + + @Output('appDebounceClick') clickEvent = new EventEmitter<any>(); + + // tslint:disable-next-line:no-input-rename + @Input('appDebounceClickTime') + set debounceTime(value: number) { + if (this.subscription) { + this.subscription.unsubscribe(); + } + this.subscription = fromEvent(<HTMLElement>this.element.nativeElement, 'click').pipe( + debounceTime(value) + ).subscribe(o => this.clickEvent.emit(o)); + } + + constructor(private element: ElementRef) { + } + + ngOnInit() { + if (!this.subscription) { + this.subscription = fromEvent(<HTMLElement>this.element.nativeElement, 'click').pipe( + debounceTime(500) + ).subscribe(o => this.clickEvent.emit(o)); + } + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } +} diff --git a/Timeline/ClientApp/src/app/utilities/utility.module.ts b/Timeline/ClientApp/src/app/utilities/utility.module.ts new file mode 100644 index 00000000..dd686bf7 --- /dev/null +++ b/Timeline/ClientApp/src/app/utilities/utility.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { DebounceClickDirective } from './debounce-click.directive'; + +@NgModule({ + declarations: [DebounceClickDirective], + imports: [CommonModule], + exports: [DebounceClickDirective] +}) +export class UtilityModule { } |