diff --git a/demo-shell-ng2/app/components/activiti/activiti-demo.component.html b/demo-shell-ng2/app/components/activiti/activiti-demo.component.html index 48829e5f36..70f5c41cd0 100644 --- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.html +++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.html @@ -8,6 +8,7 @@ APPS TASK LIST PROCESS LIST + START PROCESS ANALYTICS @@ -64,7 +65,6 @@
Process Filters - @@ -90,6 +90,21 @@ + +
+
+
+
+
+ Start Process + +
+
+
+
+
+ +
diff --git a/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts b/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts index 3cb9e17d37..7597abd8a7 100644 --- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts +++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts @@ -22,7 +22,7 @@ import { ActivitiApps, ActivitiTaskList } from 'ng2-activiti-tasklist'; -import { ActivitiProcessInstanceListComponent } from 'ng2-activiti-processlist'; +import { ActivitiProcessInstanceListComponent, ActivitiStartProcessInstance } from 'ng2-activiti-processlist'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs/Rx'; import { @@ -65,6 +65,9 @@ export class ActivitiDemoComponent implements AfterViewChecked { @ViewChild('activitiprocessdetails') activitiprocessdetails: any; + @ViewChild(ActivitiStartProcessInstance) + activitiStartProcess: ActivitiStartProcessInstance; + @ViewChild('tabmain') tabMain: any; @@ -186,7 +189,9 @@ export class ActivitiDemoComponent implements AfterViewChecked { } onStartProcessInstance() { + this.activitiStartProcess.reset(); this.activitiprocesslist.reload(); + this.changeTab('start-process', 'processes'); } processCancelled(data: any) { diff --git a/ng2-components/ng2-activiti-processlist/index.ts b/ng2-components/ng2-activiti-processlist/index.ts index 08a0671b82..8cb4397556 100644 --- a/ng2-components/ng2-activiti-processlist/index.ts +++ b/ng2-components/ng2-activiti-processlist/index.ts @@ -27,12 +27,15 @@ import { ActivitiProcessInstanceHeader } from './src/components/activiti-process import { ActivitiProcessInstanceTasks } from './src/components/activiti-process-instance-tasks.component'; import { ActivitiComments } from './src/components/activiti-comments.component'; import { ActivitiProcessInstanceDetails } from './src/components/activiti-process-instance-details.component'; -import { ActivitiStartProcessButton } from './src/components/activiti-start-process.component'; +import { ActivitiStartProcessInstance } from './src/components/activiti-start-process.component'; +import { ActivitiStartProcessInstanceDialog } from './src/components/activiti-start-process-dialog.component'; import { ActivitiProcessService } from './src/services/activiti-process.service'; // components export * from './src/components/activiti-processlist.component'; export * from './src/components/activiti-process-instance-details.component'; +export * from './src/components/activiti-start-process.component'; +export * from './src/components/activiti-start-process-dialog.component'; // models export * from './src/models/index'; @@ -47,7 +50,8 @@ export const ACTIVITI_PROCESSLIST_DIRECTIVES: [any] = [ ActivitiProcessInstanceHeader, ActivitiProcessInstanceTasks, ActivitiComments, - ActivitiStartProcessButton + ActivitiStartProcessInstance, + ActivitiStartProcessInstanceDialog ]; export const ACTIVITI_PROCESSLIST_PROVIDERS: [any] = [ diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.css b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.css new file mode 100644 index 0000000000..04ff1685d2 --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.css @@ -0,0 +1,12 @@ +:host { + width: 100%; +} + +.mdl-dialog { + width: 400px; + width: -moz-fit-content; + width: -webkit-fit-content; + width: -ms-fit-content; + width: -o-fit-content; + width: fit-content; +} diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.html b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.html new file mode 100644 index 0000000000..d0271c5284 --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.html @@ -0,0 +1,12 @@ + + + +

{{'START_PROCESS.DIALOG.TITLE'|translate}}

