diff --git a/.travis.yml b/.travis.yml index 3aca566204c..d1a00422a28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ env: - MODULE=ng2-activiti-diagrams - MODULE=ng2-activiti-analytics - MODULE=ng2-alfresco-userinfo + - MODULE=ng2-alfresco-social before_script: - if ([ "$MODULE" != "ng2-alfresco-core" ]); then diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index ddd23ad44e2..00000000000 --- a/Jenkinsfile +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/groovy -@Library('github.com/fabric8io/fabric8-pipeline-library@v2.2.311') -def utils = new io.fabric8.Utils() -clientsNode{ - def envStage = utils.environmentNamespace('staging') - def envProd = utils.environmentNamespace('production') - def newVersion = '' - - git 'https://github.com/Alfresco/alfresco-ng2-components.git' - - stage 'Canary release' - echo 'NOTE: running pipelines for the first time will take longer as build and base docker images are pulled onto the node' - if (!fileExists ('Dockerfile')) { - writeFile file: 'Dockerfile', text: 'FROM node:5.3-onbuild' - } - - newVersion = performCanaryRelease {} - - def rc = getKubernetesJson { - port = 8080 - label = 'node' - icon = 'https://cdn.rawgit.com/fabric8io/fabric8/dc05040/website/src/images/logos/nodejs.svg' - version = newVersion - imageName = clusterImageName - } - - stage 'Rollout Staging' - kubernetesApply(file: rc, environment: envStage) - - stage 'Approve' - approve{ - room = null - version = canaryVersion - console = fabric8Console - environment = envStage - } - - stage 'Rollout Production' - kubernetesApply(file: rc, environment: envProd) - -} diff --git a/appveyor.yml b/appveyor.yml index 1ff6eb83878..2e00efed227 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,6 +24,7 @@ environment: - COMPONENT_NAME: ng2-activiti-diagrams - COMPONENT_NAME: ng2-activiti-analytics - COMPONENT_NAME: ng2-alfresco-userinfo + - COMPONENT_NAME: ng2-alfresco-social # Install scripts. (runs after repo cloning) install: diff --git a/demo-shell-ng2/app/app.component.html b/demo-shell-ng2/app/app.component.html index 131f0aeb306..d052ae1f880 100644 --- a/demo-shell-ng2/app/app.component.html +++ b/demo-shell-ng2/app/app.component.html @@ -58,6 +58,7 @@ Process Services Webscript Tag + Social About Settings diff --git a/demo-shell-ng2/app/app.module.ts b/demo-shell-ng2/app/app.module.ts index 0cc24ac8642..e7d6029f49c 100644 --- a/demo-shell-ng2/app/app.module.ts +++ b/demo-shell-ng2/app/app.module.ts @@ -25,6 +25,7 @@ import { DataTableModule } from 'ng2-alfresco-datatable'; import { DocumentListModule } from 'ng2-alfresco-documentlist'; import { UploadModule } from 'ng2-alfresco-upload'; import { TagModule } from 'ng2-alfresco-tag'; +import { SocialModule } from 'ng2-alfresco-social'; import { WebScriptModule } from 'ng2-alfresco-webscript'; import { ViewerModule } from 'ng2-alfresco-viewer'; import { ActivitiFormModule } from 'ng2-activiti-form'; @@ -50,6 +51,7 @@ import { FormViewer, WebscriptComponent, TagComponent, + SocialComponent, AboutComponent, FilesComponent, FormNodeViewer, @@ -68,6 +70,7 @@ import { DocumentListModule.forRoot(), UploadModule.forRoot(), TagModule.forRoot(), + SocialModule.forRoot(), WebScriptModule.forRoot(), ViewerModule.forRoot(), ActivitiFormModule.forRoot(), @@ -91,6 +94,7 @@ import { FormViewer, WebscriptComponent, TagComponent, + SocialComponent, AboutComponent, FilesComponent, FormNodeViewer, diff --git a/demo-shell-ng2/app/app.routes.ts b/demo-shell-ng2/app/app.routes.ts index 139c70467b2..08ff178d0f6 100644 --- a/demo-shell-ng2/app/app.routes.ts +++ b/demo-shell-ng2/app/app.routes.ts @@ -29,6 +29,7 @@ import { ActivitiAppsView, WebscriptComponent, TagComponent, + SocialComponent, AboutComponent, FormViewer, FormNodeViewer, @@ -117,6 +118,11 @@ export const appRoutes: Routes = [ component: TagComponent, canActivate: [AuthGuardEcm] }, + { + path: 'social', + component: SocialComponent, + canActivate: [AuthGuardEcm] + }, { path: 'about', component: AboutComponent }, { path: 'settings', component: SettingComponent } ]; diff --git a/demo-shell-ng2/app/components/activiti/activiti-demo.component.css b/demo-shell-ng2/app/components/activiti/activiti-demo.component.css index 4eeff07a017..771705f19e6 100644 --- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.css +++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.css @@ -21,6 +21,6 @@ } .list-buttons { - text-align: right; + text-align: left; margin-bottom: 5px; -} \ No newline at end of file +} 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 46e3349d226..9599799d100 100644 --- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.html +++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.html @@ -19,20 +19,25 @@
- - -
-
+ + + + + + +
+
+ (taskCreated)="onTaskCreated($event)" + (taskDeleted)="onTaskDeleted($event)">
@@ -76,13 +83,6 @@
- - -
-
+ + + + + + +
+
+ [versioning] = "versioning"> @@ -21,19 +20,29 @@ [allowDropFiles]="true" (error)="onNavigationError($event)" (success)="resetError()" - (preview)="showFile($event)"> + (preview)="showFile($event)" + (permissionError)="onPermissionsFailed($event)"> - + + + + + + @@ -92,6 +104,9 @@ @@ -107,64 +122,65 @@ -

- -

- - -

- -

- -

- -

- -

- -

