[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 <maurizio.vitale@alfresco.com>
This commit is contained in:
tomgny
2021-10-13 12:34:14 +02:00
committed by GitHub
parent 38449a80f3
commit 033ac9ebae
8 changed files with 400 additions and 16 deletions

View File

@@ -3,13 +3,12 @@ const rootMain = require('../../../.storybook/main');
module.exports = { module.exports = {
...rootMain, ...rootMain,
core: { ...rootMain.core, builder: 'webpack4' }, core: { ...rootMain.core, builder: 'webpack4' },
stories: [ stories: [
...rootMain.stories, ...rootMain.stories,
'../src/lib/**/*.stories.mdx', '../src/lib/**/*.stories.@(js|jsx|ts|tsx)'
'../src/lib/**/task-header-cloud.component.stories.@(js|jsx|ts|tsx)'
], ],
addons: [...rootMain.addons ], addons: [...rootMain.addons ],
webpackFinal: async (config, { configType }) => { webpackFinal: async (config, { configType }) => {
@@ -17,8 +16,8 @@ module.exports = {
if (rootMain.webpackFinal) { if (rootMain.webpackFinal) {
config = await rootMain.webpackFinal(config, { configType }); config = await rootMain.webpackFinal(config, { configType });
} }
// add your own webpack tweaks if needed // add your own webpack tweaks if needed

View File

@@ -19,7 +19,6 @@ export enum ClaimTaskEnum {
claim = 'claim', claim = 'claim',
unclaim = 'unclaim' unclaim = 'unclaim'
} }
export interface TaskPriorityOption { export interface TaskPriorityOption {
label: string; label: string;
key: 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.NORMAL', value: '2', key: '2' },
{ label: 'ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.HIGH', value: '3', key: '3' } { 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';

View File

@@ -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<unknown>;
priorities: TaskPriorityOption[];
completeTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel>;
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<TaskDetailsCloudModel>;
unclaimTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel>;
getTaskById(appName: string, taskId: string): Observable<TaskDetailsCloudModel>;
createNewTask(startTaskRequest: StartTaskCloudRequestModel, appName: string): Observable<TaskDetailsCloudModel>;
updateTask(appName: string, taskId: string, payload: any): Observable<TaskDetailsCloudModel>;
getCandidateUsers(appName: string, taskId: string): Observable<string[]>;
getCandidateGroups(appName: string, taskId: string): Observable<string[]>;
getProcessDefinitions(appName: string): Observable<ProcessDefinitionCloud[]>;
assign(appName: string, taskId: string, assignee: string): Observable<TaskDetailsCloudModel>;
getPriorityLabel(priority: number): string;
}

View File

@@ -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<TaskDetailsCloudModel> {
return of(taskDetailsContainer[taskId]);
}
getCandidateUsers(_appName: string, taskId: string): Observable<string[]> {
if (taskId === 'mock-no-candidate-users') {
return of([]);
}
return of(['user1', 'user2']);
}
getCandidateGroups(_appName: string, taskId: string): Observable<string[]> {
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<TaskDetailsCloudModel> {
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<TaskDetailsCloudModel> {
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<TaskDetailsCloudModel> {
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<TaskDetailsCloudModel> {
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<TaskDetailsCloudModel> {
window.alert('Create new task mock');
return from([]);
}
getProcessDefinitions(appName: string): Observable<ProcessDefinitionCloud[]> {
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<TaskDetailsCloudModel> {
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');
}
}
}

View File

@@ -23,14 +23,14 @@ import { TaskDetailsCloudModel, StartTaskCloudResponseModel } from '../start-tas
import { BaseCloudService } from '../../services/base-cloud.service'; import { BaseCloudService } from '../../services/base-cloud.service';
import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model'; import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model';
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.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({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class TaskCloudService extends BaseCloudService { export class TaskCloudService extends BaseCloudService implements TaskCloudInterface {
static TASK_ASSIGNED_STATE = 'ASSIGNED';
dataChangesDetected$ = new Subject(); dataChangesDetected$ = new Subject();
constructor( constructor(
@@ -67,7 +67,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Boolean value if the task can be completed * @returns Boolean value if the task can be completed
*/ */
canCompleteTask(taskDetails: TaskDetailsCloudModel): boolean { 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 * @returns Boolean value if the task is editable
*/ */
isTaskEditable(taskDetails: TaskDetailsCloudModel): boolean { 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 { isAssigneePropertyClickable(taskDetails: TaskDetailsCloudModel, candidateUsers: CardViewArrayItem[], candidateGroups: CardViewArrayItem[]): boolean {
let isClickable = false; let isClickable = false;
const states = [TaskCloudService.TASK_ASSIGNED_STATE]; const states = [TASK_ASSIGNED_STATE];
if (candidateUsers?.length || candidateGroups?.length) { if (candidateUsers?.length || candidateGroups?.length) {
isClickable = states.includes(taskDetails.status); isClickable = states.includes(taskDetails.status);
} }
@@ -94,7 +94,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Boolean value if the task can be completed * @returns Boolean value if the task can be completed
*/ */
canClaimTask(taskDetails: TaskDetailsCloudModel): boolean { 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 { canUnclaimTask(taskDetails: TaskDetailsCloudModel): boolean {
const currentUser = this.identityUserService.getCurrentUserInfo().username; 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;
} }
/** /**

View File

@@ -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<TaskHeaderCloudComponent> = (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
};

View File

@@ -146,6 +146,7 @@ export const completedTaskDetailsCloudMock: TaskDetailsCloudModel = {
'description': 'This is the description ', 'description': 'This is the description ',
'createdDate': new Date(1545048055900), 'createdDate': new Date(1545048055900),
'dueDate': new Date(1545091200000), 'dueDate': new Date(1545091200000),
'completedDate': new Date(1546091200000),
'claimedDate': null, 'claimedDate': null,
'priority': 5, 'priority': 5,
'category': null, 'category': null,
@@ -208,3 +209,63 @@ export const suspendedTaskDetailsCloudMock: TaskDetailsCloudModel = {
'lastModifiedFrom': null, 'lastModifiedFrom': null,
'standalone': true '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
};

View File

@@ -20,12 +20,14 @@ import { CommonModule } from '@angular/common';
import { MaterialModule } from '../../material.module'; import { MaterialModule } from '../../material.module';
import { CoreModule } from '@alfresco/adf-core'; import { CoreModule } from '@alfresco/adf-core';
import { TaskHeaderCloudComponent } from './components/task-header-cloud.component'; import { TaskHeaderCloudComponent } from './components/task-header-cloud.component';
import { TranslateModule } from '@ngx-translate/core';
@NgModule({ @NgModule({
imports: [ imports: [
TranslateModule.forRoot(),
CommonModule, CommonModule,
MaterialModule, MaterialModule,
CoreModule CoreModule.forRoot()
], ],
declarations: [ declarations: [
TaskHeaderCloudComponent TaskHeaderCloudComponent