import {
DeleteConfirmationComponent
} from './delete-confirmation-modal/delete-confirmation-modal.component';
-import { DeletionLinkComponent } from './deletion-link/deletion-link.component';
+import { DeletionModalComponent } from './deletion-modal/deletion-modal.component';
import { HelperComponent } from './helper/helper.component';
import { ModalComponent } from './modal/modal.component';
import { SparklineComponent } from './sparkline/sparkline.component';
UsageBarComponent,
DeleteConfirmationComponent,
ModalComponent,
- DeletionLinkComponent
+ DeletionModalComponent
+ ],
+ entryComponents: [
+ DeletionModalComponent
],
providers: [],
exports: [
entryComponents: [
DeleteConfirmationComponent,
ModalComponent,
- DeletionLinkComponent
+ DeletionModalComponent
]
})
export class ComponentsModule { }
+++ /dev/null
-<a (click)="showModal(deletionModal)">
- <i class="fa fa-fw fa-trash-o"></i>
- <ng-container *ngTemplateOutlet="deletionHeading"></ng-container>
-</a>
-
-<ng-template #deletionModal>
- <cd-modal #modal
- [modalRef]="bsModalRef">
- <ng-container class="modal-title">
- <ng-container *ngTemplateOutlet="deletionHeading"></ng-container>
- </ng-container>
-
- <ng-container class="modal-content">
- <ng-container *ngTemplateOutlet="deletionContent"></ng-container>
- </ng-container>
- </cd-modal>
-</ng-template>
-
-<ng-template #deletionContent>
- <form name="deletionForm"
- #formDir="ngForm"
- (submit)="delete()"
- [formGroup]="deletionForm"
- novalidate>
- <div class="modal-body">
- <ng-template *ngTemplateOutlet="deletionDescription"></ng-template>
- <p>
- <ng-container i18n>
- To confirm the deletion, enter
- </ng-container>
- <kbd>{{ pattern }}</kbd>
- <ng-container i18n>
- and click on
- </ng-container>
- <kbd>
- <ng-container *ngTemplateOutlet="deletionHeading"></ng-container>
- </kbd>.
- </p>
- <div class="form-group"
- [ngClass]="{'has-error': invalidControl(formDir.submitted)}">
- <input type="text"
- class="form-control"
- name="confirmation"
- id="confirmation"
- [placeholder]="pattern"
- autocomplete="off"
- (keyup)="updateConfirmation($event)"
- formControlName="confirmation"
- autofocus>
- <span class="help-block"
- *ngIf="invalidControl(formDir.submitted,'required')"
- i18n>
- This field is required.
- </span>
- <span class="help-block"
- *ngIf="invalidControl(formDir.submitted, 'pattern')">
- '{{ confirmation.value }}'
- <span i18n>doesn't match</span>
- '{{ pattern }}'.
- </span>
- </div>
- </div>
- <div class="modal-footer">
- <cd-submit-button #submitButton
- [form]="deletionForm"
- (submitAction)="deletionCall()">
- <ng-container *ngTemplateOutlet="deletionHeading"></ng-container>
- </cd-submit-button>
- <button class="btn btn-link btn-sm"
- (click)="hideModal()"
- i18n>
- Cancel
- </button>
- </div>
- </form>
-</ng-template>
-
-<ng-template #deletionHeading>
- <ng-container i18n>
- Delete
- </ng-container>
- {{ metaType }}
-</ng-template>
-
-<ng-template #deletionDescription>
- <ng-content></ng-content>
-</ng-template>
-
+++ /dev/null
-import { Component, ViewChild } from '@angular/core';
-import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
-import { FormGroupDirective, ReactiveFormsModule } from '@angular/forms';
-
-import { ModalModule } from 'ngx-bootstrap';
-import { Observable } from 'rxjs/Observable';
-import { Subscriber } from 'rxjs/Subscriber';
-
-import { ModalComponent } from '../modal/modal.component';
-import { SubmitButtonComponent } from '../submit-button/submit-button.component';
-import { DeletionLinkComponent } from './deletion-link.component';
-
-@Component({
- template: `
- <cd-deletion-link #ctrlDeleteButton
- metaType="Controller delete handling"
- pattern="ctrl-test"
- (toggleDeletion)="fakeDeleteController()">
- The spinner is handled by the controller if you have use the modal as ViewChild in order to
- use it's functions to stop the spinner or close the dialog.
- </cd-deletion-link>
- <cd-deletion-link #modalDeleteButton
- metaType="Modal delete handling"
- [deletionObserver]="fakeDelete()"
- pattern="modal-test">
- The spinner is handled by the modal if your given deletion function returns a Observable.
- </cd-deletion-link>
- `
-})
-class MockComponent {
- @ViewChild('ctrlDeleteButton') ctrlDeleteButton: DeletionLinkComponent;
- @ViewChild('modalDeleteButton') modalDeleteButton: DeletionLinkComponent;
- someData = [1, 2, 3, 4, 5];
- finished: number[];
-
- finish() {
- this.finished = [6, 7, 8, 9];
- }
-
- fakeDelete() {
- return (): Observable<any> => {
- return new Observable((observer: Subscriber<any>) => {
- Observable.timer(100).subscribe(() => {
- observer.next(this.finish());
- observer.complete();
- });
- });
- };
- }
-
- fakeDeleteController() {
- Observable.timer(100).subscribe(() => {
- this.finish();
- this.ctrlDeleteButton.hideModal();
- });
- }
-}
-
-describe('DeletionLinkComponent', () => {
- let mockComponent: MockComponent;
- let component: DeletionLinkComponent;
- let fixture: ComponentFixture<MockComponent>;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [ MockComponent, DeletionLinkComponent, ModalComponent,
- SubmitButtonComponent],
- imports: [ModalModule.forRoot(), ReactiveFormsModule],
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(MockComponent);
- mockComponent = fixture.componentInstance;
- component = mockComponent.ctrlDeleteButton;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
- describe('component functions', () => {
-
- const mockShowModal = () => {
- component.showModal(null);
- };
-
- const changeValue = (value) => {
- component.confirmation.setValue(value);
- component.confirmation.markAsDirty();
- component.confirmation.updateValueAndValidity();
- };
-
- beforeEach(() => {
- spyOn(component.modalService, 'show').and.returnValue({
- hide: () => true
- });
- });
-
- it('should test showModal', () => {
- changeValue('something');
- expect(mockShowModal).toBeTruthy();
- expect(component.confirmation.value).toBe('something');
- expect(component.modalService.show).not.toHaveBeenCalled();
- mockShowModal();
- expect(component.modalService.show).toHaveBeenCalled();
- expect(component.confirmation.value).toBe(null);
- expect(component.confirmation.pristine).toBe(true);
- });
-
- it('should test hideModal', () => {
- expect(component.bsModalRef).not.toBeTruthy();
- mockShowModal();
- expect(component.bsModalRef).toBeTruthy();
- expect(component.hideModal).toBeTruthy();
- spyOn(component.bsModalRef, 'hide').and.stub();
- expect(component.bsModalRef.hide).not.toHaveBeenCalled();
- component.hideModal();
- expect(component.bsModalRef.hide).toHaveBeenCalled();
- });
-
- describe('invalid control', () => {
-
- const testInvalidControl = (submitted: boolean, error: string, expected: boolean) => {
- expect(component.invalidControl(submitted, error)).toBe(expected);
- };
-
- beforeEach(() => {
- component.deletionForm.reset();
- });
-
- it('should test empty values', () => {
- expect(component.invalidControl).toBeTruthy();
- component.deletionForm.reset();
- testInvalidControl(false, undefined, false);
- testInvalidControl(true, 'required', true);
- component.deletionForm.reset();
- changeValue('let-me-pass');
- changeValue('');
- testInvalidControl(true, 'required', true);
- });
-
- it('should test pattern', () => {
- changeValue('let-me-pass');
- testInvalidControl(false, 'pattern', true);
- changeValue('ctrl-test');
- testInvalidControl(false, undefined, false);
- testInvalidControl(true, undefined, false);
- });
- });
-
- describe('deletion call', () => {
- beforeEach(() => {
- spyOn(component.toggleDeletion, 'emit');
- spyOn(component, 'stopLoadingSpinner');
- spyOn(component, 'hideModal').and.stub();
- });
-
- describe('Controller driven', () => {
- beforeEach(() => {
- mockShowModal();
- expect(component.toggleDeletion.emit).not.toHaveBeenCalled();
- expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
- expect(component.hideModal).not.toHaveBeenCalled();
- });
-
- it('should delete without doing anything but call emit', () => {
- component.deletionCall();
- expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
- expect(component.hideModal).not.toHaveBeenCalled();
- expect(component.toggleDeletion.emit).toHaveBeenCalled();
- });
-
- it('should test fake deletion that closes modal', <any>fakeAsync(() => {
- mockComponent.fakeDeleteController();
- expect(component.hideModal).not.toHaveBeenCalled();
- expect(mockComponent.finished).toBe(undefined);
- tick(2000);
- expect(component.hideModal).toHaveBeenCalled();
- expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
- }));
- });
-
- describe('Modal driven', () => {
- it('should delete and close modal', <any>fakeAsync(() => {
- component = mockComponent.modalDeleteButton;
- mockShowModal();
- spyOn(component.toggleDeletion, 'emit');
- spyOn(component, 'stopLoadingSpinner');
- spyOn(component, 'hideModal').and.stub();
- spyOn(mockComponent, 'fakeDelete');
-
- component.deletionCall();
- expect(mockComponent.finished).toBe(undefined);
- expect(component.toggleDeletion.emit).not.toHaveBeenCalled();
- expect(component.hideModal).not.toHaveBeenCalled();
-
- tick(2000);
- expect(component.toggleDeletion.emit).not.toHaveBeenCalled();
- expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
- expect(component.hideModal).toHaveBeenCalled();
- expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
- }));
- });
- });
- });
-
-});
+++ /dev/null
-import {
- Component, EventEmitter, Input, OnInit, Output, TemplateRef, ViewChild
-} from '@angular/core';
-import { FormControl, FormGroup, FormGroupDirective, Validators } from '@angular/forms';
-
-import { BsModalRef, BsModalService } from 'ngx-bootstrap';
-import { Observable } from 'rxjs/Observable';
-
-import { SubmitButtonComponent } from '../submit-button/submit-button.component';
-
-@Component({
- selector: 'cd-deletion-link',
- templateUrl: './deletion-link.component.html',
- styleUrls: ['./deletion-link.component.scss']
-})
-export class DeletionLinkComponent implements OnInit {
- @ViewChild(SubmitButtonComponent) submitButton: SubmitButtonComponent;
- @Input() metaType: string;
- @Input() pattern = 'yes';
- @Input() deletionObserver: () => Observable<any>;
- @Output() toggleDeletion = new EventEmitter();
- bsModalRef: BsModalRef;
- deletionForm: FormGroup;
- confirmation: FormControl;
- delete: Function;
-
- constructor(public modalService: BsModalService) {}
-
- ngOnInit() {
- this.confirmation = new FormControl('', {
- validators: [
- Validators.required,
- Validators.pattern(this.pattern)
- ],
- updateOn: 'blur'
- });
- this.deletionForm = new FormGroup({
- confirmation: this.confirmation
- });
- }
-
- showModal(template: TemplateRef<any>) {
- this.deletionForm.reset();
- this.bsModalRef = this.modalService.show(template);
- this.delete = () => {
- this.submitButton.submit();
- };
- }
-
- invalidControl(submitted: boolean, error?: string): boolean {
- const control = this.confirmation;
- return !!(
- (submitted || control.dirty) &&
- control.invalid &&
- (error ? control.errors[error] : true)
- );
- }
-
- updateConfirmation($e) {
- if ($e.key !== 'Enter') {
- return;
- }
- this.confirmation.setValue($e.target.value);
- this.confirmation.markAsDirty();
- this.confirmation.updateValueAndValidity();
- }
-
- deletionCall() {
- if (this.deletionObserver) {
- this.deletionObserver().subscribe(
- undefined,
- () => this.stopLoadingSpinner(),
- () => this.hideModal()
- );
- } else {
- this.toggleDeletion.emit();
- }
- }
-
- hideModal() {
- this.bsModalRef.hide();
- }
-
- stopLoadingSpinner() {
- this.submitButton.loading = false;
- }
-}
--- /dev/null
+<cd-modal #modal
+ [modalRef]="modalRef">
+ <ng-container class="modal-title">
+ <ng-container *ngTemplateOutlet="deletionHeading"></ng-container>
+ </ng-container>
+
+ <ng-container class="modal-content">
+ <form name="deletionForm"
+ #formDir="ngForm"
+ (submit)="delete()"
+ [formGroup]="deletionForm"
+ novalidate>
+ <div class="modal-body">
+ <ng-container *ngTemplateOutlet="description"></ng-container>
+ <p>
+ <ng-container i18n>
+ To confirm the deletion, enter
+ </ng-container>
+ <kbd>{{ pattern }}</kbd>
+ <ng-container i18n>
+ and click on
+ </ng-container>
+ <kbd>
+ <ng-container *ngTemplateOutlet="deletionHeading"></ng-container>
+ </kbd>.
+ </p>
+ <div class="form-group"
+ [ngClass]="{'has-error': invalidControl(formDir.submitted)}">
+ <input type="text"
+ class="form-control"
+ name="confirmation"
+ id="confirmation"
+ [placeholder]="pattern"
+ [pattern]="pattern"
+ autocomplete="off"
+ (keyup)="updateConfirmation($event)"
+ formControlName="confirmation"
+ autofocus>
+ <span class="help-block"
+ *ngIf="invalidControl(formDir.submitted,'required')"
+ i18n>
+ This field is required.
+ </span>
+ <span class="help-block"
+ *ngIf="invalidControl(formDir.submitted, 'pattern')">
+ '{{ confirmation.value }}'
+ <span i18n>doesn't match</span>
+ '{{ pattern }}'.
+ </span>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <cd-submit-button #submitButton
+ [form]="deletionForm"
+ (submitAction)="deletionCall()">
+ <ng-container *ngTemplateOutlet="deletionHeading"></ng-container>
+ </cd-submit-button>
+ <button class="btn btn-link btn-sm"
+ (click)="hideModal()"
+ i18n>
+ Cancel
+ </button>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
+
+<ng-template #deletionHeading>
+ <ng-container i18n>
+ Delete
+ </ng-container>
+ {{ metaType }}
+</ng-template>
--- /dev/null
+import { Component, NgModule, TemplateRef, ViewChild } from '@angular/core';
+import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap';
+import { Observable } from 'rxjs/Observable';
+import { Subscriber } from 'rxjs/Subscriber';
+
+import { ModalComponent } from '../modal/modal.component';
+import { SubmitButtonComponent } from '../submit-button/submit-button.component';
+import { DeletionModalComponent } from './deletion-modal.component';
+
+@NgModule({
+ entryComponents: [DeletionModalComponent]
+})
+export class MockModule {}
+
+@Component({
+ template: `
+ <button type="button"
+ class="btn btn-sm btn-primary"
+ (click)="openCtrlDriven()">
+ <i class="fa fa-fw fa-trash"></i>Deletion Ctrl-Test
+ <ng-template #ctrlDescription>
+ The spinner is handled by the controller if you have use the modal as ViewChild in order to
+ use it's functions to stop the spinner or close the dialog.
+ </ng-template>
+ </button>
+
+ <button type="button"
+ class="btn btn-sm btn-primary"
+ (click)="openModalDriven()">
+ <i class="fa fa-fw fa-trash"></i>Deletion Modal-Test
+ <ng-template #modalDescription>
+ The spinner is handled by the modal if your given deletion function returns a Observable.
+ </ng-template>
+ </button>
+ `
+})
+class MockComponent {
+ @ViewChild('ctrlDescription') ctrlDescription: TemplateRef<any>;
+ @ViewChild('modalDescription') modalDescription: TemplateRef<any>;
+ someData = [1, 2, 3, 4, 5];
+ finished: number[];
+ ctrlRef: BsModalRef;
+ modalRef: BsModalRef;
+
+ // Normally private - public was needed for the tests
+ constructor(public modalService: BsModalService) {}
+
+ openCtrlDriven() {
+ this.ctrlRef = this.modalService.show(DeletionModalComponent);
+ this.ctrlRef.content.setUp({
+ metaType: 'Controller delete handling',
+ pattern: 'ctrl-test',
+ deletionMethod: this.fakeDeleteController.bind(this),
+ description: this.ctrlDescription,
+ modalRef: this.ctrlRef
+ });
+ }
+
+ openModalDriven() {
+ this.modalRef = this.modalService.show(DeletionModalComponent);
+ this.modalRef.content.setUp({
+ metaType: 'Modal delete handling',
+ pattern: 'modal-test',
+ deletionObserver: this.fakeDelete(),
+ description: this.modalDescription,
+ modalRef: this.modalRef
+ });
+ }
+
+ finish() {
+ this.finished = [6, 7, 8, 9];
+ }
+
+ fakeDelete() {
+ return (): Observable<any> => {
+ return new Observable((observer: Subscriber<any>) => {
+ Observable.timer(100).subscribe(() => {
+ observer.next(this.finish());
+ observer.complete();
+ });
+ });
+ };
+ }
+
+ fakeDeleteController() {
+ Observable.timer(100).subscribe(() => {
+ this.finish();
+ this.ctrlRef.hide();
+ });
+ }
+}
+
+describe('DeletionModalComponent', () => {
+ let mockComponent: MockComponent;
+ let component: DeletionModalComponent;
+ let mockFixture: ComponentFixture<MockComponent>;
+ let fixture: ComponentFixture<DeletionModalComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ MockComponent, DeletionModalComponent, ModalComponent,
+ SubmitButtonComponent],
+ imports: [ModalModule.forRoot(), ReactiveFormsModule, MockModule],
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ mockFixture = TestBed.createComponent(MockComponent);
+ mockComponent = mockFixture.componentInstance;
+ // Mocking the modals as a lot would be left over
+ spyOn(mockComponent.modalService, 'show').and.callFake(() => {
+ const ref = new BsModalRef();
+ fixture = TestBed.createComponent(DeletionModalComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ ref.content = component;
+ return ref;
+ });
+ mockComponent.openCtrlDriven();
+ mockFixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ describe('setUp', () => {
+ const clearSetup = () => {
+ component.metaType = undefined;
+ component.pattern = 'yes';
+ component.deletionObserver = undefined;
+ component.description = undefined;
+ component.modalRef = undefined;
+ };
+
+ const expectSetup = (metaType, observer: boolean, method: boolean, pattern,
+ template: boolean) => {
+ expect(component.modalRef).toBeTruthy();
+ expect(component.metaType).toBe(metaType);
+ expect(!!component.deletionObserver).toBe(observer);
+ expect(!!component.deletionMethod).toBe(method);
+ expect(component.pattern).toBe(pattern);
+ expect(!!component.description).toBe(template);
+ };
+
+ beforeEach(() => {
+ clearSetup();
+ });
+
+ it('should throw error if no modal reference is given', () => {
+ expect(() => component.setUp({
+ metaType: undefined,
+ modalRef: undefined
+ })).toThrowError('No modal reference');
+ });
+
+ it('should throw error if no meta type is given', () => {
+ expect(() => component.setUp({
+ metaType: undefined,
+ modalRef: mockComponent.ctrlRef
+ })).toThrowError('No meta type');
+ });
+
+ it('should throw error if no deletion method is given', () => {
+ expect(() => component.setUp({
+ metaType: 'Sth',
+ modalRef: mockComponent.ctrlRef
+ })).toThrowError('No deletion method');
+ });
+
+ it('should throw no errors if metaType, modalRef and a deletion method were given',
+ () => {
+ component.setUp({
+ metaType: 'Observer',
+ modalRef: mockComponent.ctrlRef,
+ deletionObserver: mockComponent.fakeDelete()
+ });
+ expectSetup('Observer', true, false, 'yes', false);
+ clearSetup();
+ component.setUp({
+ metaType: 'Controller',
+ modalRef: mockComponent.ctrlRef,
+ deletionMethod: mockComponent.fakeDeleteController
+ });
+ expectSetup('Controller', false, true, 'yes', false);
+ });
+
+ it('should test optional parameters - pattern and description',
+ () => {
+ component.setUp({
+ metaType: 'Pattern only',
+ modalRef: mockComponent.ctrlRef,
+ deletionObserver: mockComponent.fakeDelete(),
+ pattern: '{sth/!$_8()'
+ });
+ expectSetup('Pattern only', true, false, '{sth/!$_8()', false);
+ clearSetup();
+ component.setUp({
+ metaType: 'Description only',
+ modalRef: mockComponent.ctrlRef,
+ deletionObserver: mockComponent.fakeDelete(),
+ description: mockComponent.modalDescription
+ });
+ expectSetup('Description only', true, false, 'yes', true);
+ clearSetup();
+ component.setUp({
+ metaType: 'Description and pattern',
+ modalRef: mockComponent.ctrlRef,
+ deletionObserver: mockComponent.fakeDelete(),
+ description: mockComponent.modalDescription,
+ pattern: '{sth/!$_8()'
+ });
+ expectSetup('Description and pattern', true, false, '{sth/!$_8()', true);
+ });
+ });
+
+ it('should test if the ctrl driven mock is set correctly through mock component', () => {
+ expect(component.metaType).toBe('Controller delete handling');
+ expect(component.pattern).toBe('ctrl-test');
+ expect(component.description).toBeTruthy();
+ expect(component.modalRef).toBeTruthy();
+ expect(component.deletionMethod).toBeTruthy();
+ expect(component.deletionObserver).not.toBeTruthy();
+ });
+
+ it('should test if the modal driven mock is set correctly through mock component', () => {
+ mockComponent.openModalDriven();
+ expect(component.metaType).toBe('Modal delete handling');
+ expect(component.pattern).toBe('modal-test');
+ expect(component.description).toBeTruthy();
+ expect(component.modalRef).toBeTruthy();
+ expect(component.deletionObserver).toBeTruthy();
+ expect(component.deletionMethod).not.toBeTruthy();
+ });
+
+ describe('component functions', () => {
+ const changeValue = (value) => {
+ component.confirmation.setValue(value);
+ component.confirmation.markAsDirty();
+ component.confirmation.updateValueAndValidity();
+ fixture.detectChanges();
+ };
+
+ it('should test hideModal', () => {
+ expect(component.modalRef).toBeTruthy();
+ expect(component.hideModal).toBeTruthy();
+ spyOn(component.modalRef, 'hide').and.callThrough();
+ expect(component.modalRef.hide).not.toHaveBeenCalled();
+ component.hideModal();
+ expect(component.modalRef.hide).toHaveBeenCalled();
+ });
+
+ describe('invalid control', () => {
+ const testInvalidControl = (submitted: boolean, error: string, expected: boolean) => {
+ expect(component.invalidControl(submitted, error)).toBe(expected);
+ };
+
+ beforeEach(() => {
+ component.deletionForm.reset();
+ });
+
+ it('should test empty values', () => {
+ expect(component.invalidControl).toBeTruthy();
+ component.deletionForm.reset();
+ testInvalidControl(false, undefined, false);
+ testInvalidControl(true, 'required', true);
+ component.deletionForm.reset();
+ changeValue('let-me-pass');
+ changeValue('');
+ testInvalidControl(true, 'required', true);
+ });
+
+ it('should test pattern', () => {
+ changeValue('let-me-pass');
+ testInvalidControl(false, 'pattern', true);
+ changeValue('ctrl-test');
+ testInvalidControl(false, undefined, false);
+ testInvalidControl(true, undefined, false);
+ });
+ });
+
+ describe('deletion call', () => {
+ beforeEach(() => {
+ spyOn(component, 'stopLoadingSpinner').and.callThrough();
+ spyOn(component, 'hideModal').and.callThrough();
+ });
+
+ describe('Controller driven', () => {
+ beforeEach(() => {
+ spyOn(component, 'deletionMethod').and.callThrough();
+ spyOn(mockComponent.ctrlRef, 'hide').and.callThrough();
+ });
+
+ it('should test fake deletion that closes modal', <any>fakeAsync(() => {
+ // Before deletionCall
+ expect(component.deletionMethod).not.toHaveBeenCalled();
+ // During deletionCall
+ component.deletionCall();
+ expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
+ expect(component.hideModal).not.toHaveBeenCalled();
+ expect(mockComponent.ctrlRef.hide).not.toHaveBeenCalled();
+ expect(component.deletionMethod).toHaveBeenCalled();
+ expect(mockComponent.finished).toBe(undefined);
+ // After deletionCall
+ tick(2000);
+ expect(component.hideModal).not.toHaveBeenCalled();
+ expect(mockComponent.ctrlRef.hide).toHaveBeenCalled();
+ expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
+ }));
+ });
+
+ describe('Modal driven', () => {
+ beforeEach(() => {
+ mockComponent.openModalDriven();
+ spyOn(mockComponent.modalRef, 'hide').and.callThrough();
+ spyOn(component, 'stopLoadingSpinner').and.callThrough();
+ spyOn(component, 'hideModal').and.callThrough();
+ spyOn(mockComponent, 'fakeDelete').and.callThrough();
+ });
+
+ it('should delete and close modal', <any>fakeAsync(() => {
+ // During deletionCall
+ component.deletionCall();
+ expect(mockComponent.finished).toBe(undefined);
+ expect(component.hideModal).not.toHaveBeenCalled();
+ expect(mockComponent.modalRef.hide).not.toHaveBeenCalled();
+ // After deletionCall
+ tick(2000);
+ expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
+ expect(mockComponent.modalRef.hide).toHaveBeenCalled();
+ expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
+ expect(component.hideModal).toHaveBeenCalled();
+ }));
+ });
+ });
+ });
+
+});
--- /dev/null
+import {
+ Component, OnInit, TemplateRef, ViewChild
+} from '@angular/core';
+import { FormControl, FormGroup, Validators } from '@angular/forms';
+
+import { BsModalRef, BsModalService } from 'ngx-bootstrap';
+import { Observable } from 'rxjs/Observable';
+
+import { SubmitButtonComponent } from '../submit-button/submit-button.component';
+
+@Component({
+ selector: 'cd-deletion-modal',
+ templateUrl: './deletion-modal.component.html',
+ styleUrls: ['./deletion-modal.component.scss']
+})
+export class DeletionModalComponent implements OnInit {
+ @ViewChild(SubmitButtonComponent) submitButton: SubmitButtonComponent;
+ description: TemplateRef<any>;
+ metaType: string;
+ pattern = 'yes';
+ deletionObserver: () => Observable<any>;
+ deletionMethod: Function;
+ modalRef: BsModalRef;
+
+ deletionForm: FormGroup;
+ confirmation: FormControl;
+
+ // Parameters are destructed here than assigned to specific types and marked as optional
+ setUp({modalRef, metaType, deletionMethod, pattern, deletionObserver, description}:
+ { modalRef: BsModalRef, metaType: string, deletionMethod?: Function, pattern?: string,
+ deletionObserver?: () => Observable<any>, description?: TemplateRef<any>}) {
+ if (!modalRef) {
+ throw new Error('No modal reference');
+ } else if (!metaType) {
+ throw new Error('No meta type');
+ } else if (!(deletionMethod || deletionObserver)) {
+ throw new Error('No deletion method');
+ }
+ this.metaType = metaType;
+ this.modalRef = modalRef;
+ this.deletionMethod = deletionMethod;
+ this.pattern = pattern || this.pattern;
+ this.deletionObserver = deletionObserver;
+ this.description = description;
+ }
+
+ ngOnInit() {
+ this.confirmation = new FormControl('', {
+ validators: [
+ Validators.required
+ ],
+ updateOn: 'blur'
+ });
+ this.deletionForm = new FormGroup({
+ confirmation: this.confirmation
+ });
+ }
+
+ invalidControl(submitted: boolean, error?: string): boolean {
+ const control = this.confirmation;
+ return !!(
+ (submitted || control.dirty) &&
+ control.invalid &&
+ (error ? control.errors[error] : true)
+ );
+ }
+
+ updateConfirmation($e) {
+ if ($e.key !== 'Enter') {
+ return;
+ }
+ this.confirmation.setValue($e.target.value);
+ this.confirmation.markAsDirty();
+ this.confirmation.updateValueAndValidity();
+ }
+
+ delete () {
+ this.submitButton.submit();
+ }
+
+ deletionCall() {
+ if (this.deletionObserver) {
+ this.deletionObserver().subscribe(
+ undefined,
+ this.stopLoadingSpinner.bind(this),
+ this.hideModal.bind(this)
+ );
+ } else {
+ this.deletionMethod();
+ }
+ }
+
+ hideModal() {
+ this.modalRef.hide();
+ }
+
+ stopLoadingSpinner() {
+ this.submitButton.loading = false;
+ }
+}