- -
Upload
-
-
- -
-
-
- -
-
-
-
- -
-
+
+
+ Multiple File Upload +
+ +
+ Folder upload +
+ +
+ Custom extensions filter +
+ +
+ Enable versioning +
+ +
+ Disable when user has no permissions +
+ +
Upload
+
+ + + +
+
+ + +
+
+ + +
+
+ Enable upload (demoing enabled/disabled state) +
+
diff --git a/demo-shell-ng2/app/components/files/files.component.ts b/demo-shell-ng2/app/components/files/files.component.ts index 077260db550..639e089cd83 100644 --- a/demo-shell-ng2/app/components/files/files.component.ts +++ b/demo-shell-ng2/app/components/files/files.component.ts @@ -15,39 +15,63 @@ * limitations under the License. */ -import { Component, OnInit, Optional, ViewChild, ChangeDetectorRef } from '@angular/core'; +import { Component, Input, OnInit, AfterViewInit, Optional, ViewChild, ChangeDetectorRef } from '@angular/core'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { AlfrescoAuthenticationService, LogService } from 'ng2-alfresco-core'; +import { AlfrescoAuthenticationService, LogService, NotificationService } from 'ng2-alfresco-core'; import { DocumentActionsService, DocumentListComponent, ContentActionHandler, DocumentActionModel, FolderActionModel } from 'ng2-alfresco-documentlist'; import { FormService } from 'ng2-activiti-form'; +import { UploadButtonComponent, UploadDragAreaComponent } from 'ng2-alfresco-upload'; @Component({ selector: 'files-component', templateUrl: './files.component.html', styleUrls: ['./files.component.css'] }) -export class FilesComponent implements OnInit { +export class FilesComponent implements OnInit, AfterViewInit { // The identifier of a node. You can also use one of these well-known aliases: -my- | -shared- | -root- currentFolderId: string = '-my-'; errorMessage: string = null; fileNodeId: any; fileShowed: boolean = false; + + @Input() multipleFileUpload: boolean = false; + + @Input() + disableWithNoPermission: boolean = false; + + @Input() folderUpload: boolean = false; + + @Input() acceptedFilesTypeShow: boolean = false; + + @Input() versioning: boolean = false; + + @Input() acceptedFilesType: string = '.jpg,.pdf,.js'; + @Input() + enableUpload: boolean = true; + @ViewChild(DocumentListComponent) documentList: DocumentListComponent; + @ViewChild(UploadButtonComponent) + uploadButton: UploadButtonComponent; + + @ViewChild(UploadDragAreaComponent) + uploadDragArea: UploadDragAreaComponent; + constructor(private documentActions: DocumentActionsService, private authService: AlfrescoAuthenticationService, private formService: FormService, private logService: LogService, private changeDetector: ChangeDetectorRef, private router: Router, + private notificationService: NotificationService, @Optional() private route: ActivatedRoute) { documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this)); } @@ -73,27 +97,12 @@ export class FilesComponent implements OnInit { } } - toggleMultipleFileUpload() { - this.multipleFileUpload = !this.multipleFileUpload; - return this.multipleFileUpload; - } - toggleFolder() { this.multipleFileUpload = false; this.folderUpload = !this.folderUpload; return this.folderUpload; } - toggleAcceptedFilesType() { - this.acceptedFilesTypeShow = !this.acceptedFilesTypeShow; - return this.acceptedFilesTypeShow; - } - - toggleVersioning() { - this.versioning = !this.versioning; - return this.versioning; - } - ngOnInit() { if (this.route) { this.route.params.forEach((params: Params) => { @@ -113,6 +122,20 @@ export class FilesComponent implements OnInit { } } + ngAfterViewInit() { + this.uploadButton.onSuccess + .debounceTime(100) + .subscribe((event) => { + this.reload(event); + }); + + this.uploadDragArea.onSuccess + .debounceTime(100) + .subscribe((event) => { + this.reload(event); + }); + } + viewActivitiForm(event?: any) { this.router.navigate(['/activiti/tasksnode', event.value.entry.id]); } @@ -146,4 +169,20 @@ export class FilesComponent implements OnInit { window.alert(`Starting BPM process: ${processDefinition.id}`); }.bind(this); } + + onPermissionsFailed(event: any) { + this.notificationService.openSnackMessage(`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000); + } + + onUploadPermissionFailed(event: any) { + this.notificationService.openSnackMessage(`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000); + } + + reload(event: any) { + if (event && event.value && event.value.entry && event.value.entry.parentId) { + if (this.documentList.currentFolderId === event.value.entry.parentId) { + this.documentList.reload(); + } + } + } } diff --git a/demo-shell-ng2/app/components/index.ts b/demo-shell-ng2/app/components/index.ts index fb66f819f6c..95e9466cd1c 100644 --- a/demo-shell-ng2/app/components/index.ts +++ b/demo-shell-ng2/app/components/index.ts @@ -24,6 +24,7 @@ export { ActivitiDemoComponent } from './activiti/activiti-demo.component'; export { FormViewer } from './activiti/form-viewer.component'; export { WebscriptComponent } from './webscript/webscript.component'; export { TagComponent } from './tag/tag.component'; +export { SocialComponent } from './social/social.component'; export { AboutComponent } from './about/about.component'; export { FilesComponent } from './files/files.component'; export { FormNodeViewer } from './activiti/form-node-viewer.component'; diff --git a/demo-shell-ng2/app/components/setting/setting.component.css b/demo-shell-ng2/app/components/setting/setting.component.css index 0e576c8af9e..91992ec4aa0 100644 --- a/demo-shell-ng2/app/components/setting/setting.component.css +++ b/demo-shell-ng2/app/components/setting/setting.component.css @@ -29,3 +29,7 @@ .table-row { display: table-row; } + +.adf-setting-input-padding{ + padding-top: 0px !important; +} diff --git a/demo-shell-ng2/app/components/setting/setting.component.html b/demo-shell-ng2/app/components/setting/setting.component.html index 2184da7ff89..7c1a19d69c9 100644 --- a/demo-shell-ng2/app/components/setting/setting.component.html +++ b/demo-shell-ng2/app/components/setting/setting.component.html @@ -10,18 +10,31 @@

SETTINGS

Content Services host URL configuration
Process Services host URL configuration
diff --git a/demo-shell-ng2/app/components/setting/setting.component.ts b/demo-shell-ng2/app/components/setting/setting.component.ts index b0d7d17a53b..733007344c1 100644 --- a/demo-shell-ng2/app/components/setting/setting.component.ts +++ b/demo-shell-ng2/app/components/setting/setting.component.ts @@ -15,15 +15,17 @@ * limitations under the License. */ -import { Component } from '@angular/core'; +import { Component, AfterViewChecked } from '@angular/core'; import { AlfrescoSettingsService, StorageService, LogService } from 'ng2-alfresco-core'; +declare var componentHandler: any; + @Component({ selector: 'alfresco-setting-demo', templateUrl: './setting.component.html', styleUrls: ['./setting.component.css'] }) -export class SettingComponent { +export class SettingComponent implements AfterViewChecked { ecmHost: string; bpmHost: string; @@ -35,24 +37,39 @@ export class SettingComponent { this.bpmHost = this.settingsService.bpmHost; } + ngAfterViewChecked() { + // workaround for MDL issues with dynamic components + if (componentHandler) { + componentHandler.upgradeAllRegistered(); + } + } + public onChangeECMHost(event: KeyboardEvent): void { let value = (event.target).value.trim(); - if (value) { + if (value && this.isValidUrl(value)) { this.logService.info(`ECM host: ${value}`); this.ecmHost = value; this.settingsService.ecmHost = value; this.storage.setItem(`ecmHost`, value); + } else { + console.error('Ecm address does not match the pattern'); } } public onChangeBPMHost(event: KeyboardEvent): void { let value = (event.target).value.trim(); - if (value) { + if (value && this.isValidUrl(value)) { this.logService.info(`BPM host: ${value}`); this.bpmHost = value; this.settingsService.bpmHost = value; this.storage.setItem(`bpmHost`, value); + } else { + console.error('Bpm address does not match the pattern'); } } + isValidUrl(url: string) { + return /^(http|https):\/\/.*/.test(url); + } + } diff --git a/demo-shell-ng2/app/components/social/social.component.ts b/demo-shell-ng2/app/components/social/social.component.ts new file mode 100644 index 00000000000..af40a06f6c7 --- /dev/null +++ b/demo-shell-ng2/app/components/social/social.component.ts @@ -0,0 +1,39 @@ +/*! + * @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 } from '@angular/core'; + +@Component({ + selector: 'alfresco-social-demo', + template: ` +
+
+
+
+ Like component +
+
+ Rating component + +
+
+ ` +}) +export class SocialComponent { + + nodeId: string = '74cd8a96-8a21-47e5-9b3b-a1b3e296787d'; +} diff --git a/demo-shell-ng2/app/vendor.ts b/demo-shell-ng2/app/vendor.ts index 55f232dee51..91d7764380b 100644 --- a/demo-shell-ng2/app/vendor.ts +++ b/demo-shell-ng2/app/vendor.ts @@ -25,6 +25,7 @@ import 'ng2-alfresco-documentlist'; import 'ng2-alfresco-login'; import 'ng2-alfresco-search'; import 'ng2-alfresco-tag'; +import 'ng2-alfresco-social'; import 'ng2-alfresco-upload'; import 'ng2-alfresco-viewer'; import 'ng2-alfresco-webscript'; diff --git a/demo-shell-ng2/package.json b/demo-shell-ng2/package.json index 1dcccc870c3..e86868a891e 100644 --- a/demo-shell-ng2/package.json +++ b/demo-shell-ng2/package.json @@ -1,11 +1,11 @@ { "name": "Alfresco-Angular2-Demo", "description": "Demo shell for Alfresco Angular2 components", - "version": "1.3.0", + "version": "1.4.0", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings dist", - "start": "npm run server-versions && webpack-dev-server --progress --max_old_space_size=4096 --max_new_space_size=4096", + "start": "npm run tslint && npm run server-versions && webpack-dev-server --progress --max_old_space_size=4096 --max_new_space_size=4096", "start:dist": "wsrv -s dist/ -p 3000 -a 0.0.0.0", "clean-build": "rimraf 'app/{,**/}**.js' 'app/{,**/}**.js.map' 'app/{,**/}**.d.ts'", "test": "karma start", @@ -63,7 +63,7 @@ "@angular/platform-browser-dynamic": "2.2.2", "@angular/router": "3.2.2", "@angular/upgrade": "2.2.2", - "alfresco-js-api": "~1.3.0", + "alfresco-js-api": "~1.4.0", "chart.js": "2.5.0", "core-js": "2.4.1", "dialog-polyfill": "0.4.7", @@ -75,20 +75,21 @@ "md-date-time-picker": "2.2.0", "moment": "2.15.1", "ng2-3d-editor": "0.0.15", - "ng2-activiti-analytics": "1.3.0", - "ng2-activiti-form": "1.3.0", - "ng2-activiti-processlist": "1.3.0", - "ng2-activiti-tasklist": "1.3.0", - "ng2-alfresco-core": "1.3.0", - "ng2-alfresco-datatable": "1.3.0", - "ng2-alfresco-documentlist": "1.3.0", - "ng2-alfresco-login": "1.3.0", - "ng2-alfresco-search": "1.3.0", - "ng2-alfresco-tag": "1.3.0", - "ng2-alfresco-upload": "1.3.0", - "ng2-alfresco-userinfo": "1.3.0", - "ng2-alfresco-viewer": "1.3.0", - "ng2-alfresco-webscript": "1.3.0", + "ng2-activiti-analytics": "1.4.0", + "ng2-activiti-form": "1.4.0", + "ng2-activiti-processlist": "1.4.0", + "ng2-activiti-tasklist": "1.4.0", + "ng2-alfresco-core": "1.4.0", + "ng2-alfresco-datatable": "1.4.0", + "ng2-alfresco-documentlist": "1.4.0", + "ng2-alfresco-login": "1.4.0", + "ng2-alfresco-search": "1.4.0", + "ng2-alfresco-tag": "1.4.0", + "ng2-alfresco-social": "1.3.0", + "ng2-alfresco-upload": "1.4.0", + "ng2-alfresco-userinfo": "1.4.0", + "ng2-alfresco-viewer": "1.4.0", + "ng2-alfresco-webscript": "1.4.0", "ng2-charts": "1.5.0", "ng2-translate": "2.5.0", "pdfjs-dist": "1.5.404", diff --git a/fabric8.yml b/fabric8.yml deleted file mode 100644 index a4c33210d17..00000000000 --- a/fabric8.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -buildName: "adf2" -links: - Git: "https://github.com/Alfresco/alfresco-ng2-components.git" - Job: "http://192.168.64.3:31752/job/adf2" - Production: "http://10.0.0.114:80/kubernetes/pods?namespace=default-production" - Staging: "http://10.0.0.114:80/kubernetes/pods?namespace=default-staging" -environments: - Staging: "default-staging" - Production: "default-production" -useLocalFlow: true diff --git a/ng2-components/ng2-activiti-analytics/demo/package.json b/ng2-components/ng2-activiti-analytics/demo/package.json index 97163946ceb..5a39e014845 100644 --- a/ng2-components/ng2-activiti-analytics/demo/package.json +++ b/ng2-components/ng2-activiti-analytics/demo/package.json @@ -59,10 +59,10 @@ "moment": "2.15.1", "raphael": "^2.2.6", "ng2-translate": "2.5.0", - "alfresco-js-api": "~1.3.0", - "ng2-alfresco-core": "1.3.0", - "ng2-activiti-diagrams": "1.3.0", - "ng2-activiti-analytics": "1.3.0" + "alfresco-js-api": "~1.4.0", + "ng2-alfresco-core": "1.4.0", + "ng2-activiti-diagrams": "1.4.0", + "ng2-activiti-analytics": "1.4.0" }, "devDependencies": { "@types/jasmine": "^2.2.33", diff --git a/ng2-components/ng2-activiti-analytics/package.json b/ng2-components/ng2-activiti-analytics/package.json index 7c320334209..06f04794ed2 100644 --- a/ng2-components/ng2-activiti-analytics/package.json +++ b/ng2-components/ng2-activiti-analytics/package.json @@ -1,7 +1,7 @@ { "name": "ng2-activiti-analytics", "description": "Activiti Angular2 Analytics Component", - "version": "1.3.0", + "version": "1.4.0", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", @@ -52,14 +52,14 @@ "@angular/platform-browser": "2.2.2", "@angular/platform-browser-dynamic": "2.2.2", "@angular/router": "3.2.2", - "alfresco-js-api": "~1.3.0", + "alfresco-js-api": "~1.4.0", "chart.js": "2.5.0", "core-js": "2.4.1", "hammerjs": "2.0.8", "md-date-time-picker": "2.2.0", "moment": "2.15.1", - "ng2-activiti-diagrams": "1.3.0", - "ng2-alfresco-core": "1.3.0", + "ng2-activiti-diagrams": "1.4.0", + "ng2-alfresco-core": "1.4.0", "ng2-charts": "1.5.0", "ng2-translate": "2.5.0", "raphael": "2.2.7", diff --git a/ng2-components/ng2-activiti-analytics/src/components/analytics-report-parameters.component.spec.ts b/ng2-components/ng2-activiti-analytics/src/components/analytics-report-parameters.component.spec.ts index 89a292a9cc2..50f34e6b997 100644 --- a/ng2-components/ng2-activiti-analytics/src/components/analytics-report-parameters.component.spec.ts +++ b/ng2-components/ng2-activiti-analytics/src/components/analytics-report-parameters.component.spec.ts @@ -428,6 +428,7 @@ describe('AnalyticsReportParametersComponent', () => { }); describe('When the form is rendered correctly', () => { + let validForm: boolean = true; let values: any = { dateRange: { startDate: '2016-09-01', endDate: '2016-10-05' @@ -468,11 +469,17 @@ describe('AnalyticsReportParametersComponent', () => { fixture.whenStable().then(() => { component.toggleParameters(); component.reportId = '1'; - spyOn(component, 'isFormValid').and.returnValue(true); + spyOn(component, 'isFormValid').and.callFake(() => { + return validForm; + }); fixture.detectChanges(); }); })); + afterEach(() => { + validForm = true; + }); + it('Should be able to change the report title', async(() => { let title: HTMLElement = element.querySelector('h4'); title.click(); @@ -567,6 +574,52 @@ describe('AnalyticsReportParametersComponent', () => { contentType: 'json' }); })); + + it('Should hide export button if the form is not valid', async(() => { + let exportButton: HTMLButtonElement = element.querySelector('#export-button'); + expect(exportButton).toBeDefined(); + expect(exportButton).not.toBeNull(); + validForm = false; + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + exportButton = element.querySelector('#export-button'); + expect(exportButton).toBeNull(); + }); + })); + + it('Should hide save button if the form is not valid', async(() => { + let saveButton: HTMLButtonElement = element.querySelector('#save-button'); + expect(saveButton).toBeDefined(); + expect(saveButton).not.toBeNull(); + validForm = false; + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + saveButton = element.querySelector('#save-button'); + expect(saveButton).toBeNull(); + }); + })); + + it('Should show export and save button when the form became valid', async(() => { + validForm = false; + fixture.detectChanges(); + let saveButton: HTMLButtonElement = element.querySelector('#save-button'); + let exportButton: HTMLButtonElement = element.querySelector('#export-button'); + expect(saveButton).toBeNull(); + expect(exportButton).toBeNull(); + validForm = true; + fixture.whenStable().then(() => { + fixture.detectChanges(); + saveButton = element.querySelector('#save-button'); + exportButton = element.querySelector('#export-button'); + expect(saveButton).not.toBeNull(); + expect(saveButton).toBeDefined(); + expect(exportButton).not.toBeNull(); + expect(exportButton).toBeDefined(); + }); + })); }); + }); }); diff --git a/ng2-components/ng2-activiti-analytics/src/components/analytics-report-parameters.component.ts b/ng2-components/ng2-activiti-analytics/src/components/analytics-report-parameters.component.ts index cc1905f2b1f..bac24f8cdcd 100644 --- a/ng2-components/ng2-activiti-analytics/src/components/analytics-report-parameters.component.ts +++ b/ng2-components/ng2-activiti-analytics/src/components/analytics-report-parameters.component.ts @@ -25,9 +25,10 @@ import { SimpleChanges, OnDestroy, AfterViewChecked, + AfterContentChecked, ViewChild } from '@angular/core'; -import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; +import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms'; import * as moment from 'moment'; import { AlfrescoTranslationService, LogService, ContentService } from 'ng2-alfresco-core'; import { AnalyticsService } from '../services/analytics.service'; @@ -47,7 +48,7 @@ declare let dialogPolyfill: any; templateUrl: './analytics-report-parameters.component.html', styleUrls: ['./analytics-report-parameters.component.css'] }) -export class AnalyticsReportParametersComponent implements OnInit, OnChanges, OnDestroy, AfterViewChecked { +export class AnalyticsReportParametersComponent implements OnInit, OnChanges, OnDestroy, AfterViewChecked, AfterContentChecked { public static FORMAT_DATE_ACTIVITI: string = 'YYYY-MM-DD'; @@ -102,6 +103,7 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On private reportParamQuery: ReportQuery; private reportName: string; private hideParameters: boolean = true; + private formValidState: boolean = false; constructor(private translateService: AlfrescoTranslationService, private analyticsService: AnalyticsService, @@ -131,6 +133,9 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On ngOnChanges(changes: SimpleChanges) { this.isEditable = false; + if (this.reportForm) { + this.reportForm.reset(); + } let reportId = changes['reportId']; if (reportId && reportId.currentValue) { this.getReportParams(reportId.currentValue); @@ -147,42 +152,42 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On parameters.forEach((param: ReportParameterDetailsModel) => { switch (param.type) { case 'dateRange' : - formBuilderGroup.dateRange = new FormGroup({}); + formBuilderGroup.dateRange = new FormGroup({}, Validators.required); break; case 'processDefinition': formBuilderGroup.processDefGroup = new FormGroup({ - processDefinitionId: new FormControl() - }); + processDefinitionId: new FormControl(null, Validators.required, null) + }, Validators.required); break; case 'duration': formBuilderGroup.durationGroup = new FormGroup({ - duration: new FormControl() - }); + duration: new FormControl(null, Validators.required, null) + }, Validators.required); break; case 'dateInterval': formBuilderGroup.dateIntervalGroup = new FormGroup({ - dateRangeInterval: new FormControl() - }); + dateRangeInterval: new FormControl(null, Validators.required, null) + }, Validators.required); break; case 'boolean': formBuilderGroup.typeFilteringGroup = new FormGroup({ - typeFiltering: new FormControl() - }); + typeFiltering: new FormControl(null, Validators.required, null) + }, Validators.required); break; case 'task': formBuilderGroup.taskGroup = new FormGroup({ - taskName: new FormControl() - }); + taskName: new FormControl(null, Validators.required, null) + }, Validators.required); break; case 'integer': formBuilderGroup.processInstanceGroup = new FormGroup({ - slowProcessInstanceInteger: new FormControl() - }); + slowProcessInstanceInteger: new FormControl(null, Validators.required, null) + }, Validators.required); break; case 'status': formBuilderGroup.statusGroup = new FormGroup({ - status: new FormControl() - }); + status: new FormControl(null, Validators.required, null) + }, Validators.required); break; default: return; @@ -190,6 +195,7 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On }); this.reportForm = this.formBuilder.group(formBuilderGroup); this.reportForm.valueChanges.subscribe(data => this.onValueChanged(data)); + this.reportForm.statusChanges.subscribe(data => this.onStatusChanged(data)); } public getReportParams(reportId: string) { @@ -243,6 +249,12 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On } } + onStatusChanged(status: any) { + if (this.reportForm && !this.reportForm.pending && this.reportForm.dirty) { + this.formValidState = this.reportForm.valid; + } + } + public convertMomentDate(date: string) { return moment(date, AnalyticsReportParametersComponent.FORMAT_DATE_ACTIVITI, true) .format(AnalyticsReportParametersComponent.FORMAT_DATE_ACTIVITI) + 'T00:00:00.000Z'; @@ -346,14 +358,14 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On this.reportName = ''; } - isFormValid() { - return this.reportForm && this.reportForm.valid && this.reportForm.dirty; - } - isSaveAction() { return this.action === 'Save'; } + isFormValid() { + return this.reportForm && this.reportForm.dirty && this.reportForm.valid; + } + doExport(paramQuery: ReportQuery) { this.analyticsService.exportReportToCsv(this.reportId, paramQuery).subscribe( (data: any) => { @@ -375,12 +387,17 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On } ngAfterViewChecked() { - // workaround for MDL issues with dynamic components if (componentHandler) { componentHandler.upgradeAllRegistered(); } } + ngAfterContentChecked() { + if (this.reportForm && this.reportForm.valid) { + this.reportForm.markAsDirty(); + } + } + toggleParameters() { this.hideParameters = !this.hideParameters; } diff --git a/ng2-components/ng2-activiti-diagrams/demo/package.json b/ng2-components/ng2-activiti-diagrams/demo/package.json index 1f56dcf3ca2..5d5e770acdb 100644 --- a/ng2-components/ng2-activiti-diagrams/demo/package.json +++ b/ng2-components/ng2-activiti-diagrams/demo/package.json @@ -43,15 +43,15 @@ "@angular/material": "2.0.0-beta.1", "@angular/router": "3.2.2", "@angular/upgrade": "2.2.2", - "alfresco-js-api": "~1.3.0", + "alfresco-js-api": "~1.4.0", "core-js": "2.4.1", "dialog-polyfill": "0.4.7", "element.scrollintoviewifneeded-polyfill": "1.0.1", "intl": "1.2.4", "material-design-icons": "2.2.3", "material-design-lite": "1.2.1", - "ng2-activiti-diagrams": "1.3.0", - "ng2-alfresco-core": "1.3.0", + "ng2-activiti-diagrams": "1.4.0", + "ng2-alfresco-core": "1.4.0", "ng2-translate": "2.5.0", "raphael": "^2.2.6", "reflect-metadata": "0.1.10", diff --git a/ng2-components/ng2-activiti-diagrams/package.json b/ng2-components/ng2-activiti-diagrams/package.json index 1032411f615..562e8139f78 100644 --- a/ng2-components/ng2-activiti-diagrams/package.json +++ b/ng2-components/ng2-activiti-diagrams/package.json @@ -1,7 +1,7 @@ { "name": "ng2-activiti-diagrams", "description": "Activiti Angular2 Diagrams Component", - "version": "1.3.0", + "version": "1.4.0", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", @@ -48,10 +48,10 @@ "@angular/platform-browser": "2.2.2", "@angular/platform-browser-dynamic": "2.2.2", "@angular/router": "3.2.2", - "alfresco-js-api": "~1.3.0", + "alfresco-js-api": "~1.4.0", "core-js": "2.4.1", "hammerjs": "2.0.8", - "ng2-alfresco-core": "1.3.0", + "ng2-alfresco-core": "1.4.0", "ng2-translate": "2.5.0", "raphael": "^2.2.6", "reflect-metadata": "0.1.10", diff --git a/ng2-components/ng2-activiti-form/README.md b/ng2-components/ng2-activiti-form/README.md index 4a377ee56f8..e4017047527 100644 --- a/ng2-components/ng2-activiti-form/README.md +++ b/ng2-components/ng2-activiti-form/README.md @@ -438,10 +438,33 @@ class MyComponent { | getRestFieldValuesByProcessId | (processDefinitionId: string, field: string) | Observable\ | | | getRestFieldValuesColumnByProcessId | (processDefinitionId: string, field: string, column?: string) | Observable\ | | | getRestFieldValuesColumn | (taskId: string, field: string, column?: string) | Observable\ | | -| getWorkflowGroups\* | (filter: string, groupId?: string) | Observable\ | | -| getWorkflowUsers\* | (filter: string, groupId?: string) | Observable\ | | +| getWorkflowGroups\ | (filter: string, groupId?: string) | Observable\ | | +| getWorkflowUsers\ | (filter: string, groupId?: string) | Observable\ | | -\* _Uses private Activiti WebApp api_ +## Common scenarios + +### Changing field value based on another field + +Create a simple Form with a dropdown widget (id: `type`), and a multiline text (id: `description`). + +```ts +formService.formFieldValueChanged.subscribe((e: FormFieldEvent) => { + if (e.field.id === 'type') { + const fields: FormFieldModel[] = e.form.getFormFields(); + const description = fields.find(f => f.id === 'description'); + if (description != null) { + console.log(description); + description.value = 'Type set to ' + e.field.value; + } + } +}); +``` + +You subscribe to the `formFieldValueChanged` event and check whether event is raised for the `type` widget, then you search for a `description` widget and assign its value to some simple text. + +The result should be as following: + +![](docs/assets/form-service-sample-01.png) ## See also diff --git a/ng2-components/ng2-activiti-form/demo/package.json b/ng2-components/ng2-activiti-form/demo/package.json index ea982f06d17..b5aa8bf9de0 100644 --- a/ng2-components/ng2-activiti-form/demo/package.json +++ b/ng2-components/ng2-activiti-form/demo/package.json @@ -56,9 +56,9 @@ "moment": "2.15.1", "md-date-time-picker": "2.2.0", "ng2-translate": "2.5.0", - "alfresco-js-api": "~1.3.0", - "ng2-alfresco-core": "1.3.0", - "ng2-activiti-form": "1.3.0" + "alfresco-js-api": "~1.4.0", + "ng2-alfresco-core": "1.4.0", + "ng2-activiti-form": "1.4.0" }, "devDependencies": { "@types/jasmine": "^2.2.33", diff --git a/ng2-components/ng2-activiti-form/docs/assets/form-service-sample-01.png b/ng2-components/ng2-activiti-form/docs/assets/form-service-sample-01.png new file mode 100644 index 00000000000..479144ac25a Binary files /dev/null and b/ng2-components/ng2-activiti-form/docs/assets/form-service-sample-01.png differ diff --git a/ng2-components/ng2-activiti-form/package.json b/ng2-components/ng2-activiti-form/package.json index bfa63188fd8..23f7672e9d8 100644 --- a/ng2-components/ng2-activiti-form/package.json +++ b/ng2-components/ng2-activiti-form/package.json @@ -1,7 +1,7 @@ { "name": "ng2-activiti-form", "description": "Alfresco Activiti Form Component for Angular 2", - "version": "1.3.0", + "version": "1.4.0", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", @@ -55,12 +55,12 @@ "@angular/platform-browser": "2.2.2", "@angular/platform-browser-dynamic": "2.2.2", "@angular/router": "3.2.2", - "alfresco-js-api": "~1.3.0", + "alfresco-js-api": "~1.4.0", "core-js": "2.4.1", "hammerjs": "2.0.8", "md-date-time-picker": "2.2.0", "moment": "2.15.1", - "ng2-alfresco-core": "1.3.0", + "ng2-alfresco-core": "1.4.0", "ng2-translate": "2.5.0", "reflect-metadata": "0.1.10", "rxjs": "5.0.0-beta.12", diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts b/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts index e3ea619a737..0fa09bf4fd5 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts +++ b/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts @@ -26,7 +26,6 @@ import { FormEvent, FormErrorEvent } from './../events/index'; import { WidgetVisibilityService } from './../services/widget-visibility.service'; -declare let dialogPolyfill: any; declare var componentHandler: any; /** @@ -118,7 +117,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges { showSaveButton: boolean = true; @Input() - showDebugButton: boolean = true; + showDebugButton: boolean = false; @Input() readOnly: boolean = false; diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.spec.ts index 1ae3a0c1193..45b47162a5a 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.spec.ts @@ -121,6 +121,109 @@ describe('FormFieldModel', () => { expect(field.value).toBe('deferred'); }); + it('should parse the date with the default format (D-M-YYYY) if the display format is missing', () => { + let form = new FormModel(); + let field = new FormFieldModel(form, { + fieldType: 'FormFieldRepresentation', + id: 'mmddyyyy', + name: 'MM-DD-YYYY', + type: 'date', + value: '2017-04-28T00:00:00.000+0000', + required: false, + readOnly: false, + params: { + field: { + id: 'mmddyyyy', + name: 'MM-DD-YYYY', + type: 'date', + value: null, + required: false, + readOnly: false + } + } + }); + expect(field.value).toBe('28-4-2017'); + expect(form.values['mmddyyyy']).toEqual('2017-04-28T00:00:00.000Z'); + }); + + it('should parse the date with the format MM-DD-YYYY', () => { + let form = new FormModel(); + let field = new FormFieldModel(form, { + fieldType: 'FormFieldRepresentation', + id: 'mmddyyyy', + name: 'MM-DD-YYYY', + type: 'date', + value: '2017-04-28T00:00:00.000+0000', + required: false, + readOnly: false, + params: { + field: { + id: 'mmddyyyy', + name: 'MM-DD-YYYY', + type: 'date', + value: null, + required: false, + readOnly: false + } + }, + dateDisplayFormat: 'MM-DD-YYYY' + }); + expect(field.value).toBe('04-28-2017'); + expect(form.values['mmddyyyy']).toEqual('2017-04-28T00:00:00.000Z'); + }); + + it('should parse the date with the format MM-YY-DD', () => { + let form = new FormModel(); + let field = new FormFieldModel(form, { + fieldType: 'FormFieldRepresentation', + id: 'mmyydd', + name: 'MM-YY-DD', + type: 'date', + value: '2017-04-28T00:00:00.000+0000', + required: false, + readOnly: false, + params: { + field: { + id: 'mmyydd', + name: 'MM-YY-DD', + type: 'date', + value: null, + required: false, + readOnly: false + } + }, + dateDisplayFormat: 'MM-YY-DD' + }); + expect(field.value).toBe('04-17-28'); + expect(form.values['mmyydd']).toEqual('2017-04-28T00:00:00.000Z'); + }); + + it('should parse the date with the format DD-MM-YYYY', () => { + let form = new FormModel(); + let field = new FormFieldModel(form, { + fieldType: 'FormFieldRepresentation', + id: 'ddmmyyy', + name: 'DD-MM-YYYY', + type: 'date', + value: '2017-04-28T00:00:00.000+0000', + required: false, + readOnly: false, + params: { + field: { + id: 'ddmmyyy', + name: 'DD-MM-YYYY', + type: 'date', + value: null, + required: false, + readOnly: false + } + }, + dateDisplayFormat: 'DD-MM-YYYY' + }); + expect(field.value).toBe('28-04-2017'); + expect(form.values['ddmmyyy']).toEqual('2017-04-28T00:00:00.000Z'); + }); + it('should return the label of selected dropdown value ', () => { let field = new FormFieldModel(new FormModel(), { type: FormFieldTypes.DROPDOWN, diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts index cba2e957280..6a20425aeee 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts @@ -76,7 +76,7 @@ export class FormFieldModel extends FormWidgetModel { visibilityCondition: WidgetVisibilityModel = null; enableFractions: boolean = false; currency: string = null; - dateDisplayFormat: string = this.defaultDateFormat; + dateDisplayFormat: string = this.dateDisplayFormat || this.defaultDateFormat; // container model members numberOfColumns: number = 1; @@ -249,9 +249,14 @@ export class FormFieldModel extends FormWidgetModel { */ if (json.type === FormFieldTypes.DATE) { if (value) { - let d = moment(value.split('T')[0], 'YYYY-M-D'); - if (d.isValid()) { - value = d.format(this.dateDisplayFormat); + let dateValue; + if (NumberFieldValidator.isNumber(value)) { + dateValue = moment(value); + } else { + dateValue = moment(value.split('T')[0], 'YYYY-M-D'); + } + if (dateValue && dateValue.isValid()) { + value = dateValue.format(this.dateDisplayFormat); } } } @@ -307,9 +312,14 @@ export class FormFieldModel extends FormWidgetModel { } break; case FormFieldTypes.DATE: - let d = moment(this.value, this.dateDisplayFormat); - if (d.isValid()) { - this.form.values[this.id] = `${d.format('YYYY-MM-DD')}T00:00:00.000Z`; + let dateValue; + if (NumberFieldValidator.isNumber(this.value)) { + dateValue = moment(this.value); + } else { + dateValue = moment(this.value, this.dateDisplayFormat); + } + if (dateValue && dateValue.isValid()) { + this.form.values[this.id] = `${dateValue.format('YYYY-MM-DD')}T00:00:00.000Z`; } else { this.form.values[this.id] = null; } diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.html b/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.html index 375c3a884b4..cc370722a9e 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.html +++ b/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.html @@ -42,30 +42,7 @@
- -
-
{{field.name}}
-
- - - - - - - - - - - -
- {{column.name}} -
- {{ getCellValue(row, column) }} -
-
-
+
diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.spec.ts index 207e51f4a11..2cb5bee5b9a 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.spec.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { CoreModule, LogServiceMock } from 'ng2-alfresco-core'; import { Observable } from 'rxjs/Rx'; @@ -25,7 +26,6 @@ import { EcmModelService } from '../../../services/ecm-model.service'; import { FormFieldModel } from './../core/form-field.model'; import { FormFieldTypes } from '../core/form-field-types'; import { FormModel } from '../core/form.model'; -import { DynamicTableColumn, DynamicTableRow } from './../dynamic-table/dynamic-table.widget.model'; import { WidgetVisibilityService } from '../../../services/widget-visibility.service'; describe('DisplayValueWidget', () => { @@ -441,6 +441,65 @@ describe('DisplayValueWidget', () => { expect(widget.value).toBe(''); }); + it('should show the [DATE] field with the default format (D-M-YYYY) if the display format is missing', () => { + widget.field = new FormFieldModel(null, { + type: FormFieldTypes.DISPLAY_VALUE, + value: '1982-03-13T00:00:00.000Z', + params: { + field: { + type: FormFieldTypes.DATE + } + } + }); + widget.ngOnInit(); + expect(widget.value).toBe('13-3-1982'); + }); + + it('should show the [DATE] field with the custom display format (MM-DD-YYYY)', () => { + widget.field = new FormFieldModel(null, { + type: FormFieldTypes.DISPLAY_VALUE, + value: '1982-03-13T00:00:00.000Z', + dateDisplayFormat: 'MM-DD-YYYY', + params: { + field: { + type: FormFieldTypes.DATE + } + } + }); + widget.ngOnInit(); + expect(widget.value).toBe('03-13-1982'); + }); + + it('should show the [DATE] field with the custom display format (MM-YY-DD)', () => { + widget.field = new FormFieldModel(null, { + type: FormFieldTypes.DISPLAY_VALUE, + value: '1982-03-13T00:00:00.000Z', + dateDisplayFormat: 'MM-YY-DD', + params: { + field: { + type: FormFieldTypes.DATE + } + } + }); + widget.ngOnInit(); + expect(widget.value).toBe('03-82-13'); + }); + + it('should show the [DATE] field with the custom display format (DD-MM-YYYY)', () => { + widget.field = new FormFieldModel(null, { + type: FormFieldTypes.DISPLAY_VALUE, + value: '1982-03-13T00:00:00.000Z', + dateDisplayFormat: 'DD-MM-YYYY', + params: { + field: { + type: FormFieldTypes.DATE + } + } + }); + widget.ngOnInit(); + expect(widget.value).toBe('13-03-1982'); + }); + it('should not setup [DATE] field when missing value', () => { widget.field = new FormFieldModel(null, { type: FormFieldTypes.DISPLAY_VALUE, @@ -549,135 +608,6 @@ describe('DisplayValueWidget', () => { expect(widget.value).toBe(value); }); - it('should setup [DYNAMIC_TABLE] field', () => { - let columns = [{id: '1', visible: false}, {id: '2', visible: true}]; - let rows = [{}, {}]; - - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.DISPLAY_VALUE, - params: { - field: { - type: FormFieldTypes.DYNAMIC_TABLE - } - }, - columnDefinitions: columns, - value: rows - }); - widget.ngOnInit(); - - expect(widget.columns.length).toBe(2); - expect(widget.columns[0].id).toBe(columns[0].id); - expect(widget.columns[1].id).toBe(columns[1].id); - - expect(widget.visibleColumns.length).toBe(1); - expect(widget.visibleColumns[0].id).toBe(columns[1].id); - - expect(widget.rows.length).toBe(2); - }); - - it('should setup [DYNAMIC_TABLE] field with empty schema', () => { - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.DISPLAY_VALUE, - params: { - field: { - type: FormFieldTypes.DYNAMIC_TABLE - } - }, - columnDefinitions: null, - value: null - }); - widget.ngOnInit(); - - expect(widget.value).toBeNull(); - expect(widget.columns).toEqual([]); - expect(widget.rows).toEqual([]); - }); - - it('should retrieve default cell value', () => { - const value = ''; - let row = {value: {key: value}}; - let column = {id: 'key'}; - - expect(widget.getCellValue(row, column)).toBe(value); - }); - - it('should retrieve dropdown cell value', () => { - const value = {id: '1', name: 'one'}; - let row = {value: {key: value}}; - let column = {id: 'key', type: 'Dropdown'}; - - expect(widget.getCellValue(row, column)).toBe(value.name); - }); - - it('should fallback to empty cell value for dropdown', () => { - let row = {value: {}}; - let column = {id: 'key', type: 'Dropdown'}; - - expect(widget.getCellValue(row, column)).toBe(''); - }); - - it('should retrieve boolean cell value', () => { - let row1 = {value: {key: true}}; - let row2 = {value: {key: 'positive'}}; - let row3 = {value: {key: null}}; - let column = {id: 'key', type: 'Boolean'}; - - expect(widget.getCellValue(row1, column)).toBe(true); - expect(widget.getCellValue(row2, column)).toBe(true); - expect(widget.getCellValue(row3, column)).toBe(false); - }); - - it('should retrieve date cell value', () => { - const value = '2016-10-04T00:00:00.000Z'; - let row = {value: {key: value}}; - let column = {id: 'key', type: 'Date'}; - - expect(widget.getCellValue(row, column)).toBe('4-10-2016'); - }); - - it('should fallback to empty cell value for date', () => { - let row = {value: {}}; - let column = {id: 'key', type: 'Date'}; - - expect(widget.getCellValue(row, column)).toBe(''); - }); - - it('should retrieve empty text cell value', () => { - let row = {value: {}}; - let column = {id: 'key'}; - - expect(widget.getCellValue(row, column)).toBe(''); - }); - - it('should prepend default amount currency', () => { - const value = '10'; - let row = {value: {key: value}}; - let column = {id: 'key', type: 'Amount'}; - - const expected = `$ ${value}`; - expect(widget.getCellValue(row, column)).toBe(expected); - }); - - it('should prepend custom amount currency', () => { - const value = '10'; - const currency = 'GBP'; - let row = {value: {key: value}}; - let column = {id: 'key', type: 'Amount', amountCurrency: currency}; - - const expected = `${currency} ${value}`; - expect(widget.getCellValue(row, column)).toBe(expected); - }); - - it('should use zero for missing amount', () => { - const value = null; - const currency = 'GBP'; - let row = {value: {key: value}}; - let column = {id: 'key', type: 'Amount', amountCurrency: currency}; - - const expected = `${currency} 0`; - expect(widget.getCellValue(row, column)).toBe(expected); - }); - describe('UI check', () => { let widgetUI: DisplayValueWidget; let fixture: ComponentFixture; @@ -689,12 +619,16 @@ describe('DisplayValueWidget', () => { window['componentHandler'] = componentHandler; TestBed.configureTestingModule({ imports: [CoreModule], - declarations: [DisplayValueWidget, ActivitiContent], + declarations: [ + DisplayValueWidget, + ActivitiContent + ], providers: [ EcmModelService, FormService, WidgetVisibilityService - ] + ], + schemas: [ CUSTOM_ELEMENTS_SCHEMA ] }).compileComponents().then(() => { fixture = TestBed.createComponent(DisplayValueWidget); widgetUI = fixture.componentInstance; diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.ts b/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.ts index 12fcd4f0406..c1f16decf6b 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/display-value/display-value.widget.ts @@ -22,8 +22,8 @@ import { WidgetComponent } from './../widget.component'; import { FormFieldTypes } from '../core/form-field-types'; import { FormService } from '../../../services/form.service'; import { FormFieldOption } from './../core/form-field-option'; -import { DynamicTableColumn, DynamicTableRow } from './../dynamic-table/dynamic-table.widget.model'; import { WidgetVisibilityService } from '../../../services/widget-visibility.service'; +import { NumberFieldValidator } from '../core/form-field-validator'; @Component({ moduleId: module.id, @@ -42,9 +42,7 @@ export class DisplayValueWidget extends WidgetComponent implements OnInit { linkText: string; // dynamic table - rows: DynamicTableRow[] = []; - columns: DynamicTableColumn[] = []; - visibleColumns: DynamicTableColumn[] = []; + tableEditable = false; // upload/attach hasFile: boolean = false; @@ -64,6 +62,10 @@ export class DisplayValueWidget extends WidgetComponent implements OnInit { if (this.field.params['showDocumentContent'] !== undefined) { this.showDocumentContent = !!this.field.params['showDocumentContent']; } + if (this.field.params['tableEditable'] !== undefined) { + this.tableEditable = !!this.field.params['tableEditable']; + } + let originalField = this.field.params['field']; if (originalField && originalField.type) { this.fieldType = originalField.type; @@ -115,10 +117,15 @@ export class DisplayValueWidget extends WidgetComponent implements OnInit { break; case FormFieldTypes.DATE: if (this.value) { - let d = moment(this.value.split('T')[0], 'YYYY-M-D'); - if (d.isValid()) { - const displayFormat = originalField['dateDisplayFormat'] || this.field.defaultDateFormat; - this.value = d.format(displayFormat); + let dateValue; + if (NumberFieldValidator.isNumber(this.value)) { + dateValue = moment(this.value); + } else { + dateValue = moment(this.value.split('T')[0], 'YYYY-M-D'); + } + if (dateValue && dateValue.isValid()) { + const displayFormat = this.field.dateDisplayFormat || this.field.defaultDateFormat; + this.value = dateValue.format(displayFormat); } } break; @@ -132,16 +139,6 @@ export class DisplayValueWidget extends WidgetComponent implements OnInit { this.linkUrl = this.getHyperlinkUrl(this.field); this.linkText = this.getHyperlinkText(this.field); break; - case FormFieldTypes.DYNAMIC_TABLE: - let json = this.field.json; - if (json.columnDefinitions) { - this.columns = json.columnDefinitions.map(obj => obj); - this.visibleColumns = this.columns.filter(col => col.visible); - } - if (json.value) { - this.rows = json.value.map(obj => {selected: false, value: obj}); - } - break; default: this.value = this.field.value; break; @@ -216,31 +213,4 @@ export class DisplayValueWidget extends WidgetComponent implements OnInit { } ); } - - getCellValue(row: DynamicTableRow, column: DynamicTableColumn): any { - - let result = row.value[column.id]; - - if (column.type === 'Dropdown') { - if (result) { - return result.name; - } - } - - if (column.type === 'Boolean') { - return result ? true : false; - } - - if (column.type === 'Date') { - if (result) { - return moment(result.split('T')[0], 'YYYY-MM-DD').format('D-M-YYYY'); - } - } - - if (column.type === 'Amount') { - return (column.amountCurrency || '$') + ' ' + (result || 0); - } - - return result || ''; - } } diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/dynamic-table/dynamic-table.widget.html b/ng2-components/ng2-activiti-form/src/components/widgets/dynamic-table/dynamic-table.widget.html index cb78fcb0e80..7c5270ba6ac 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/dynamic-table/dynamic-table.widget.html +++ b/ng2-components/ng2-activiti-form/src/components/widgets/dynamic-table/dynamic-table.widget.html @@ -1,9 +1,9 @@
-
{{content.name}}
+
{{content.name}}
-
-
+
+
@@ -24,9 +24,9 @@
-
+
-
+
+ diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-apps.component.spec.ts b/ng2-components/ng2-activiti-tasklist/src/components/activiti-apps.component.spec.ts index dcaac27e5a9..28707b09dd9 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-apps.component.spec.ts +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-apps.component.spec.ts @@ -59,7 +59,7 @@ describe('ActivitiApps', () => { debugElement = fixture.debugElement; service = fixture.debugElement.injector.get(ActivitiTaskListService); - getAppsSpy = spyOn(service, 'getDeployedApplications').and.returnValue(Observable.of()); + getAppsSpy = spyOn(service, 'getDeployedApplications').and.returnValue(Observable.of(deployedApps)); componentHandler = jasmine.createSpyObj('componentHandler', [ 'upgradeAllRegistered', @@ -79,6 +79,59 @@ describe('ActivitiApps', () => { expect(getAppsSpy).toHaveBeenCalled(); }); + it('should show the apps filterd by defaultAppId', () => { + component.filtersAppId = [{defaultAppId: 'fake-app-1'}]; + fixture.detectChanges(); + expect(component.isEmpty()).toBe(false); + expect(component.appList).toBeDefined(); + expect(component.appList.length).toEqual(1); + }); + + it('should show the apps filterd by deploymentId', () => { + component.filtersAppId = [{deploymentId: '4'}]; + fixture.detectChanges(); + expect(component.isEmpty()).toBe(false); + expect(component.appList).toBeDefined(); + expect(component.appList.length).toEqual(1); + expect(component.appList[0].deploymentId).toEqual('4'); + }); + + it('should show the apps filterd by name', () => { + component.filtersAppId = [{name: 'App5'}]; + fixture.detectChanges(); + expect(component.isEmpty()).toBe(false); + expect(component.appList).toBeDefined(); + expect(component.appList.length).toEqual(1); + expect(component.appList[0].name).toEqual('App5'); + }); + + it('should show the apps filterd by id', () => { + component.filtersAppId = [{id: 6}]; + fixture.detectChanges(); + expect(component.isEmpty()).toBe(false); + expect(component.appList).toBeDefined(); + expect(component.appList.length).toEqual(1); + expect(component.appList[0].id).toEqual(6); + }); + + it('should show the apps filterd by modelId', () => { + component.filtersAppId = [{modelId: 66}]; + fixture.detectChanges(); + expect(component.isEmpty()).toBe(false); + expect(component.appList).toBeDefined(); + expect(component.appList.length).toEqual(2); + expect(component.appList[0].modelId).toEqual(66); + }); + + it('should show the apps filterd by tenandId', () => { + component.filtersAppId = [{tenantId: 9}]; + fixture.detectChanges(); + expect(component.isEmpty()).toBe(false); + expect(component.appList).toBeDefined(); + expect(component.appList.length).toEqual(2); + expect(component.appList[0].tenantId).toEqual(9); + }); + it('should emit an error when an error occurs loading apps', () => { let emitSpy = spyOn(component.error, 'emit'); getAppsSpy.and.returnValue(Observable.throw({})); @@ -119,7 +172,7 @@ describe('ActivitiApps', () => { it('should display all deployed apps', () => { getAppsSpy.and.returnValue(Observable.of(deployedApps)); fixture.detectChanges(); - expect(debugElement.queryAll(By.css('h1')).length).toBe(3); + expect(debugElement.queryAll(By.css('h1')).length).toBe(6); }); it('should not display undeployed apps', () => { diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-apps.component.ts b/ng2-components/ng2-activiti-tasklist/src/components/activiti-apps.component.ts index 82bd16a2be3..099bd5c4967 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-apps.component.ts +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-apps.component.ts @@ -45,6 +45,9 @@ export class ActivitiApps implements OnInit { @Input() layoutType: string = ActivitiApps.LAYOUT_GRID; + @Input() + filtersAppId: any[]; + @Output() appClick: EventEmitter = new EventEmitter(); @@ -90,6 +93,7 @@ export class ActivitiApps implements OnInit { private load() { this.activitiTaskList.getDeployedApplications().subscribe( (res) => { + res = this.filterApps(res); res.forEach((app: AppDefinitionRepresentationModel) => { if (app.defaultAppId === ActivitiApps.DEFAULT_TASKS_APP) { app.name = ActivitiApps.DEFAULT_TASKS_APP_NAME; @@ -125,6 +129,27 @@ export class ActivitiApps implements OnInit { return (this.currentApp !== undefined && appId === this.currentApp.id); } + private filterApps(apps: AppDefinitionRepresentationModel []): AppDefinitionRepresentationModel[] { + let filteredApps = []; + if (this.filtersAppId) { + apps.filter((app: AppDefinitionRepresentationModel) => { + this.filtersAppId.forEach((filter) => { + if (app.defaultAppId === filter.defaultAppId || + app.deploymentId === filter.deploymentId || + app.name === filter.name || + app.id === filter.id || + app.modelId === filter.modelId || + app.tenantId === filter.tenantId) { + filteredApps.push(app); + } + }); + }); + } else { + return apps; + } + return filteredApps; + } + /** * Check if the value of the layoutType property is an allowed value * @returns {boolean} diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-checklist.component.html b/ng2-components/ng2-activiti-tasklist/src/components/activiti-checklist.component.html index 1f74274e2b2..f62128faab2 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-checklist.component.html +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-checklist.component.html @@ -7,9 +7,11 @@
= new EventEmitter(); + @Output() + taskDeleted: EventEmitter = new EventEmitter(); + @Output() onError: EventEmitter = new EventEmitter(); @@ -258,6 +264,10 @@ export class ActivitiTaskDetails implements OnInit, OnChanges { this.taskCreated.emit(task); } + onChecklistTaskDeleted(taskId: string) { + this.taskDeleted.emit(taskId); + } + onFormError(error: any) { this.errorDialog.nativeElement.showModal(); this.onError.emit(error); diff --git a/ng2-components/ng2-activiti-tasklist/src/i18n/en.json b/ng2-components/ng2-activiti-tasklist/src/i18n/en.json index 7c79ccb57a8..87528d901e0 100644 --- a/ng2-components/ng2-activiti-tasklist/src/i18n/en.json +++ b/ng2-components/ng2-activiti-tasklist/src/i18n/en.json @@ -1,4 +1,7 @@ { + "APPS": { + "NONE": "No apps found." + }, "TASK_LIST": { "MESSAGES": { "NONE": "No tasks list found." diff --git a/ng2-components/ng2-activiti-tasklist/src/i18n/it.json b/ng2-components/ng2-activiti-tasklist/src/i18n/it.json index 2965bcdb398..8d33434f4df 100644 --- a/ng2-components/ng2-activiti-tasklist/src/i18n/it.json +++ b/ng2-components/ng2-activiti-tasklist/src/i18n/it.json @@ -1,4 +1,7 @@ { + "APPS": { + "NONE": "Nessuna applicazione trovata." + }, "TASK_LIST": { "MESSAGES": { "NONE": "Nessuna lista tasks trovata." diff --git a/ng2-components/ng2-activiti-tasklist/src/models/filter.model.ts b/ng2-components/ng2-activiti-tasklist/src/models/filter.model.ts index 7d56ba22997..1de4733fc37 100644 --- a/ng2-components/ng2-activiti-tasklist/src/models/filter.model.ts +++ b/ng2-components/ng2-activiti-tasklist/src/models/filter.model.ts @@ -54,7 +54,7 @@ export class AppDefinitionRepresentationModel { * @returns {FilterRepresentationModel} . */ export class FilterRepresentationModel { - id: number; + id: string; appId: string; name: string; recent: boolean; @@ -64,6 +64,7 @@ export class FilterRepresentationModel { landingTaskId: string; constructor(obj?: any) { + this.id = obj && obj.id || null; this.appId = obj && obj.appId || null; this.name = obj && obj.name || null; this.recent = obj && obj.recent || false; diff --git a/ng2-components/ng2-activiti-tasklist/src/services/activiti-tasklist.service.spec.ts b/ng2-components/ng2-activiti-tasklist/src/services/activiti-tasklist.service.spec.ts index cc154438320..e6961ae81f1 100644 --- a/ng2-components/ng2-activiti-tasklist/src/services/activiti-tasklist.service.spec.ts +++ b/ng2-components/ng2-activiti-tasklist/src/services/activiti-tasklist.service.spec.ts @@ -15,173 +15,65 @@ * limitations under the License. */ -import { TestBed, async } from '@angular/core/testing'; -import { CoreModule } from 'ng2-alfresco-core'; +import { ReflectiveInjector } from '@angular/core'; +import { async } from '@angular/core/testing'; +import { + AlfrescoAuthenticationService, + AlfrescoSettingsService, + AlfrescoApiService, + StorageService, + LogService +} from 'ng2-alfresco-core'; import { ActivitiTaskListService } from './activiti-tasklist.service'; import { TaskDetailsModel } from '../models/task-details.model'; import { FilterRepresentationModel, - AppDefinitionRepresentationModel, TaskQueryRequestRepresentationModel } from '../models/filter.model'; import { Comment } from '../models/comment.model'; +import { + fakeFilters, + fakeAppPromise, + fakeAppFilter, + fakeFilter, + fakeTaskList, + fakeErrorTaskList, + fakeTasksComment, + fakeTasksChecklist, + fakeTaskDetails, + fakeUser, + fakeApps, + fakeRepresentationFilter1, + secondFakeTaskList, + fakeRepresentationFilter2, + fakeFormList, + fakeTaskListDifferentProcessDefinitionKey, + fakeFilterWithProcessDefinitionKey +} from '../assets/tasklist-service.mock'; declare let jasmine: any; -describe('ActivitiTaskListService', () => { +describe('Activiti TaskList Service', () => { - let fakeFilters = { - size: 2, total: 2, start: 0, - data: [ - new AppDefinitionRepresentationModel( - { - id: '1', name: 'FakeInvolvedTasks', recent: false, icon: 'glyphicon-align-left', - filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-involved' } - } - ), - { - id: '2', name: 'FakeMyTasks', recent: false, icon: 'glyphicon-align-left', - filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-assignee' } - } - ] - }; - - let fakeAppFilter = { - size: 1, total: 1, start: 0, - data: [ - { - id: 1, name: 'FakeInvolvedTasks', recent: false, icon: 'glyphicon-align-left', - filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-involved' } - } - ] - }; - - let fakeApps = { - size: 2, total: 2, start: 0, - data: [ - { - id: 1, defaultAppId: null, name: 'Sales-Fakes-App', description: 'desc-fake1', modelId: 22, - theme: 'theme-1-fake', icon: 'glyphicon-asterisk', 'deploymentId': '111', 'tenantId': null - }, - { - id: 2, defaultAppId: null, name: 'health-care-Fake', description: 'desc-fake2', modelId: 33, - theme: 'theme-2-fake', icon: 'glyphicon-asterisk', 'deploymentId': '444', 'tenantId': null - } - ] - }; - - let fakeFilter = { - sort: 'created-desc', text: '', state: 'open', assignment: 'fake-assignee' - }; - - let fakeUser = { id: 1, email: 'fake-email@dom.com', firstName: 'firstName', lastName: 'lastName' }; - - let fakeTaskList = { - size: 1, total: 1, start: 0, - data: [ - { - id: '1', name: 'FakeNameTask', description: null, category: null, - assignee: fakeUser, - created: '2016-07-15T11:19:17.440+0000' - } - ] - }; - - let secondFakeTaskList = { - size: 1, total: 1, start: 0, - data: [ - { - id: '200', name: 'FakeNameTask', description: null, category: null, - assignee: fakeUser, - created: '2016-07-15T11:19:17.440+0000' - } - ] - }; - - let fakeErrorTaskList = { - error: 'wrong request' - }; - - let fakeTaskDetails = { id: '999', name: 'fake-task-name', formKey: '99', assignee: fakeUser }; - - let fakeTasksComment = { - size: 2, total: 2, start: 0, - data: [ - { - id: 1, message: 'fake-message-1', created: '', createdBy: fakeUser - }, - { - id: 2, message: 'fake-message-2', created: '', createdBy: fakeUser - } - ] - }; - - let fakeTasksChecklist = { - size: 1, total: 1, start: 0, - data: [ - { - id: 1, name: 'FakeCheckTask1', description: null, category: null, - assignee: fakeUser, - created: '2016-07-15T11:19:17.440+0000' - }, - { - id: 2, name: 'FakeCheckTask2', description: null, category: null, - assignee: fakeUser, - created: '2016-07-15T11:19:17.440+0000' - } - ] - }; - - let fakeRepresentationFilter1: FilterRepresentationModel = new FilterRepresentationModel({ - appId: 1, - name: 'CONTAIN FILTER', - recent: true, - icon: 'glyphicon-align-left', - filter: { - processDefinitionId: null, - processDefinitionKey: null, - name: null, - state: 'open', - sort: 'created-desc', - assignment: 'involved', - dueAfter: null, - dueBefore: null - } - }); + let service: ActivitiTaskListService; + let injector; - let fakeRepresentationFilter2: FilterRepresentationModel = new FilterRepresentationModel({ - appId: 2, - name: 'NO TASK FILTER', - recent: false, - icon: 'glyphicon-inbox', - filter: { - processDefinitionId: null, - processDefinitionKey: null, - name: null, - state: 'open', - sort: 'created-desc', - assignment: 'assignee', - dueAfter: null, - dueBefore: null - } + beforeEach(() => { + injector = ReflectiveInjector.resolveAndCreate([ + AlfrescoSettingsService, + AlfrescoApiService, + AlfrescoAuthenticationService, + ActivitiTaskListService, + StorageService, + LogService + ]); }); - let fakeAppPromise = new Promise(function (resolve, reject) { - resolve(fakeAppFilter); + beforeEach(() => { + service = injector.get(ActivitiTaskListService); }); - let service: ActivitiTaskListService; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - CoreModule.forRoot() - ], - providers: [ - ActivitiTaskListService - ] - }); - service = TestBed.get(ActivitiTaskListService); jasmine.Ajax.install(); }); @@ -189,461 +81,558 @@ describe('ActivitiTaskListService', () => { jasmine.Ajax.uninstall(); }); - it('should return the task list filters', (done) => { - service.getTaskListFilters().subscribe( - (res) => { - expect(res).toBeDefined(); - expect(res.length).toEqual(2); - expect(res[0].name).toEqual('FakeInvolvedTasks'); - expect(res[1].name).toEqual('FakeMyTasks'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeFilters) - }); - }); + describe('Content tests', () => { - it('should call the api withthe appId', (done) => { - spyOn(service, 'callApiTaskFilters').and.returnValue((fakeAppPromise)); - - let appId = '1'; - service.getTaskListFilters(appId).subscribe( - (res) => { - expect(service.callApiTaskFilters).toHaveBeenCalledWith(appId); - done(); - } - ); - }); + it('should return the task list filters', (done) => { + service.getTaskListFilters().subscribe( + (res) => { + expect(res).toBeDefined(); + expect(res.length).toEqual(2); + expect(res[0].name).toEqual('FakeInvolvedTasks'); + expect(res[1].name).toEqual('FakeMyTasks'); + done(); + } + ); - it('should return the app filter by id', (done) => { - let appId = '1'; - service.getTaskListFilters(appId).subscribe( - (res) => { - expect(res).toBeDefined(); - expect(res.length).toEqual(1); - expect(res[0].name).toEqual('FakeInvolvedTasks'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeAppFilter) + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeFilters) + }); }); - }); - it('should return the task list filtered', (done) => { - service.getTasks(fakeFilter).subscribe( - res => { - expect(res).toBeDefined(); - expect(res.length).toEqual(1); - expect(res[0].name).toEqual('FakeNameTask'); - expect(res[0].assignee.email).toEqual('fake-email@dom.com'); - expect(res[0].assignee.firstName).toEqual('firstName'); - expect(res[0].assignee.lastName).toEqual('lastName'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeTaskList) + it('should return the task filter by id', (done) => { + service.getTaskFilterById('2').subscribe( + (res: FilterRepresentationModel) => { + expect(res).toBeDefined(); + expect(res.id).toEqual('2'); + expect(res.name).toEqual('FakeMyTasks'); + expect(res.filter.sort).toEqual('created-desc'); + expect(res.filter.state).toEqual('open'); + expect(res.filter.assignment).toEqual('fake-assignee'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeFilters) + }); }); - }); - it('should throw an exception when the response is wrong', () => { - service.getTasks(fakeFilter).subscribe( - (res) => { - }, - (err: any) => { - expect(err).toBeDefined(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 404, - contentType: 'application/json', - responseText: JSON.stringify(fakeErrorTaskList) + it('should return the task filter by name', (done) => { + service.getTaskFilterByName('FakeMyTasks').subscribe( + (res: FilterRepresentationModel) => { + expect(res).toBeDefined(); + expect(res.id).toEqual('2'); + expect(res.name).toEqual('FakeMyTasks'); + expect(res.filter.sort).toEqual('created-desc'); + expect(res.filter.state).toEqual('open'); + expect(res.filter.assignment).toEqual('fake-assignee'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeFilters) + }); }); - }); - it('should return the task details ', (done) => { - service.getTaskDetails('999').subscribe( - (res: TaskDetailsModel) => { - expect(res).toBeDefined(); - expect(res.id).toEqual('999'); - expect(res.name).toEqual('fake-task-name'); - expect(res.formKey).toEqual('99'); - expect(res.assignee).toBeDefined(); - expect(res.assignee.email).toEqual('fake-email@dom.com'); - expect(res.assignee.firstName).toEqual('firstName'); - expect(res.assignee.lastName).toEqual('lastName'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeTaskDetails) + it('should call the api withthe appId', (done) => { + spyOn(service, 'callApiTaskFilters').and.returnValue((fakeAppPromise)); + + let appId = '1'; + service.getTaskListFilters(appId).subscribe( + (res) => { + expect(service.callApiTaskFilters).toHaveBeenCalledWith(appId); + done(); + } + ); }); - }); - it('should return the tasks comments ', (done) => { - service.getTaskComments('999').subscribe( - (res: Comment[]) => { - expect(res).toBeDefined(); - expect(res.length).toEqual(2); - expect(res[0].message).toEqual('fake-message-1'); - expect(res[1].message).toEqual('fake-message-2'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeTasksComment) + it('should return the app filter by id', (done) => { + let appId = '1'; + service.getTaskListFilters(appId).subscribe( + (res) => { + expect(res).toBeDefined(); + expect(res.length).toEqual(1); + expect(res[0].name).toEqual('FakeInvolvedTasks'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeAppFilter) + }); }); - }); - it('should return the tasks checklists ', (done) => { - service.getTaskChecklist('999').subscribe( - (res: TaskDetailsModel[]) => { - expect(res).toBeDefined(); - expect(res.length).toEqual(2); - expect(res[0].name).toEqual('FakeCheckTask1'); - expect(res[0].assignee.email).toEqual('fake-email@dom.com'); - expect(res[0].assignee.firstName).toEqual('firstName'); - expect(res[0].assignee.lastName).toEqual('lastName'); - expect(res[1].name).toEqual('FakeCheckTask2'); - expect(res[1].assignee.email).toEqual('fake-email@dom.com'); - expect(res[1].assignee.firstName).toEqual('firstName'); - expect(res[0].assignee.lastName).toEqual('lastName'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeTasksChecklist) + it('should return the task list filtered', (done) => { + service.getTasks(fakeFilter).subscribe( + res => { + expect(res).toBeDefined(); + expect(res.length).toEqual(1); + expect(res[0].name).toEqual('FakeNameTask'); + expect(res[0].assignee.email).toEqual('fake-email@dom.com'); + expect(res[0].assignee.firstName).toEqual('firstName'); + expect(res[0].assignee.lastName).toEqual('lastName'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeTaskList) + }); }); - }); - it('should add a task ', (done) => { - let taskFake = new TaskDetailsModel({ - id: 123, - parentTaskId: 456, - name: 'FakeNameTask', - description: null, - category: null, - assignee: fakeUser, - created: '' + it('should return the task list filtered by processDefinitionKey', (done) => { + service.getTasks(fakeFilterWithProcessDefinitionKey).subscribe( + res => { + expect(res).toBeDefined(); + expect(res.length).toEqual(1); + expect(res[0].name).toEqual('FakeNameTask'); + expect(res[0].assignee.email).toEqual('fake-email@dom.com'); + expect(res[0].assignee.firstName).toEqual('firstName'); + expect(res[0].assignee.lastName).toEqual('lastName'); + expect(res[0].processDefinitionKey).toEqual('1'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeTaskListDifferentProcessDefinitionKey) + }); }); - service.addTask(taskFake).subscribe( - (res: TaskDetailsModel) => { - expect(res).toBeDefined(); - expect(res.id).not.toEqual(''); - expect(res.name).toEqual('FakeNameTask'); - expect(res.created).not.toEqual(''); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({ - id: '777', name: 'FakeNameTask', description: null, category: null, - assignee: fakeUser, - created: '2016-07-15T11:19:17.440+0000' - }) + it('should throw an exception when the response is wrong', () => { + service.getTasks(fakeFilter).subscribe( + (res) => { + }, + (err: any) => { + expect(err).toBeDefined(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 404, + contentType: 'application/json', + responseText: JSON.stringify(fakeErrorTaskList) + }); }); - }); - it('should add a comment task ', (done) => { - service.addTaskComment('999', 'fake-comment-message').subscribe( - (res: Comment) => { - expect(res).toBeDefined(); - expect(res.id).not.toEqual(''); - expect(res.message).toEqual('fake-comment-message'); - expect(res.created).not.toEqual(''); - expect(res.createdBy.email).toEqual('fake-email@dom.com'); - expect(res.createdBy.firstName).toEqual('firstName'); - expect(res.createdBy.lastName).toEqual('lastName'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({ - id: '111', message: 'fake-comment-message', - createdBy: fakeUser, - created: '2016-07-15T11:19:17.440+0000' - }) + it('should return the task details ', (done) => { + service.getTaskDetails('999').subscribe( + (res: TaskDetailsModel) => { + expect(res).toBeDefined(); + expect(res.id).toEqual('999'); + expect(res.name).toEqual('fake-task-name'); + expect(res.formKey).toEqual('99'); + expect(res.assignee).toBeDefined(); + expect(res.assignee.email).toEqual('fake-email@dom.com'); + expect(res.assignee.firstName).toEqual('firstName'); + expect(res.assignee.lastName).toEqual('lastName'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeTaskDetails) + }); }); - }); - it('should complete the task ', (done) => { - service.completeTask('999').subscribe( - (res: any) => { - expect(res).toBeDefined(); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({}) + it('should return the tasks comments ', (done) => { + service.getTaskComments('999').subscribe( + (res: Comment[]) => { + expect(res).toBeDefined(); + expect(res.length).toEqual(2); + expect(res[0].message).toEqual('fake-message-1'); + expect(res[1].message).toEqual('fake-message-2'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeTasksComment) + }); }); - }); - it('should return the total number of tasks', (done) => { - service.getTotalTasks(fakeFilter).subscribe( - res => { - expect(res).toBeDefined(); - expect(res.size).toEqual(1); - expect(res.total).toEqual(1); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeTaskList) + it('should return the tasks checklists ', (done) => { + service.getTaskChecklist('999').subscribe( + (res: TaskDetailsModel[]) => { + expect(res).toBeDefined(); + expect(res.length).toEqual(2); + expect(res[0].name).toEqual('FakeCheckTask1'); + expect(res[0].assignee.email).toEqual('fake-email@dom.com'); + expect(res[0].assignee.firstName).toEqual('firstName'); + expect(res[0].assignee.lastName).toEqual('lastName'); + expect(res[1].name).toEqual('FakeCheckTask2'); + expect(res[1].assignee.email).toEqual('fake-email@dom.com'); + expect(res[1].assignee.firstName).toEqual('firstName'); + expect(res[0].assignee.lastName).toEqual('lastName'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeTasksChecklist) + }); }); - }); - it('should return the default filters', (done) => { - service.createDefaultFilters('1234').subscribe( - (res: FilterRepresentationModel []) => { - expect(res).toBeDefined(); - expect(res.length).toEqual(4); - expect(res[0].name).toEqual('Involved Tasks'); - expect(res[1].name).toEqual('My Tasks'); - expect(res[2].name).toEqual('Queued Tasks'); - expect(res[3].name).toEqual('Completed Tasks'); - done(); - } - ); - - jasmine.Ajax.requests.at(0).respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({ - id: '111', name: 'Involved Tasks', filter: { assignment: 'fake-involved' } - }) + it('should add a task ', (done) => { + let taskFake = new TaskDetailsModel({ + id: 123, + parentTaskId: 456, + name: 'FakeNameTask', + description: null, + category: null, + assignee: fakeUser, + created: '' + }); + + service.addTask(taskFake).subscribe( + (res: TaskDetailsModel) => { + expect(res).toBeDefined(); + expect(res.id).not.toEqual(''); + expect(res.name).toEqual('FakeNameTask'); + expect(res.created).not.toEqual(''); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({ + id: '777', name: 'FakeNameTask', description: null, category: null, + assignee: fakeUser, + created: '2016-07-15T11:19:17.440+0000' + }) + }); }); - jasmine.Ajax.requests.at(1).respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({ - id: '222', name: 'My Tasks', filter: { assignment: 'fake-assignee' } - }) + it('should remove a checklist task ', (done) => { + service.deleteTask('999').subscribe( + () => { + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json' + }); }); - jasmine.Ajax.requests.at(2).respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({ - id: '333', name: 'Queued Tasks', filter: { assignment: 'fake-candidate' } - }) + it('should add a comment task ', (done) => { + service.addTaskComment('999', 'fake-comment-message').subscribe( + (res: Comment) => { + expect(res).toBeDefined(); + expect(res.id).not.toEqual(''); + expect(res.message).toEqual('fake-comment-message'); + expect(res.created).not.toEqual(''); + expect(res.createdBy.email).toEqual('fake-email@dom.com'); + expect(res.createdBy.firstName).toEqual('firstName'); + expect(res.createdBy.lastName).toEqual('lastName'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({ + id: '111', message: 'fake-comment-message', + createdBy: fakeUser, + created: '2016-07-15T11:19:17.440+0000' + }) + }); }); - jasmine.Ajax.requests.at(3).respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({ - id: '444', name: 'Completed Tasks', filter: { assignment: 'fake-involved' } - }) + it('should complete the task ', (done) => { + service.completeTask('999').subscribe( + (res: any) => { + expect(res).toBeDefined(); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({}) + }); }); - }); - it('should add a filter ', (done) => { - let filterFake = new FilterRepresentationModel({ - name: 'FakeNameFilter', - assignment: 'fake-assignement' + it('should return the total number of tasks', (done) => { + service.getTotalTasks(fakeFilter).subscribe( + res => { + expect(res).toBeDefined(); + expect(res.size).toEqual(1); + expect(res.total).toEqual(1); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeTaskList) + }); }); - service.addFilter(filterFake).subscribe( - (res: FilterRepresentationModel) => { - expect(res).toBeDefined(); - expect(res.id).not.toEqual(''); - expect(res.name).toEqual('FakeNameFilter'); - expect(res.filter.assignment).toEqual('fake-assignement'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({ - id: '2233', name: 'FakeNameFilter', filter: { assignment: 'fake-assignement' } - }) + it('should return the default filters', (done) => { + service.createDefaultFilters('1234').subscribe( + (res: FilterRepresentationModel []) => { + expect(res).toBeDefined(); + expect(res.length).toEqual(4); + expect(res[0].name).toEqual('Involved Tasks'); + expect(res[1].name).toEqual('My Tasks'); + expect(res[2].name).toEqual('Queued Tasks'); + expect(res[3].name).toEqual('Completed Tasks'); + done(); + } + ); + + jasmine.Ajax.requests.at(0).respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({ + id: '111', name: 'Involved Tasks', filter: { assignment: 'fake-involved' } + }) + }); + + jasmine.Ajax.requests.at(1).respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({ + id: '222', name: 'My Tasks', filter: { assignment: 'fake-assignee' } + }) + }); + + jasmine.Ajax.requests.at(2).respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({ + id: '333', name: 'Queued Tasks', filter: { assignment: 'fake-candidate' } + }) + }); + + jasmine.Ajax.requests.at(3).respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({ + id: '444', name: 'Completed Tasks', filter: { assignment: 'fake-involved' } + }) + }); }); - }); - it('should get the deployed apps ', (done) => { - service.getDeployedApplications().subscribe( - (res: any) => { - expect(res).toBeDefined(); - expect(res.length).toEqual(2); - expect(res[0].name).toEqual('Sales-Fakes-App'); - expect(res[0].description).toEqual('desc-fake1'); - expect(res[0].deploymentId).toEqual('111'); - expect(res[1].name).toEqual('health-care-Fake'); - expect(res[1].description).toEqual('desc-fake2'); - expect(res[1].deploymentId).toEqual('444'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeApps) + it('should add a filter ', (done) => { + let filterFake = new FilterRepresentationModel({ + name: 'FakeNameFilter', + assignment: 'fake-assignement' + }); + + service.addFilter(filterFake).subscribe( + (res: FilterRepresentationModel) => { + expect(res).toBeDefined(); + expect(res.id).not.toEqual(''); + expect(res.name).toEqual('FakeNameFilter'); + expect(res.filter.assignment).toEqual('fake-assignement'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({ + id: '2233', name: 'FakeNameFilter', filter: { assignment: 'fake-assignement' } + }) + }); }); - }); - it('should get the filter deployed app ', (done) => { - let name = 'health-care-Fake'; - service.getDeployedApplications(name).subscribe( - (res: any) => { - expect(res).toBeDefined(); - expect(res.name).toEqual('health-care-Fake'); - expect(res.description).toEqual('desc-fake2'); - expect(res.deploymentId).toEqual('444'); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeApps) + it('should get the deployed apps ', (done) => { + service.getDeployedApplications().subscribe( + (res: any) => { + expect(res).toBeDefined(); + expect(res.length).toEqual(2); + expect(res[0].name).toEqual('Sales-Fakes-App'); + expect(res[0].description).toEqual('desc-fake1'); + expect(res[0].deploymentId).toEqual('111'); + expect(res[1].name).toEqual('health-care-Fake'); + expect(res[1].description).toEqual('desc-fake2'); + expect(res[1].deploymentId).toEqual('444'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeApps) + }); }); - }); - it('should create a new standalone task ', (done) => { - let taskFake = new TaskDetailsModel({ - name: 'FakeNameTask', - description: 'FakeDescription', - category: '3' + it('should get the filter deployed app ', (done) => { + let name = 'health-care-Fake'; + service.getDeployedApplications(name).subscribe( + (res: any) => { + expect(res).toBeDefined(); + expect(res.name).toEqual('health-care-Fake'); + expect(res.description).toEqual('desc-fake2'); + expect(res.deploymentId).toEqual('444'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeApps) + }); }); - service.createNewTask(taskFake).subscribe( - (res: TaskDetailsModel) => { - expect(res).toBeDefined(); - expect(res.id).not.toEqual(''); - expect(res.name).toEqual('FakeNameTask'); - expect(res.description).toEqual('FakeDescription'); - expect(res.category).toEqual('3'); - expect(res.created).not.toEqual(''); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({ - id: '777', + it('should create a new standalone task ', (done) => { + let taskFake = new TaskDetailsModel({ name: 'FakeNameTask', description: 'FakeDescription', - category: '3', - assignee: fakeUser, - created: '2016-07-15T11:19:17.440+0000' - }) + category: '3' + }); + + service.createNewTask(taskFake).subscribe( + (res: TaskDetailsModel) => { + expect(res).toBeDefined(); + expect(res.id).not.toEqual(''); + expect(res.name).toEqual('FakeNameTask'); + expect(res.description).toEqual('FakeDescription'); + expect(res.category).toEqual('3'); + expect(res.created).not.toEqual(''); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({ + id: '777', + name: 'FakeNameTask', + description: 'FakeDescription', + category: '3', + assignee: fakeUser, + created: '2016-07-15T11:19:17.440+0000' + }) + }); }); - }); - it('should claim a task', (done) => { - let taskId = '111'; + it('should claim a task', (done) => { + let taskId = '111'; - service.claimTask(taskId).subscribe( - (res: any) => { - done(); - } - ); + service.claimTask(taskId).subscribe( + (res: any) => { + done(); + } + ); - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify({}) + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify({}) + }); }); - }); - it('should return the filter if it contains task id', (done) => { - let taskId = '1'; - let filterFake = new FilterRepresentationModel({ - name: 'FakeNameFilter', - assignment: 'fake-assignement', - filter: { - processDefinitionKey: '1', - assignment: 'fake', - state: 'none', - sort: 'asc' - } - }); + it('should return the filter if it contains task id', (done) => { + let taskId = '1'; + let filterFake = new FilterRepresentationModel({ + name: 'FakeNameFilter', + assignment: 'fake-assignement', + filter: { + processDefinitionKey: '1', + assignment: 'fake', + state: 'none', + sort: 'asc' + } + }); - service.isTaskRelatedToFilter(taskId, filterFake).subscribe( - (res: any) => { - expect(res).toBeDefined(); - expect(res).not.toBeNull(); - done(); - } - ); - - jasmine.Ajax.requests.mostRecent().respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeTaskList) - }); - }); + service.isTaskRelatedToFilter(taskId, filterFake).subscribe( + (res: any) => { + expect(res).toBeDefined(); + expect(res).not.toBeNull(); + done(); + } + ); - it('should return the filters if it contains task id', async(() => { - let taskId = '1'; - - let fakeFilterList: FilterRepresentationModel[] = []; - fakeFilterList.push(fakeRepresentationFilter1, fakeRepresentationFilter2); - let resultFilter = null; - service.getFilterForTaskById(taskId, fakeFilterList).subscribe( - (res: FilterRepresentationModel) => { - resultFilter = res; - }, - () => { - }, - () => { - expect(resultFilter).toBeDefined(); - expect(resultFilter).not.toBeNull(); - expect(resultFilter.name).toBe('CONTAIN FILTER'); - }); - - jasmine.Ajax.requests.at(0).respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(fakeTaskList) + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeTaskList) + }); }); - jasmine.Ajax.requests.at(1).respondWith({ - 'status': 200, - contentType: 'application/json', - responseText: JSON.stringify(secondFakeTaskList) + it('should return the filters if it contains task id', async(() => { + let taskId = '1'; + + let fakeFilterList: FilterRepresentationModel[] = []; + fakeFilterList.push(fakeRepresentationFilter1, fakeRepresentationFilter2); + let resultFilter = null; + service.getFilterForTaskById(taskId, fakeFilterList).subscribe( + (res: FilterRepresentationModel) => { + resultFilter = res; + }, + () => { + }, + () => { + expect(resultFilter).toBeDefined(); + expect(resultFilter).not.toBeNull(); + expect(resultFilter.name).toBe('CONTAIN FILTER'); + }); + + jasmine.Ajax.requests.at(0).respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeTaskList) + }); + + jasmine.Ajax.requests.at(1).respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(secondFakeTaskList) + }); + })); + + it('should get possibile form list', (done) => { + service.getFormList().subscribe( + (res: any) => { + expect(res).toBeDefined(); + expect(res.length).toBe(2); + expect(res[0].id).toBe(1); + expect(res[0].name).toBe('form with all widgets'); + expect(res[1].id).toBe(2); + expect(res[1].name).toBe('uppy'); + done(); + } + ); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeFormList) + }); }); - })); + }); }); diff --git a/ng2-components/ng2-activiti-tasklist/src/services/activiti-tasklist.service.ts b/ng2-components/ng2-activiti-tasklist/src/services/activiti-tasklist.service.ts index d2ea8f94c2e..d64b4ff9266 100644 --- a/ng2-components/ng2-activiti-tasklist/src/services/activiti-tasklist.service.ts +++ b/ng2-components/ng2-activiti-tasklist/src/services/activiti-tasklist.service.ts @@ -35,7 +35,7 @@ export class ActivitiTaskListService { } /** - * Retrive all the Deployed app + * Retrieve all the Deployed app * @returns {Observable} */ getDeployedApplications(name?: string): Observable { @@ -50,7 +50,7 @@ export class ActivitiTaskListService { } /** - * Retrive all the Tasks filters + * Retrieve all the Tasks filters * @returns {Observable} */ getTaskListFilters(appId?: string): Observable { @@ -65,6 +65,30 @@ export class ActivitiTaskListService { }).catch(err => this.handleError(err)); } + /** + * Retrieve the Tasks filter by id + * @param taskId - string - The id of the filter + * @returns {Observable} + */ + getTaskFilterById(taskId: string, appId?: string): Observable { + return Observable.fromPromise(this.callApiTaskFilters(appId)) + .map((response: any) => { + return response.data.find(filter => filter.id === taskId); + }).catch(err => this.handleError(err)); + } + + /** + * Retrieve the Tasks filter by name + * @param taskName - string - The name of the filter + * @returns {Observable} + */ + getTaskFilterByName(taskName: string, appId?: string): Observable { + return Observable.fromPromise(this.callApiTaskFilters(appId)) + .map((response: any) => { + return response.data.find(filter => filter.name === taskName); + }).catch(err => this.handleError(err)); + } + /** * Return all the filters in the list where the task id belong * @param taskId - string @@ -108,7 +132,7 @@ export class ActivitiTaskListService { } /** - * Retrive all the tasks filtered by filterModel + * Retrieve all the tasks filtered by filterModel * @param filter - TaskFilterRepresentationModel * @returns {any} */ @@ -124,7 +148,7 @@ export class ActivitiTaskListService { } /** - * Retrive all the task details + * Retrieve all the task details * @param id - taskId * @returns {} */ @@ -137,7 +161,7 @@ export class ActivitiTaskListService { } /** - * Retrive all the task's comments + * Retrieve all the task's comments * @param id - taskId * @returns {} */ @@ -155,7 +179,7 @@ export class ActivitiTaskListService { } /** - * Retrive all the task's checklist + * Retrieve all the task's checklist * @param id - taskId * @returns {TaskDetailsModel} */ @@ -172,7 +196,7 @@ export class ActivitiTaskListService { } /** - * Retrive all the form shared with this user + * Retrieve all the form shared with this user * @returns {TaskDetailsModel} */ getFormList(): Observable
{ @@ -256,6 +280,15 @@ export class ActivitiTaskListService { }).catch(err => this.handleError(err)); } + /** + * Delete a task + * @param taskId - string + */ + deleteTask(taskId: string): Observable { + return Observable.fromPromise(this.callApiDeleteTask(taskId)) + .catch(err => this.handleError(err)); + } + /** * Add a filter * @param filter - FilterRepresentationModel @@ -357,6 +390,10 @@ export class ActivitiTaskListService { return this.apiService.getInstance().activiti.taskApi.addSubtask(task.parentTaskId, task); } + private callApiDeleteTask(taskId: string) { + return this.apiService.getInstance().activiti.taskApi.deleteTask(taskId); + } + private callApiAddFilter(filter: FilterRepresentationModel) { return this.apiService.getInstance().activiti.userFiltersApi.createUserTaskFilter(filter); } diff --git a/ng2-components/ng2-alfresco-core/index.ts b/ng2-components/ng2-alfresco-core/index.ts index e4e36ec27cd..c0d423dedd2 100644 --- a/ng2-components/ng2-alfresco-core/index.ts +++ b/ng2-components/ng2-alfresco-core/index.ts @@ -45,11 +45,13 @@ import { DataColumnComponent } from './src/components/data-column/data-column.co import { DataColumnListComponent } from './src/components/data-column/data-column-list.component'; import { MATERIAL_DESIGN_DIRECTIVES } from './src/components/material/index'; import { CONTEXT_MENU_PROVIDERS, CONTEXT_MENU_DIRECTIVES } from './src/components/context-menu/index'; +import { COLLAPSABLE_DIRECTIVES } from './src/components/collapsable/index'; export * from './src/services/index'; export * from './src/components/index'; export * from './src/components/data-column/data-column.component'; export * from './src/components/data-column/data-column-list.component'; +export * from './src/components/collapsable/index'; export * from './src/directives/upload.directive'; export * from './src/utils/index'; export * from './src/events/base.event'; @@ -94,6 +96,7 @@ export function createTranslateLoader(http: Http, logService: LogService) { declarations: [ ...MATERIAL_DESIGN_DIRECTIVES, ...CONTEXT_MENU_DIRECTIVES, + ...COLLAPSABLE_DIRECTIVES, UploadDirective, DataColumnComponent, DataColumnListComponent @@ -110,6 +113,7 @@ export function createTranslateLoader(http: Http, logService: LogService) { TranslateModule, ...MATERIAL_DESIGN_DIRECTIVES, ...CONTEXT_MENU_DIRECTIVES, + ...COLLAPSABLE_DIRECTIVES, UploadDirective, DataColumnComponent, DataColumnListComponent diff --git a/ng2-components/ng2-alfresco-core/package.json b/ng2-components/ng2-alfresco-core/package.json index 70afef23ae0..0935281b730 100644 --- a/ng2-components/ng2-alfresco-core/package.json +++ b/ng2-components/ng2-alfresco-core/package.json @@ -1,7 +1,7 @@ { "name": "ng2-alfresco-core", "description": "Alfresco Angular 2 Components core", - "version": "1.3.0", + "version": "1.4.0", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", @@ -65,7 +65,7 @@ "@angular/platform-browser-dynamic": "2.2.2", "@angular/router": "3.2.2", "@angular/upgrade": "2.2.2", - "alfresco-js-api": "~1.3.0", + "alfresco-js-api": "~1.4.0", "core-js": "2.4.1", "dialog-polyfill": "0.4.7", "element.scrollintoviewifneeded-polyfill": "1.0.1", diff --git a/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.css b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.css new file mode 100644 index 00000000000..778c78d0e1b --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.css @@ -0,0 +1,35 @@ +.adf-panel-heading { + float: left; + font-size: 14px; + font-weight: bold; + font-style: normal; + font-stretch: normal; + line-height: normal; + letter-spacing: normal; + text-align: left; + color: #000000; + width: 100%; +} + +.adf-panel-heading-selected { + color: #448aff; +} + +.adf-panel-heading-icon { + float: left; +} + +.adf-panel-heading-text { + float: left; + padding-left: 20px; + padding-top: 4px; +} + +.adf-panel-heading-toggle { + float: right; + cursor: pointer; +} + +.adf-panel-heading-toggle:hover { + opacity: 0.4; +} diff --git a/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.html b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.html new file mode 100644 index 00000000000..87a52e8b520 --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.html @@ -0,0 +1,16 @@ +
+
+
+ {{headingIcon}} +
+
{{heading}}
+
+ {{getAccordionIcon()}} +
+
+
+
+ +
+
+
diff --git a/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.spec.ts b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.spec.ts new file mode 100644 index 00000000000..125d87f7372 --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.spec.ts @@ -0,0 +1,76 @@ +/*! + * @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 { AccordionComponent } from './accordion.component'; +import { AccordionGroupComponent } from './accordion-group.component'; + +describe('AccordionGroupComponent', () => { + + let fixture: ComponentFixture; + let component: AccordionGroupComponent; + let element: any; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AccordionGroupComponent + ], + providers: [AccordionComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AccordionGroupComponent); + + element = fixture.nativeElement; + component = fixture.componentInstance; + + }); + + it('should be closed by default', () => { + component.heading = 'Fake Header'; + component.headingIcon = 'fake-icon'; + fixture.whenStable().then(() => { + fixture.detectChanges(); + let headerToggle = fixture.nativeElement.querySelector('.adf-panel-heading-toggle .material-icons'); + expect(headerToggle.innerText).toEqual('expand_more'); + let headerText = fixture.nativeElement.querySelector('.adf-panel-heading-text'); + expect(headerText.innerText).toEqual('Fake Header'); + let headerIcon = fixture.nativeElement.querySelector('.adf-panel-heading-icon .material-icons'); + expect(headerIcon.innerText).toEqual('fake-icon'); + }); + }); + + it('should be open when click', () => { + component.isSelected = true; + component.heading = 'Fake Header'; + component.headingIcon = 'fake-icon'; + fixture.detectChanges(); + element.querySelector('#accordion-button').click(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + let headerText = fixture.nativeElement.querySelector('.adf-panel-heading-text'); + expect(headerText.innerText).toEqual('Fake Header'); + let headerIcon = fixture.nativeElement.querySelector('.adf-panel-heading-icon .material-icons'); + expect(headerIcon.innerText).toEqual('fake-icon'); + let headerToggle = fixture.nativeElement.querySelector('.adf-panel-heading-toggle .material-icons'); + expect(headerToggle.innerText).toEqual('expand_less'); + }); + }); + +}); diff --git a/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.ts b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.ts new file mode 100644 index 00000000000..ad0fb22e465 --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion-group.component.ts @@ -0,0 +1,79 @@ +/*! + * @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, Input, OnDestroy } from '@angular/core'; +import { AccordionComponent } from './accordion.component'; + +@Component({ + selector: 'adf-accordion-group', + moduleId: module.id, + templateUrl: 'accordion-group.component.html', + styleUrls: ['./accordion-group.component.css'] + +}) +export class AccordionGroupComponent implements OnDestroy { + private _isOpen: boolean = false; + private _isSelected: boolean = false; + + @Input() + heading: string; + + @Input() + headingIcon: string; + + @Input() + set isOpen(value: boolean) { + this._isOpen = value; + if (value) { + this.accordion.closeOthers(this); + } + } + + get isOpen() { + return this._isOpen; + } + + @Input() + set isSelected(value: boolean) { + this._isSelected = value; + } + + get isSelected() { + return this._isSelected; + } + + constructor(private accordion: AccordionComponent) { + this.accordion.addGroup(this); + } + + ngOnDestroy() { + this.accordion.removeGroup(this); + } + + hasHeadingIcon() { + return this.headingIcon ? true : false; + } + + toggleOpen(event: MouseEvent): void { + event.preventDefault(); + this.isOpen = !this.isOpen; + } + + getAccordionIcon(): string { + return this.isOpen ? 'expand_less' : 'expand_more'; + } +} diff --git a/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion.component.spec.ts b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion.component.spec.ts new file mode 100644 index 00000000000..cf7b4625fea --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion.component.spec.ts @@ -0,0 +1,81 @@ +/*! + * @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 { AccordionComponent } from './accordion.component'; +import { AccordionGroupComponent } from './accordion-group.component'; + +describe('AccordionComponent', () => { + + let fixture: ComponentFixture; + let component: AccordionComponent; + let componentGroup1: AccordionGroupComponent; + let componentGroup2: AccordionGroupComponent; + let componentGroup3: AccordionGroupComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AccordionComponent + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AccordionComponent); + component = fixture.componentInstance; + }); + + afterEach(() => { + component.groups = []; + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('should add the AccordionGroup', () => { + component.addGroup(componentGroup1); + expect(component.groups.length).toBe(1); + }); + + it('should close all the other group', () => { + componentGroup1 = new AccordionGroupComponent(component); + componentGroup2 = new AccordionGroupComponent(component); + componentGroup3 = new AccordionGroupComponent(component); + componentGroup1.isOpen = false; + componentGroup2.isOpen = true; + componentGroup3.isOpen = false; + + expect(component.groups[0].isOpen).toBeFalsy(); + expect(component.groups[1].isOpen).toBeTruthy(); + expect(component.groups[2].isOpen).toBeFalsy(); + + componentGroup1.isOpen = true; + + expect(component.groups[0].isOpen).toBeTruthy(); + expect(component.groups[1].isOpen).toBeFalsy(); + expect(component.groups[2].isOpen).toBeFalsy(); + }); + + it('should remove the AccordionGroup', () => { + component.addGroup(componentGroup1); + component.removeGroup(componentGroup1); + expect(component.groups.length).toBe(0); + }); + +}); diff --git a/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion.component.ts b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion.component.ts new file mode 100644 index 00000000000..2356f90994d --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/components/collapsable/accordion.component.ts @@ -0,0 +1,51 @@ +/*! + * @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 } from '@angular/core'; +import { AccordionGroupComponent } from './accordion-group.component'; + +@Component({ + selector: 'adf-accordion', + template: ` + + `, + host: { + 'class': 'panel-group' + } +}) +export class AccordionComponent { + groups: Array = []; + + addGroup(group: AccordionGroupComponent): void { + this.groups.push(group); + } + + closeOthers(openGroup: AccordionGroupComponent): void { + this.groups.forEach((group: AccordionGroupComponent) => { + if (group !== openGroup) { + group.isOpen = false; + } + }); + } + + removeGroup(group: AccordionGroupComponent): void { + const index = this.groups.indexOf(group); + if (index !== -1) { + this.groups.splice(index, 1); + } + } +} diff --git a/ng2-components/ng2-alfresco-core/src/components/collapsable/index.ts b/ng2-components/ng2-alfresco-core/src/components/collapsable/index.ts new file mode 100644 index 00000000000..90ded41f70b --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/components/collapsable/index.ts @@ -0,0 +1,24 @@ +/*! + * @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 {AccordionComponent} from './accordion.component'; +import {AccordionGroupComponent} from './accordion-group.component'; + +export const COLLAPSABLE_DIRECTIVES: [any] = [ + AccordionComponent, + AccordionGroupComponent +]; diff --git a/ng2-components/ng2-alfresco-core/src/components/index.ts b/ng2-components/ng2-alfresco-core/src/components/index.ts index ecc9adaed99..87a331deeb9 100644 --- a/ng2-components/ng2-alfresco-core/src/components/index.ts +++ b/ng2-components/ng2-alfresco-core/src/components/index.ts @@ -17,3 +17,4 @@ export * from './context-menu/index'; export * from './material/index'; +export * from './collapsable/index'; diff --git a/ng2-components/ng2-alfresco-datatable/README.md b/ng2-components/ng2-alfresco-datatable/README.md index 1233c10340e..e220785767a 100644 --- a/ng2-components/ng2-alfresco-datatable/README.md +++ b/ng2-components/ng2-alfresco-datatable/README.md @@ -190,8 +190,20 @@ export class DataTableDemo { this.data = new ObjectDataTableAdapter( // data [ - {id: 1, name: 'Name 1', createdBy : { name: 'user'}, createdOn: 123, icon: 'http://example.com/img.png'}, - {id: 2, name: 'Name 2', createdBy : { name: 'user 2'}, createdOn: 123, icon: 'http://example.com/img.png'} + { + id: 1, + name: 'Name 1', + createdBy : { name: 'user'}, + createdOn: 123, + icon: 'http://example.com/img.png' + }, + { + id: 2, + name: 'Name 2', + createdBy : { name: 'user 2'}, + createdOn: 123, + icon: 'http://example.com/img.png' + } ] ); } @@ -295,6 +307,52 @@ onRowClick(event) { ``` +#### Column Templates + +It is possible assigning a custom column template like the following: + +```html + + + + + + + +``` + +Example above shows access to the underlying cell value by binding `value` property to the underlying context `value`: + +```html +