From 1994c98063c27a41797dec01f2ca9fcbe33ceab0 Mon Sep 17 00:00:00 2001 From: Israel Lavi Date: Mon, 21 May 2018 17:42:00 +0300 Subject: init commit onap ui Change-Id: I1dace78817dbba752c550c182dfea118b4a38646 Issue-ID: SDC-1350 Signed-off-by: Israel Lavi --- src/angular/modals/modal-button.component.ts | 29 ++++++ src/angular/modals/modal-close-button.component.ts | 34 +++++++ src/angular/modals/modal.component.html.ts | 38 ++++++++ src/angular/modals/modal.component.spec.ts | 105 +++++++++++++++++++++ src/angular/modals/modal.component.ts | 96 +++++++++++++++++++ src/angular/modals/modal.module.ts | 33 +++++++ src/angular/modals/modal.service.ts | 100 ++++++++++++++++++++ src/angular/modals/models/modal-config.ts | 44 +++++++++ 8 files changed, 479 insertions(+) create mode 100644 src/angular/modals/modal-button.component.ts create mode 100644 src/angular/modals/modal-close-button.component.ts create mode 100644 src/angular/modals/modal.component.html.ts create mode 100644 src/angular/modals/modal.component.spec.ts create mode 100644 src/angular/modals/modal.component.ts create mode 100644 src/angular/modals/modal.module.ts create mode 100644 src/angular/modals/modal.service.ts create mode 100644 src/angular/modals/models/modal-config.ts (limited to 'src/angular/modals') diff --git a/src/angular/modals/modal-button.component.ts b/src/angular/modals/modal-button.component.ts new file mode 100644 index 0000000..4fa5b7c --- /dev/null +++ b/src/angular/modals/modal-button.component.ts @@ -0,0 +1,29 @@ +import { Component, Input, HostListener } from "@angular/core"; +import { ButtonComponent } from "../buttons/button.component"; +import { ModalService } from "./modal.service"; +import template from "./../buttons/button.component.html"; + +@Component({ + selector: "sdc-modal-button", + template: template +}) +export class ModalButtonComponent extends ButtonComponent { + + @Input() public id?: string; + @Input() public callback: Function; + @Input() public closeModal: boolean; + @HostListener('click') invokeCallback = (): void => { + if (this.callback) { + this.callback(); + } + if (this.closeModal) { + this.modalService.closeModal(); + } + } + + constructor(private modalService: ModalService) { + super(); + this.closeModal = false; + } + +} diff --git a/src/angular/modals/modal-close-button.component.ts b/src/angular/modals/modal-close-button.component.ts new file mode 100644 index 0000000..e761019 --- /dev/null +++ b/src/angular/modals/modal-close-button.component.ts @@ -0,0 +1,34 @@ +import { Component, Input } from "@angular/core"; +import { ButtonComponent } from "../buttons/button.component"; +import { ModalService } from "./modal.service"; +import { RippleAnimationAction } from "../animations/ripple-click.animation.directive"; + +@Component({ + selector: "sdc-modal-close-button", + template: ` +
+ +
+ ` +}) +export class ModalCloseButtonComponent { + + @Input() testId: string; + @Input() disabled: boolean; + + public rippleAnimationAction: RippleAnimationAction = RippleAnimationAction.MOUSE_ENTER; + + constructor(private modalService: ModalService) { + } + + public closeModal = (): void => { + this.modalService.closeModal(); + } + +} diff --git a/src/angular/modals/modal.component.html.ts b/src/angular/modals/modal.component.html.ts new file mode 100644 index 0000000..90119ac --- /dev/null +++ b/src/angular/modals/modal.component.html.ts @@ -0,0 +1,38 @@ +export default ` +
+
+ +
+
+
+
+
+
+
+
+
+
{{ title }}
+ +
+
+
{{message}}
+
+
+ +
+
+ +`; diff --git a/src/angular/modals/modal.component.spec.ts b/src/angular/modals/modal.component.spec.ts new file mode 100644 index 0000000..372d59d --- /dev/null +++ b/src/angular/modals/modal.component.spec.ts @@ -0,0 +1,105 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Component, Input, NgModule, ViewContainerRef, Inject, Injectable, Type, ApplicationRef, ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, Injector } from '@angular/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core/src/metadata/ng_module'; +import { ModalService } from './modal.service'; +import { CreateDynamicComponentService } from "../utils/create-dynamic-component.service"; +import { IModalConfig, ModalType, ModalSize } from "../../../src/angular/modals/models/modal-config"; +import { ModalInnerContent } from "../../../stories/ng2-component-lab/components/modal-inner-content-example.component"; + + +describe("Modal unit-tests", () => { + let testService: ModalService; + const testInputModal = { + size: 'xl', //'xl|l|md|sm|xsm' + title: 'Test_Title', + message: 'Test_Message', + modalVisible: true + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + providers:[ + ModalService, + { provide : CreateDynamicComponentService, useClass: CreateDynamicComponentServiceTest} + ], + declarations: [], + schemas:[NO_ERRORS_SCHEMA] + }) + testService = TestBed.get(ModalService); + })); + + it('Modal should be open test', () => { + let modalInstance = testService.openModal(testInputModal); + expect(modalInstance).toBeTruthy(); + }) + + it('Modal alert window test', () => { + let modalInstance = testService.openAlertModal('testAlert', 'testMessage'); + expect(modalInstance).toBeTruthy(); + }) + + it('Modal info window test', () => { + let modalInstance = testService.openErrorModal('testMessage', 'sampleTestId'); + expect(modalInstance).toBeTruthy(); + }) + + + it('Custom Modal should be open', () => { + let modalConfig:IModalConfig = { + size: ModalSize.medium, + title: 'Title', + type: ModalType.custom, + buttons: [{text:"Save & Close", closeModal:true}, + {text:"Save", callback:this.customModalOnSave, closeModal:false}, + {text:"Cancel", type: 'secondary', closeModal:true}] + }; + let modalInstance = testService.openCustomModal(modalConfig, ModalInnerContent, {name: "Sample Content"}); + expect(modalInstance).toBeTruthy(); + }) + + it('Shoul close window', () => { + let modalInstance = testService.openModal(testInputModal); + testService.closeModal(); + expect(modalInstance.instance.modalVisible).toBeFalsy(); + }) +}) + + +const testModalInstance = { + instance:{ + closeAnimationComplete:{ + subscribe:() => { + return true; + }, + }, + _createDynamicComponentService:{ + insertComponentDynamically:() => { + return true; + } + }, + modalVisible:true + }, + +}; + +@Component({ + selector: 'modal-test', + template: `
` +}) + + + +export class CreateDynamicComponentServiceTest { + modalVisble: true; + public createComponentDynamically = (modalInstance, customData) => { + return testModalInstance; + } + public insertComponentDynamically = () =>{ + return testModalInstance; + } + +} + + + + diff --git a/src/angular/modals/modal.component.ts b/src/angular/modals/modal.component.ts new file mode 100644 index 0000000..4f4d81f --- /dev/null +++ b/src/angular/modals/modal.component.ts @@ -0,0 +1,96 @@ +import { Component, Input, Output, ViewContainerRef, ViewChild, ComponentRef, trigger, state, animate, transition, style, EventEmitter, Renderer, ElementRef } from '@angular/core'; +import { ModalButtonComponent } from './modal-button.component'; +import { LowerCasePipe } from '@angular/common'; +import { ModalCloseButtonComponent } from './modal-close-button.component'; +import template from './modal.component.html'; + +@Component({ + selector: 'sdc-modal', + template: template, + animations: [ + trigger('toggleBackground', [ + transition('* => 1', [style({opacity: 0}), animate('.45s cubic-bezier(0.23, 1, 0.32, 1)')]), + transition('1 => *', [animate('.35s cubic-bezier(0.23, 1, 0.32, 1)', style({opacity: 0}))]) + ]), + trigger('toggleModal', [ + transition('* => 1', [style({opacity: 0, transform: 'translateY(-80px)'}), animate('.45s cubic-bezier(0.23, 1, 0.32, 1)')]), + transition('1 => *', [style({opacity: 1, transform: 'translateY(0px)'}), animate('.35s ease-in-out', style({opacity:0, transform: 'translateY(-80px)'}))]) + ]) + ] +}) + +export class ModalComponent { + + @Input() size: string; 'xl|l|md|sm|xsm'; + @Input() title: string; + @Input() message: string; + @Input() buttons: ModalButtonComponent[]; + @Input() type: string; 'info|error|alert|custom'; + @Input() testId: string; + @Output() closeAnimationComplete: EventEmitter = new EventEmitter(); + + @ViewChild('modalCloseButton') + set refCloseButton(_modalCloseButton: ModalCloseButtonComponent) { + this.modalCloseButton = _modalCloseButton; + } + + modalVisible: boolean; + // Allows for custom component as body instead of simple message. + // See ModalService.createActionModal for implementation details, and HttpService's catchError() for example. + @ViewChild('dynamicContentContainer', {read: ViewContainerRef}) dynamicContentContainer: ViewContainerRef; + innerModalContent: ComponentRef; + + public calculatedTestId: string; + public modalCloseButton: ModalCloseButtonComponent; + + constructor(private renderer: Renderer, + private lowerCasePipe: LowerCasePipe + ) { + this.modalVisible = true; + } + + getCalculatedTestId = (buttonText: string): string => { + // TODO: Replace this + if (this.testId) { + return this.testId + '-' + this.lowerCasePipe.transform(buttonText); + } + return null; + } + + public modalToggled = (toggleEvent: any) => { + if (!toggleEvent.toState) { + this.closeAnimationComplete.emit(); + } + } + + public getCloseButton = (): ModalCloseButtonComponent => { + return this.modalCloseButton; + } + + public getButtonById = (id: string): ModalButtonComponent => { + return this.buttons.find((button) => { + return button.id && button.id === id; + }); + } + + public getButtons = (): ModalButtonComponent[] => { + return this.buttons; + } + + public setButtons = (_buttons: ModalButtonComponent[]): void => { + this.buttons = _buttons; + } + + public getTitle = (): string => { + return this.title; + } + + public setTitle = (_title: string): void => { + this.title = _title; + } + + public hoverAnimation(evn: MouseEvent) { + this.renderer.setElementClass(evn.target as HTMLElement, 'sdc-ripple-click__animated', true); + // evn.taregt.classList.add('sdc-ripple-click__animated'); + } +} diff --git a/src/angular/modals/modal.module.ts b/src/angular/modals/modal.module.ts new file mode 100644 index 0000000..5697437 --- /dev/null +++ b/src/angular/modals/modal.module.ts @@ -0,0 +1,33 @@ +import { NgModule } from "@angular/core"; +import { ModalComponent } from "./modal.component"; +import { ModalService } from "./modal.service"; +import { CommonModule, LowerCasePipe } from "@angular/common"; +import { ButtonsModule } from "../buttons/buttons.module"; +import { AnimationDirectivesModule } from "../animations/animation-directives.module"; +import { CreateDynamicComponentService } from "../utils/create-dynamic-component.service"; +import { ModalButtonComponent } from "./modal-button.component"; +import { ModalCloseButtonComponent } from "./modal-close-button.component"; +import { SvgIconModule } from "../svg-icon/svg-icon.module"; + +@NgModule({ + declarations: [ + ModalComponent, + ModalButtonComponent, + ModalCloseButtonComponent + ], + imports: [ + CommonModule, + ButtonsModule, + AnimationDirectivesModule, + SvgIconModule + ], + entryComponents: [ + ModalComponent, + ModalCloseButtonComponent + ], + exports: [ModalButtonComponent], + providers: [CreateDynamicComponentService, ModalService, LowerCasePipe] +}) +export class ModalModule { + +} diff --git a/src/angular/modals/modal.service.ts b/src/angular/modals/modal.service.ts new file mode 100644 index 0000000..d80ad1f --- /dev/null +++ b/src/angular/modals/modal.service.ts @@ -0,0 +1,100 @@ +import { Injectable, Type, ComponentRef } from '@angular/core'; +import { ModalComponent } from "./modal.component"; +import { CreateDynamicComponentService } from "../utils/create-dynamic-component.service"; +import { IModalConfig, ModalType, ModalSize, IModalButtonComponent } from "./models/modal-config"; + +@Injectable() +export class ModalService { + + private currentModal: ComponentRef; + + constructor(private createDynamicComponentService: CreateDynamicComponentService) { + } + + /* Shortcut method to open an alert modal with title, message, and close button that simply closes the modal. */ + public openAlertModal(title: string, message: string, actionButtonText?: string, actionButtonCallback?: Function, testId?: string) { + const modalConfig = { + size: ModalSize.small, + title: title, + message: message, + testId: testId, + buttons: this.createButtons('secondary', actionButtonText, actionButtonCallback), + type: ModalType.alert + } as IModalConfig; + const modalInstance: ComponentRef = this.openModal(modalConfig); + this.currentModal = modalInstance; + return modalInstance; + } + + public openActionModal = (title: string, message: string, actionButtonText?: string, actionButtonCallback?: Function, testId?: string): ComponentRef => { + const modalConfig = { + size: ModalSize.small, + title: title, + message: message, + testId: testId, + type: ModalType.standard, + buttons: this.createButtons('primary', actionButtonText, actionButtonCallback) + } as IModalConfig; + const modalInstance: ComponentRef = this.openModal(modalConfig); + this.currentModal = modalInstance; + return modalInstance; + } + + public openErrorModal = (errorMessage?: string, testId?: string): ComponentRef => { + const modalConfig = { + size: ModalSize.small, + title: 'Error', + message: errorMessage, + testId: testId, + buttons: [{text: "OK", type: "alert", closeModal: true}], + type: ModalType.error + } as IModalConfig; + const modalInstance: ComponentRef = this.openModal(modalConfig); + this.currentModal = modalInstance; + return modalInstance; + } + + public openCustomModal = (modalConfig: IModalConfig, dynamicComponentType: Type, dynamicComponentInput?: any) => { + const modalInstance: ComponentRef = this.openModal(modalConfig); + this.createInnnerComponent(dynamicComponentType, dynamicComponentInput); + return modalInstance; + } + + public createInnnerComponent = (dynamicComponentType: Type, dynamicComponentInput?: any): void => { + this.currentModal.instance.innerModalContent = this.createDynamicComponentService.insertComponentDynamically(dynamicComponentType, dynamicComponentInput, this.currentModal.instance.dynamicContentContainer); + } + + public openModal = (customModalData: IModalConfig): ComponentRef => { + const modalInstance: ComponentRef = this.createDynamicComponentService.createComponentDynamically(ModalComponent, customModalData); + modalInstance.instance.closeAnimationComplete.subscribe(() => { + this.destroyModal(); + }); + this.currentModal = modalInstance; + return modalInstance; + } + + public getCurrentInstance = () => { + return this.currentModal.instance; + } + + public closeModal = (): void => { // triggers closeModal animation, which then triggers toggleModal.done and the subscription to destroyModal + this.currentModal.instance.modalVisible = false; + } + + private createButtons = (type: string, actionButtonText?: string, actionButtonCallback?: Function): Array => { + const buttons: Array = []; + if (actionButtonText && actionButtonCallback) { + buttons.push({text: actionButtonText, type: type, callback: actionButtonCallback, closeModal: true}); + buttons.push({text: 'Cancel', type: 'secondary', closeModal: true}); + } else { + buttons.push({text: 'Cancel', type: type, closeModal: true}); + } + + return buttons; + } + + private destroyModal = (): void => { + this.currentModal.destroy(); + } + +} diff --git a/src/angular/modals/models/modal-config.ts b/src/angular/modals/models/modal-config.ts new file mode 100644 index 0000000..635942b --- /dev/null +++ b/src/angular/modals/models/modal-config.ts @@ -0,0 +1,44 @@ +import { Placement } from "../../common/enums"; + +export interface IModalConfig { + size?: string; // xl|l|md|sm|xsm + title?: string; + message?: string; + buttons?: IModalButtonComponent[]; + testId?: string; + type?: string; // 'info|error|alert'; +} + +export interface IButtonComponent { + text: string; + disabled?: boolean; + type?: string; + testId?: string; + preventDoubleClick?: boolean; + icon_name?: string; + icon_position?: string; + show_spinner?: boolean; + spinner_position?: Placement; + size?: string; +} + +export interface IModalButtonComponent extends IButtonComponent{ + id?: string; + callback?: Function; + closeModal?: boolean; +} + +export enum ModalType { + alert = "alert", + error = "error", + standard = "info", + custom = "custom" +} + +export enum ModalSize { + xlarge = "xl", + large = "l", + medium = "md", + small = "sm", + xsmall = "xsm" +} -- cgit