From 033ac9ebae292f057c0098475f236fe4c3c556d5 Mon Sep 17 00:00:00 2001 From: tomgny <49343696+tomgny@users.noreply.github.com> Date: Wed, 13 Oct 2021 12:34:14 +0200 Subject: [PATCH] [AAE-5952] Storybook stories for task-header-cloud component (#7287) * Enable the storybook for process-services-clod * [AAE-5952] added few stories * [AAE-5952] added additional extensions for process cloud config * [AAE-5952] added few stories * [AAE-5952] added additional extensions for process cloud config * [AAE-5952] added stories for claim by user, group and invalid app and task story separation * [AAE-5952] mmock assigned user * [AAE-5952] added task cloud interface * [AAE-5952] improved formatting * Use relative path * [AAE-5952] fixed lint errors Co-authored-by: Maurizio Vitale --- lib/process-services-cloud/.storybook/main.js | 11 +- .../src/lib/task/models/task.model.ts | 5 +- .../lib/task/services/task-cloud.interface.ts | 45 +++++ .../task/services/task-cloud.service.mock.ts | 163 ++++++++++++++++++ .../lib/task/services/task-cloud.service.ts | 16 +- .../task-header-cloud.component.stories.ts | 111 ++++++++++++ .../mocks/task-details-cloud.mock.ts | 61 +++++++ .../task-header/task-header-cloud.module.ts | 4 +- 8 files changed, 400 insertions(+), 16 deletions(-) create mode 100644 lib/process-services-cloud/src/lib/task/services/task-cloud.interface.ts create mode 100644 lib/process-services-cloud/src/lib/task/services/task-cloud.service.mock.ts create mode 100644 lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.stories.ts diff --git a/lib/process-services-cloud/.storybook/main.js b/lib/process-services-cloud/.storybook/main.js index 413a1e80e9..bffbbbd740 100644 --- a/lib/process-services-cloud/.storybook/main.js +++ b/lib/process-services-cloud/.storybook/main.js @@ -3,13 +3,12 @@ const rootMain = require('../../../.storybook/main'); module.exports = { ...rootMain, - + core: { ...rootMain.core, builder: 'webpack4' }, - + stories: [ ...rootMain.stories, - '../src/lib/**/*.stories.mdx', - '../src/lib/**/task-header-cloud.component.stories.@(js|jsx|ts|tsx)' + '../src/lib/**/*.stories.@(js|jsx|ts|tsx)' ], addons: [...rootMain.addons ], webpackFinal: async (config, { configType }) => { @@ -17,8 +16,8 @@ module.exports = { if (rootMain.webpackFinal) { config = await rootMain.webpackFinal(config, { configType }); } - - + + // add your own webpack tweaks if needed diff --git a/lib/process-services-cloud/src/lib/task/models/task.model.ts b/lib/process-services-cloud/src/lib/task/models/task.model.ts index 0ff9625bb7..772d6c7478 100644 --- a/lib/process-services-cloud/src/lib/task/models/task.model.ts +++ b/lib/process-services-cloud/src/lib/task/models/task.model.ts @@ -19,7 +19,6 @@ export enum ClaimTaskEnum { claim = 'claim', unclaim = 'unclaim' } - export interface TaskPriorityOption { label: string; key: string; @@ -32,3 +31,7 @@ export const DEFAULT_TASK_PRIORITIES: TaskPriorityOption[] = [ { label: 'ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NORMAL', value: '2', key: '2' }, { label: 'ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.HIGH', value: '3', key: '3' } ]; + +export const TASK_ASSIGNED_STATE = 'ASSIGNED'; + +export const TASK_CREATED_STATE = 'CREATED'; diff --git a/lib/process-services-cloud/src/lib/task/services/task-cloud.interface.ts b/lib/process-services-cloud/src/lib/task/services/task-cloud.interface.ts new file mode 100644 index 0000000000..5729eabc1c --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/services/task-cloud.interface.ts @@ -0,0 +1,45 @@ +/*! + * @license + * Copyright 2019 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 { CardViewArrayItem } from '@alfresco/adf-core'; +import { Observable, Subject } from 'rxjs'; +import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model'; +import { TaskPriorityOption } from '../models/task.model'; +import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model'; +import { TaskDetailsCloudModel } from '../start-task/models/task-details-cloud.model'; +export interface TaskCloudInterface { + + dataChangesDetected$: Subject; + priorities: TaskPriorityOption[]; + + completeTask(appName: string, taskId: string): Observable; + canCompleteTask(taskDetails: TaskDetailsCloudModel): boolean; + isTaskEditable(taskDetails: TaskDetailsCloudModel): boolean; + isAssigneePropertyClickable(taskDetails: TaskDetailsCloudModel, candidateUsers: CardViewArrayItem[], candidateGroups: CardViewArrayItem[]): boolean; + canClaimTask(taskDetails: TaskDetailsCloudModel): boolean; + canUnclaimTask(taskDetails: TaskDetailsCloudModel): boolean; + claimTask(appName: string, taskId: string, assignee: string): Observable; + unclaimTask(appName: string, taskId: string): Observable; + getTaskById(appName: string, taskId: string): Observable; + createNewTask(startTaskRequest: StartTaskCloudRequestModel, appName: string): Observable; + updateTask(appName: string, taskId: string, payload: any): Observable; + getCandidateUsers(appName: string, taskId: string): Observable; + getCandidateGroups(appName: string, taskId: string): Observable; + getProcessDefinitions(appName: string): Observable; + assign(appName: string, taskId: string, assignee: string): Observable; + getPriorityLabel(priority: number): string; +} diff --git a/lib/process-services-cloud/src/lib/task/services/task-cloud.service.mock.ts b/lib/process-services-cloud/src/lib/task/services/task-cloud.service.mock.ts new file mode 100644 index 0000000000..db5ab8c747 --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/services/task-cloud.service.mock.ts @@ -0,0 +1,163 @@ +/*! + * @license + * Copyright 2019 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 { Injectable } from '@angular/core'; +import { AppConfigService, CardViewArrayItem, LogService } from '@alfresco/adf-core'; +import { from, Observable, of, Subject, throwError } from 'rxjs'; +import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption, TASK_ASSIGNED_STATE, TASK_CREATED_STATE } from '../models/task.model'; +import { TaskDetailsCloudModel } from '../start-task/public-api'; +import { taskDetailsContainer } from '../task-header/mocks/task-details-cloud.mock'; +import { TaskCloudInterface } from './task-cloud.interface'; +import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model'; +import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model'; + +@Injectable({ + providedIn: 'root' +}) +export class TaskCloudServiceMock implements TaskCloudInterface { + + currentUserMock = 'AssignedTaskUser'; + dataChangesDetected$ = new Subject(); + + constructor(private appConfigService: AppConfigService, private logService: LogService) { } + + getTaskById(_appName: string, taskId: string): Observable { + return of(taskDetailsContainer[taskId]); + } + + getCandidateUsers(_appName: string, taskId: string): Observable { + if (taskId === 'mock-no-candidate-users') { + return of([]); + } + return of(['user1', 'user2']); + } + + getCandidateGroups(_appName: string, taskId: string): Observable { + if (taskId === 'mock-no-candidate-groups') { + return of([]); + } + return of(['group1', 'group2']); + } + + getPriorityLabel(priority: number): string { + const priorityItem = this.priorities.find((item) => item.value === priority.toString()) || this.priorities[0]; + return priorityItem.label; + } + + get priorities(): TaskPriorityOption[] { + return this.appConfigService.get('adf-cloud-priority-values') || DEFAULT_TASK_PRIORITIES; + } + + isTaskEditable(taskDetails: TaskDetailsCloudModel) { + return taskDetails.status === TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee); + } + + isAssigneePropertyClickable(taskDetails: TaskDetailsCloudModel, candidateUsers: CardViewArrayItem[], candidateGroups: CardViewArrayItem[]): boolean { + let isClickable = false; + const states = [TASK_ASSIGNED_STATE]; + if (candidateUsers?.length || candidateGroups?.length) { + isClickable = states.includes(taskDetails.status); + } + return isClickable; + } + + updateTask(_appName: string, taskId: string, _payload: any): Observable { + return of(taskDetailsContainer[taskId]); + } + + canCompleteTask(taskDetails: TaskDetailsCloudModel): boolean { + return taskDetails && taskDetails.status === TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee); + } + + canClaimTask(taskDetails: TaskDetailsCloudModel): boolean { + return taskDetails && taskDetails.status === TASK_CREATED_STATE; + } + + private isAssignedToMe(assignee: string): boolean { + if (assignee === this.currentUserMock) { + return true; + } + + return false; + } + + completeTask(appName: string, taskId: string): Observable { + if ((appName || appName === '') && taskId) { + window.alert('Complete task mock'); + + return from([]); + } else { + this.logService.error('AppName and TaskId are mandatory for complete a task'); + return throwError('AppName/TaskId not configured'); + } + } + + canUnclaimTask(taskDetails: TaskDetailsCloudModel): boolean { + const currentUser = this.currentUserMock; + return taskDetails && taskDetails.status === TASK_ASSIGNED_STATE && taskDetails.assignee === currentUser; + } + + claimTask(appName: string, taskId: string, _assignee: string): Observable { + if ((appName || appName === '') && taskId) { + window.alert('Claim task mock'); + + return from([]); + } else { + this.logService.error('AppName and TaskId are mandatory for querying a task'); + return throwError('AppName/TaskId not configured'); + } + } + + unclaimTask(appName: string, taskId: string): Observable { + if ((appName || appName === '') && taskId) { + window.alert('Unclaim task mock'); + + return from([]); + } else { + this.logService.error('AppName and TaskId are mandatory for querying a task'); + return throwError('AppName/TaskId not configured'); + } + } + + createNewTask(_startTaskRequest: StartTaskCloudRequestModel, _appName: string): Observable { + window.alert('Create new task mock'); + + return from([]); + } + + getProcessDefinitions(appName: string): Observable { + if (appName || appName === '') { + window.alert('Get process definitions mock'); + + return from([]); + } else { + this.logService.error('AppName is mandatory for querying task'); + return throwError('AppName not configured'); + } + } + + assign(appName: string, taskId: string, _assignee: string): Observable { + if (appName && taskId) { + window.alert('Assign mock'); + + return from([]); + } else { + this.logService.error('AppName and TaskId are mandatory to change/update the task assignee'); + return throwError('AppName/TaskId not configured'); + } + } +} diff --git a/lib/process-services-cloud/src/lib/task/services/task-cloud.service.ts b/lib/process-services-cloud/src/lib/task/services/task-cloud.service.ts index c2120f73be..8825fb8bb0 100644 --- a/lib/process-services-cloud/src/lib/task/services/task-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/task/services/task-cloud.service.ts @@ -23,14 +23,14 @@ import { TaskDetailsCloudModel, StartTaskCloudResponseModel } from '../start-tas import { BaseCloudService } from '../../services/base-cloud.service'; import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model'; import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model'; -import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption } from '../models/task.model'; +import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption, TASK_ASSIGNED_STATE, TASK_CREATED_STATE } from '../models/task.model'; +import { TaskCloudInterface } from './task-cloud.interface'; @Injectable({ providedIn: 'root' }) -export class TaskCloudService extends BaseCloudService { +export class TaskCloudService extends BaseCloudService implements TaskCloudInterface { - static TASK_ASSIGNED_STATE = 'ASSIGNED'; dataChangesDetected$ = new Subject(); constructor( @@ -67,7 +67,7 @@ export class TaskCloudService extends BaseCloudService { * @returns Boolean value if the task can be completed */ canCompleteTask(taskDetails: TaskDetailsCloudModel): boolean { - return taskDetails && taskDetails.status === TaskCloudService.TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee); + return taskDetails && taskDetails.status === TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee); } /** @@ -76,12 +76,12 @@ export class TaskCloudService extends BaseCloudService { * @returns Boolean value if the task is editable */ isTaskEditable(taskDetails: TaskDetailsCloudModel): boolean { - return taskDetails && taskDetails.status === TaskCloudService.TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee); + return taskDetails && taskDetails.status === TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee); } isAssigneePropertyClickable(taskDetails: TaskDetailsCloudModel, candidateUsers: CardViewArrayItem[], candidateGroups: CardViewArrayItem[]): boolean { let isClickable = false; - const states = [TaskCloudService.TASK_ASSIGNED_STATE]; + const states = [TASK_ASSIGNED_STATE]; if (candidateUsers?.length || candidateGroups?.length) { isClickable = states.includes(taskDetails.status); } @@ -94,7 +94,7 @@ export class TaskCloudService extends BaseCloudService { * @returns Boolean value if the task can be completed */ canClaimTask(taskDetails: TaskDetailsCloudModel): boolean { - return taskDetails && taskDetails.status === 'CREATED'; + return taskDetails && taskDetails.status === TASK_CREATED_STATE; } /** @@ -104,7 +104,7 @@ export class TaskCloudService extends BaseCloudService { */ canUnclaimTask(taskDetails: TaskDetailsCloudModel): boolean { const currentUser = this.identityUserService.getCurrentUserInfo().username; - return taskDetails && taskDetails.status === TaskCloudService.TASK_ASSIGNED_STATE && taskDetails.assignee === currentUser; + return taskDetails && taskDetails.status === TASK_ASSIGNED_STATE && taskDetails.assignee === currentUser; } /** diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.stories.ts b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.stories.ts new file mode 100644 index 0000000000..4bd3f42248 --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.stories.ts @@ -0,0 +1,111 @@ +/*! + * @license + * Copyright 2019 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 { Meta, moduleMetadata, Story } from '@storybook/angular'; +import { TRANSLATION_PROVIDER } from '@alfresco/adf-core'; +import { TaskHeaderCloudModule } from '../task-header-cloud.module'; +import { TaskHeaderCloudComponent } from './task-header-cloud.component'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { TaskCloudService } from '../../services/task-cloud.service'; +import { TaskCloudServiceMock } from '../../services/task-cloud.service.mock'; + +export default { + component: TaskHeaderCloudComponent, + title: 'Process Services Cloud/Components/Task Header', + decorators: [ + moduleMetadata({ + declarations: [], + imports: [TaskHeaderCloudModule, BrowserAnimationsModule], + providers: [ + { provide: TaskCloudService, useClass: TaskCloudServiceMock }, + { + provide: TRANSLATION_PROVIDER, + multi: true, + useValue: { + name: 'adf-process-services-cloud', + source: 'assets/adf-process-services-cloud' + } + } + ] + }) + ], + argTypes: { + appName: { table: { disable: true } }, + taskId: { table: { disable: true } } + } +} as Meta; + +const template: Story = (args) => ({ + props: { + ...args + } +}); + +export const assignedAndEditable = template.bind({}); +assignedAndEditable.args = { + appName: 'app', + showTitle: true, + taskId: 'mock-assigned-task' +}; + +export const completedAndReadonly = template.bind({}); +completedAndReadonly.args = { + ...assignedAndEditable.args, + taskId: 'mock-completed-task' +}; + +export const suspended = template.bind({}); +suspended.args = { + ...assignedAndEditable.args, + taskId: 'mock-suspended-task' +}; + +export const withParentId = template.bind({}); +withParentId.args = { + ...assignedAndEditable.args, + taskId: 'mock-parent-task-id' +}; + +export const withoutAssignee = template.bind({}); +withoutAssignee.args = { + ...assignedAndEditable.args, + taskId: 'mock-created-task' +}; + +export const notClaimableByUser = template.bind({}); +notClaimableByUser.args = { + ...assignedAndEditable.args, + taskId: 'mock-no-candidate-users' +}; + +export const taskNotClaimableByGroupUser = template.bind({}); +taskNotClaimableByGroupUser.args = { + ...assignedAndEditable.args, + taskId: 'mock-no-candidate-groups' +}; + +export const invalidForMissingApp = template.bind({}); +invalidForMissingApp.args = { + ...assignedAndEditable.args, + appName: undefined +}; + +export const invalidForMissingTaskId = template.bind({}); +invalidForMissingTaskId.args = { + ...assignedAndEditable.args, + taskId: undefined +}; diff --git a/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts b/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts index e0d5f16553..ac1ad01cf5 100644 --- a/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts +++ b/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts @@ -146,6 +146,7 @@ export const completedTaskDetailsCloudMock: TaskDetailsCloudModel = { 'description': 'This is the description ', 'createdDate': new Date(1545048055900), 'dueDate': new Date(1545091200000), + 'completedDate': new Date(1546091200000), 'claimedDate': null, 'priority': 5, 'category': null, @@ -208,3 +209,63 @@ export const suspendedTaskDetailsCloudMock: TaskDetailsCloudModel = { 'lastModifiedFrom': null, 'standalone': true }; + +export const noCandidateUsersTaskDetailsCloudMock: TaskDetailsCloudModel = { + 'appName': 'mock-app-name', + 'appVersion': 1, + 'id': 'mock-task-id', + 'assignee': '', + 'name': 'This is a new task', + 'description': 'This is the description ', + 'createdDate': new Date(1545048055900), + 'dueDate': new Date(1545091200000), + 'claimedDate': null, + 'priority': 5, + 'category': null, + 'processDefinitionId': null, + 'processInstanceId': null, + 'status': 'CREATED', + 'owner': 'ownerUser', + 'candidateUsers': null, + 'parentTaskId': null, + 'formKey': null, + 'lastModified': new Date(1545048055900), + 'lastModifiedTo': null, + 'lastModifiedFrom': null, + 'standalone': false +}; + +export const noCandidateGroupsTaskDetailsCloudMock: TaskDetailsCloudModel = { + 'appName': 'mock-app-name', + 'appVersion': 1, + 'id': 'mock-task-id', + 'assignee': '', + 'name': 'This is a new task', + 'description': 'This is the description ', + 'createdDate': new Date(1545048055900), + 'dueDate': new Date(1545091200000), + 'claimedDate': null, + 'priority': 5, + 'category': null, + 'processDefinitionId': null, + 'processInstanceId': null, + 'status': 'CREATED', + 'owner': 'ownerUser', + 'candidateGroups': null, + 'parentTaskId': null, + 'formKey': null, + 'lastModified': new Date(1545048055900), + 'lastModifiedTo': null, + 'lastModifiedFrom': null, + 'standalone': false +}; + +export const taskDetailsContainer = { + 'mock-assigned-task': assignedTaskDetailsCloudMock, + 'mock-completed-task': completedTaskDetailsCloudMock, + 'mock-created-task': createdStateTaskDetailsCloudMock, + 'mock-parent-task-id': taskDetailsWithParentTaskIdMock, + 'mock-suspended-task': suspendedTaskDetailsCloudMock, + 'mock-no-candidate-users': noCandidateUsersTaskDetailsCloudMock, + 'mock-no-candidate-groups': noCandidateGroupsTaskDetailsCloudMock +}; diff --git a/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts b/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts index bcf9d43528..4bfd7b35b0 100644 --- a/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts @@ -20,12 +20,14 @@ import { CommonModule } from '@angular/common'; import { MaterialModule } from '../../material.module'; import { CoreModule } from '@alfresco/adf-core'; import { TaskHeaderCloudComponent } from './components/task-header-cloud.component'; +import { TranslateModule } from '@ngx-translate/core'; @NgModule({ imports: [ + TranslateModule.forRoot(), CommonModule, MaterialModule, - CoreModule + CoreModule.forRoot() ], declarations: [ TaskHeaderCloudComponent