+
+ +
+
+ + +
+
diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.spec.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.spec.ts new file mode 100644 index 0000000000..6fd482a8a0 --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.spec.ts @@ -0,0 +1,171 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; +import { Observable } from 'rxjs/Rx'; +import { AlfrescoTranslationService, CoreModule } from 'ng2-alfresco-core'; +import { ActivitiFormModule, FormService } from 'ng2-activiti-form'; + +import { TranslationMock } from './../assets/translation.service.mock'; +import { newProcess, fakeProcessDefs, taskFormMock } from './../assets/activiti-start-process.component.mock'; +import { ActivitiStartProcessInstance } from './activiti-start-process.component'; +import { ActivitiStartProcessInstanceDialog } from './activiti-start-process-dialog.component'; +import { ActivitiProcessService } from '../services/activiti-process.service'; + +describe('ActivitiStartProcessInstanceDialog', () => { + + let componentHandler: any; + let component: ActivitiStartProcessInstanceDialog; + let fixture: ComponentFixture; + let processService: ActivitiProcessService; + let formService: FormService; + let getDefinitionsSpy: jasmine.Spy; + let getStartFormDefinitionSpy: jasmine.Spy; + let startProcessSpy: jasmine.Spy; + let debugElement: DebugElement; + + const showBtnSelector = '[data-automation-id="btn-show"]'; + const startBtnSelector = '[data-automation-id="btn-start"]'; + const closeBtnSelector = '[data-automation-id="btn-close"]'; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ CoreModule, ActivitiFormModule ], + declarations: [ + ActivitiStartProcessInstance, + ActivitiStartProcessInstanceDialog + ], + providers: [ + { provide: AlfrescoTranslationService, useClass: TranslationMock }, + ActivitiProcessService + ] + }).compileComponents(); + })); + + beforeEach(() => { + + fixture = TestBed.createComponent(ActivitiStartProcessInstanceDialog); + component = fixture.componentInstance; + debugElement = fixture.debugElement; + processService = fixture.debugElement.injector.get(ActivitiProcessService); + formService = fixture.debugElement.injector.get(FormService); + + getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions').and.returnValue(Observable.of(fakeProcessDefs)); + startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(Observable.of(newProcess)); + getStartFormDefinitionSpy = spyOn(formService, 'getStartFormDefinition').and.returnValue(Observable.of(taskFormMock)); + + componentHandler = jasmine.createSpyObj('componentHandler', [ + 'upgradeAllRegistered', + 'upgradeElement' + ]); + window['componentHandler'] = componentHandler; + }); + + describe('render dialog', () => { + + let buttonEl: DebugElement; + + it('should render a Start Process button by default', () => { + fixture.detectChanges(); + buttonEl = debugElement.query(By.css(showBtnSelector)); + expect(buttonEl).not.toBeNull(); + }); + + it('should not render the Start Process button when configured not to', () => { + component.showButton = false; + fixture.detectChanges(); + buttonEl = debugElement.query(By.css(showBtnSelector)); + expect(buttonEl).toBeNull(); + }); + + }); + + describe('open dialog', () => { + + it('should open dialog when button clicked', () => { + fixture.detectChanges(); + let showModalSpy = spyOn(component.dialog.nativeElement, 'showModal'); + let showButton: DebugElement = debugElement.query(By.css(showBtnSelector)); + showButton.triggerEventHandler('click', null); + expect(showModalSpy).toHaveBeenCalled(); + }); + + }); + + describe('start process', () => { + + it('should output start event when process started from inside inner component', () => { + let emitSpy = spyOn(component.start, 'emit'); + component.startProcessComponent.start.emit(newProcess); + expect(emitSpy).toHaveBeenCalledWith(newProcess); + }); + + it('should call inner component to start process when dialog Start button clicked', () => { + let startSpy = spyOn(component.startProcessComponent, 'startProcess').and.returnValue(null); + let closeButton: DebugElement = debugElement.query(By.css(startBtnSelector)); + closeButton.triggerEventHandler('click', null); + expect(startSpy).toHaveBeenCalled(); + }); + + }); + + describe('close dialog', () => { + + let dialogPolyfill: any; + + let setupDialog = () => { + component.showDialog(); + fixture.detectChanges(); + }; + + let clickCancelButton = () => { + let closeButton: DebugElement = debugElement.query(By.css(closeBtnSelector)); + closeButton.triggerEventHandler('click', null); + }; + + beforeEach(() => { + dialogPolyfill = { registerDialog: (widget) => widget.showModal = () => {} }; + dialogPolyfill.registerDialog = spyOn(dialogPolyfill, 'registerDialog').and.callThrough(); + window['dialogPolyfill'] = dialogPolyfill; + }); + + it('should close dialog when close button clicked', async(() => { + let closeSpy = spyOn(component.dialog.nativeElement, 'close'); + setupDialog(); + clickCancelButton(); + expect(closeSpy).toHaveBeenCalled(); + })); + + it('should reset embedded component when dialog cancelled', async(() => { + let resetSpy = spyOn(component.startProcessComponent, 'reset'); + setupDialog(); + clickCancelButton(); + expect(resetSpy).toHaveBeenCalled(); + })); + + it('should register dialog via polyfill', () => { + fixture.detectChanges(); + component.dialog.nativeElement.showModal = null; + component.showDialog(); + expect(dialogPolyfill.registerDialog).toHaveBeenCalledWith(component.dialog.nativeElement); + }); + + }); + +}); diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.ts new file mode 100644 index 0000000000..03cbc8df82 --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process-dialog.component.ts @@ -0,0 +1,78 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, EventEmitter, Input, Output, ViewChild, DebugElement } from '@angular/core'; +import { AlfrescoTranslationService } from 'ng2-alfresco-core'; +import { ActivitiStartProcessInstance } from './activiti-start-process.component'; +import { ProcessInstance } from './../models/process-instance.model'; + +declare let componentHandler: any; +declare let dialogPolyfill: any; + +@Component({ + selector: 'activiti-start-process-dialog', + moduleId: module.id, + templateUrl: './activiti-start-process-dialog.component.html', + styleUrls: ['./activiti-start-process-dialog.component.css'] +}) +export class ActivitiStartProcessInstanceDialog { + + @Input() + appId: string; + + @Input() + showButton: boolean = true; + + @Output() + start: EventEmitter = new EventEmitter(); + + @ViewChild('dialog') + dialog: DebugElement; + + @ViewChild(ActivitiStartProcessInstance) + startProcessComponent: ActivitiStartProcessInstance; + + constructor(private translate: AlfrescoTranslationService) { + if (translate) { + translate.addTranslationFolder('node_modules/ng2-activiti-processlist/src'); + } + } + + public showDialog() { + if (!this.dialog.nativeElement.showModal) { + dialogPolyfill.registerDialog(this.dialog.nativeElement); + } + this.dialog.nativeElement.showModal(); + } + + validateForm() { + return this.startProcessComponent.validateForm(); + } + + public startProcess() { + this.startProcessComponent.startProcess(); + } + + public closeDialog() { + this.startProcessComponent.reset(); + this.dialog.nativeElement.close(); + } + + onStartProcessInstance(processInstance: ProcessInstance) { + this.start.emit(processInstance); + } +} diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.css b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.css index d4d5dea1a8..aa8c5eb752 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.css +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.css @@ -10,10 +10,6 @@ color: rgb(255, 152, 0); } -.mdl-dialog { - width: 400px; -} - .mdl-textfield.alf-mdl-selectfield label { color: rgba(0,0,0,.54); font-size: 12px; @@ -26,9 +22,10 @@ margin-bottom: 20px; } -.mdl-card .mdl-dialog__content { +.mdl-card .mdl-card__supporting-text { width: 100%; padding: 20px; + box-sizing: inherit; } .mdl-dialog { diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.html b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.html index 46db03fb2e..7026fbc354 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.html +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.html @@ -1,8 +1,5 @@ - - - -

{{'START_PROCESS.DIALOG.TITLE'|translate}}

-
+
+
{{errorMessageId|translate}}
@@ -21,15 +18,13 @@
+ (formSaved)='onFormSaved($event)' + (formCompleted)='onFormCompleted($event)' + (formLoaded)='onFormLoaded($event)' + (onError)='onFormError($event)'>
-
+
-
-
+
diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.spec.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.spec.ts index 9dc1b5dee9..b6b833e34d 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.spec.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.spec.ts @@ -24,14 +24,14 @@ import { ActivitiFormModule, FormService } from 'ng2-activiti-form'; import { TranslationMock } from './../assets/translation.service.mock'; import { newProcess, fakeProcessDefs, fakeProcessDefWithForm, taskFormMock } from './../assets/activiti-start-process.component.mock'; -import { ActivitiStartProcessButton } from './activiti-start-process.component'; +import { ActivitiStartProcessInstance } from './activiti-start-process.component'; import { ActivitiProcessService } from '../services/activiti-process.service'; -describe('ActivitiStartProcessButton', () => { +describe('ActivitiStartProcessInstance', () => { let componentHandler: any; - let component: ActivitiStartProcessButton; - let fixture: ComponentFixture; + let component: ActivitiStartProcessInstance; + let fixture: ComponentFixture; let processService: ActivitiProcessService; let formService: FormService; let getDefinitionsSpy: jasmine.Spy; @@ -43,7 +43,7 @@ describe('ActivitiStartProcessButton', () => { TestBed.configureTestingModule({ imports: [ CoreModule, ActivitiFormModule ], declarations: [ - ActivitiStartProcessButton + ActivitiStartProcessInstance ], providers: [ { provide: AlfrescoTranslationService, useClass: TranslationMock }, @@ -55,7 +55,7 @@ describe('ActivitiStartProcessButton', () => { beforeEach(() => { - fixture = TestBed.createComponent(ActivitiStartProcessButton); + fixture = TestBed.createComponent(ActivitiStartProcessInstance); component = fixture.componentInstance; debugElement = fixture.debugElement; processService = fixture.debugElement.injector.get(ActivitiProcessService); @@ -148,7 +148,6 @@ describe('ActivitiStartProcessButton', () => { beforeEach(() => { component.name = 'My new process'; - component.showDialog(); fixture.detectChanges(); }); @@ -218,7 +217,6 @@ describe('ActivitiStartProcessButton', () => { beforeEach(async(() => { component.name = 'My new process'; - component.showDialog(); fixture.detectChanges(); component.onProcessDefChange('my:process1'); fixture.whenStable(); @@ -248,7 +246,6 @@ describe('ActivitiStartProcessButton', () => { beforeEach(() => { getDefinitionsSpy.and.returnValue(Observable.of(fakeProcessDefWithForm)); - component.showDialog(); fixture.detectChanges(); component.onProcessDefChange('my:process1'); fixture.detectChanges(); @@ -275,67 +272,4 @@ describe('ActivitiStartProcessButton', () => { }); - describe('cancel dialog', () => { - - let dialogPolyfill: any; - - let setupDialog = (definitions?: any) => { - if (definitions) { - getDefinitionsSpy.and.returnValue(Observable.of(definitions)); - } - component.showDialog(); - fixture.detectChanges(); - component.onProcessDefChange('my:process1'); - fixture.detectChanges(); - }; - - let clickCancelButton = () => { - let closeButton: DebugElement = debugElement.query(By.css('[data-automation-id="btn-close"]')); - closeButton.triggerEventHandler('click', null); - }; - - beforeEach(() => { - dialogPolyfill = { registerDialog: (widget) => widget.showModal = () => {} }; - dialogPolyfill.registerDialog = spyOn(dialogPolyfill, 'registerDialog').and.callThrough(); - window['dialogPolyfill'] = dialogPolyfill; - }); - - it('should close dialog when cancel button clicked', async(() => { - let closeSpy = spyOn(component.dialog.nativeElement, 'close'); - component.showDialog(); - fixture.detectChanges(); - clickCancelButton(); - expect(closeSpy).toHaveBeenCalled(); - })); - - it('should clear processId and name when dialog cancelled', async(() => { - setupDialog(); - clickCancelButton(); - expect(component.currentProcessDef.id).toBeNull(); - expect(component.name).toBe(''); - })); - - it('should clear form values when dialog cancelled', async(() => { - setupDialog(fakeProcessDefWithForm); - fixture.whenStable().then(() => { - component.startForm.data = { - language: 'fr', - upload: {} - }; - fixture.detectChanges(); - clickCancelButton(); - expect(component.startForm.data['language']).toBeUndefined(); - expect(component.startForm.data['upload']).toBeUndefined(); - }); - })); - - it('should register dialog via polyfill', () => { - fixture.detectChanges(); - component.dialog.nativeElement.showModal = null; - component.showDialog(); - expect(dialogPolyfill.registerDialog).toHaveBeenCalledWith(component.dialog.nativeElement); - }); - - }); - }); diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.ts index 7a87a6b6be..6ccd4b936e 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, Output, OnInit, ViewChild, DebugElement, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, Input, Output, OnInit, ViewChild, OnChanges, SimpleChanges } from '@angular/core'; import { AlfrescoTranslationService } from 'ng2-alfresco-core'; import { ActivitiStartForm } from 'ng2-activiti-form'; import { ProcessInstance } from './../models/process-instance.model'; @@ -26,23 +26,23 @@ declare let componentHandler: any; declare let dialogPolyfill: any; @Component({ - selector: 'activiti-start-process-instance', + selector: 'activiti-start-process', moduleId: module.id, templateUrl: './activiti-start-process.component.html', styleUrls: ['./activiti-start-process.component.css'] }) -export class ActivitiStartProcessButton implements OnInit, OnChanges { +export class ActivitiStartProcessInstance implements OnInit, OnChanges { @Input() appId: string; + @Input() + showStartButton: boolean = true; + @Output() start: EventEmitter = new EventEmitter(); - @ViewChild('dialog') - dialog: DebugElement; - - @ViewChild('startForm') + @ViewChild(ActivitiStartForm) startForm: ActivitiStartForm; processDefinitions: ProcessDefinitionRepresentation[] = []; @@ -86,13 +86,6 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges { ); } - public showDialog() { - if (!this.dialog.nativeElement.showModal) { - dialogPolyfill.registerDialog(this.dialog.nativeElement); - } - this.dialog.nativeElement.showModal(); - } - public startProcess() { if (this.currentProcessDef.id && this.name) { this.resetErrorMessage(); @@ -101,7 +94,6 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges { (res) => { this.name = ''; this.start.emit(res); - this.cancel(); }, (err) => { this.errorMessageId = 'START_PROCESS.ERROR.START'; @@ -111,11 +103,6 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges { } } - public cancel() { - this.reset(); - this.dialog.nativeElement.close(); - } - onProcessDefChange(processDefinitionId) { let processDef = this.processDefinitions.find((processDefinition) => { return processDefinition.id === processDefinitionId; @@ -147,7 +134,7 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges { this.errorMessageId = ''; } - private reset() { + public reset() { this.resetSelectedProcessDefinition(); this.name = ''; if (this.startForm) {