[ADF-3409] [ADF-3413] Standalone task - Attach/Remove forms bugs (#3670)

* Fix 3409 disable button logic
Fix 3413 change the formName based on selected form

* [ADF-3409] fixed missing import

* [ADF-3409] fixed Observable.of in of
This commit is contained in:
Maurizio Vitale
2018-08-10 10:17:19 +01:00
committed by Eugenio Romano
parent e1d5ef6ee9
commit 8330ed2879
8 changed files with 210 additions and 34 deletions

View File

@@ -5,7 +5,7 @@ Status: Active
# Task Standalone component # Task Standalone component
This component can be used when there is no form attached to a task. This component can be used when the task doesn't belong to any processes.
## Basic Usage ## Basic Usage

View File

@@ -17,6 +17,76 @@
import { TaskDetailsModel } from '../../task-list/models/task-details.model'; import { TaskDetailsModel } from '../../task-list/models/task-details.model';
export let standaloneTaskWithForm = new TaskDetailsModel({
'id': '100',
'name': 'Standalone Task With Form',
'description': null,
'category': null,
'assignee': { 'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com' },
'created': '2016-11-03T15:25:42.749+0000',
'dueDate': null,
'endDate': null,
'duration': null,
'priority': 50,
'parentTaskId': null,
'parentTaskName': null,
'processInstanceId': null,
'processInstanceName': null,
'processDefinitionId': null,
'processDefinitionName': null,
'processDefinitionDescription': null,
'processDefinitionKey': null,
'processDefinitionCategory': null,
'processDefinitionVersion': null,
'processDefinitionDeploymentId': null,
'formKey': '222',
'processInstanceStartUserId': null,
'initiatorCanCompleteTask': false,
'adhocTaskCanBeReassigned': false,
'taskDefinitionKey': 'sid-DDECD9E4-0299-433F-9193-C3D905C3EEBE',
'executionId': '86',
'involvedGroups': [],
'involvedPeople': [],
'memberOfCandidateUsers': false,
'managerOfCandidateGroup': false,
'memberOfCandidateGroup': false
});
export let standaloneTaskWithoutForm = new TaskDetailsModel({
'id': '200',
'name': 'Standalone Task Without Form',
'description': null,
'category': null,
'assignee': { 'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com' },
'created': '2016-11-03T15:25:42.749+0000',
'dueDate': null,
'endDate': null,
'duration': null,
'priority': 50,
'parentTaskId': null,
'parentTaskName': null,
'processInstanceId': null,
'processInstanceName': null,
'processDefinitionId': null,
'processDefinitionName': null,
'processDefinitionDescription': null,
'processDefinitionKey': null,
'processDefinitionCategory': null,
'processDefinitionVersion': null,
'processDefinitionDeploymentId': null,
'formKey': null,
'processInstanceStartUserId': null,
'initiatorCanCompleteTask': false,
'adhocTaskCanBeReassigned': false,
'taskDefinitionKey': 'sid-DDECD9E4-0299-433F-9193-C3D905C3EEBE',
'executionId': '86',
'involvedGroups': [],
'involvedPeople': [],
'memberOfCandidateUsers': false,
'managerOfCandidateGroup': false,
'memberOfCandidateGroup': false
});
export let taskDetailsMock = new TaskDetailsModel({ export let taskDetailsMock = new TaskDetailsModel({
'id': '91', 'id': '91',
'name': 'Request translation', 'name': 'Request translation',

View File

@@ -7,14 +7,14 @@
</mat-card-title> </mat-card-title>
<div class="adf-attach-form-row"> <div class="adf-attach-form-row">
<mat-form-field class="adf-grid-full-width"> <mat-form-field class="adf-grid-full-width">
<mat-select placeholder="{{ 'ADF_TASK_LIST.ATTACH_FORM.SELECT_OPTION' | translate }}" id="form_id" [(ngModel)]="formId"> <mat-select [formControl]="attachFormControl" placeholder="{{ 'ADF_TASK_LIST.ATTACH_FORM.SELECT_OPTION' | translate }}" id="form_id" [(ngModel)]="selectedFormId">
<mat-option *ngFor="let form of forms" [value]="form.id">{{ form.name }}</mat-option> <mat-option *ngFor="let form of forms" [value]="form.id">{{ form.name }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<adf-form <adf-form *ngIf="this.attachFormControl.valid"
[formId]="formId" [formId]="selectedFormId"
[readOnly]="true" [readOnly]="true"
[showCompleteButton]="false" [showCompleteButton]="false"
[showRefreshButton]="false" [showRefreshButton]="false"
@@ -29,7 +29,7 @@
</div> </div>
<div> <div>
<button mat-button id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button> <button mat-button id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button>
<button mat-button id="adf-no-form-attach-form-button" color="primary" (click)="onAttachFormButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.LABEL.ATTACHFORM' | translate }}</button> <button mat-button id="adf-no-form-attach-form-button" [disabled]="disableSubmit" color="primary" (click)="onAttachFormButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.LABEL.ATTACHFORM' | translate }}</button>
</div> </div>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>

View File

@@ -21,6 +21,7 @@ import { setupTestBed } from '@alfresco/adf-core';
import { ProcessTestingModule } from '../../testing/process.testing.module'; import { ProcessTestingModule } from '../../testing/process.testing.module';
import { TaskListService } from './../services/tasklist.service'; import { TaskListService } from './../services/tasklist.service';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { By } from '@angular/platform-browser';
describe('AttachFormComponent', () => { describe('AttachFormComponent', () => {
let component: AttachFormComponent; let component: AttachFormComponent;
@@ -42,6 +43,19 @@ describe('AttachFormComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
})); }));
afterEach(() => {
fixture.destroy();
TestBed.resetTestingModule();
});
it('should show the attach button disabled', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
let attachButton = fixture.debugElement.query(By.css('#adf-no-form-attach-form-button'));
expect(attachButton.nativeElement.disabled).toBeTruthy();
});
}));
it('should emit cancel event if clicked on Cancel Button ', async(() => { it('should emit cancel event if clicked on Cancel Button ', async(() => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
@@ -52,9 +66,9 @@ describe('AttachFormComponent', () => {
}); });
})); }));
it('should call attachFormToATask if clicked on Complete Button', async(() => { it('should call attachFormToATask if clicked on attach Button', async(() => {
component.taskId = 1; component.taskId = 1;
component.formId = 2; component.attachFormControl.setValue(2);
spyOn(taskService, 'attachFormToATask').and.returnValue(of(true)); spyOn(taskService, 'attachFormToATask').and.returnValue(of(true));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
@@ -65,12 +79,42 @@ describe('AttachFormComponent', () => {
}); });
})); }));
it('should render the attachForm enabled if the user select the different formId', async(() => {
component.taskId = 1;
component.formId = 2;
component.attachFormControl.setValue(3);
fixture.detectChanges();
spyOn(taskService, 'attachFormToATask').and.returnValue(of(true));
fixture.detectChanges();
fixture.whenStable().then(() => {
let attachButton = fixture.debugElement.query(By.css('#adf-no-form-attach-form-button'));
expect(attachButton.nativeElement.disabled).toBeFalsy();
});
}));
it('should render a disabled attachForm button if the user select the original formId', async(() => {
component.taskId = 1;
component.formId = 2;
component.attachFormControl.setValue(3);
fixture.detectChanges();
spyOn(taskService, 'attachFormToATask').and.returnValue(of(true));
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
component.attachFormControl.setValue(2);
fixture.detectChanges();
let attachButton = fixture.debugElement.query(By.css('#adf-no-form-attach-form-button'));
expect(attachButton.nativeElement.disabled).toBeTruthy();
});
}));
it('should show the adf-form of the selected form', async(() => { it('should show the adf-form of the selected form', async(() => {
component.taskId = 1; component.taskId = 1;
component.formKey = 12; component.selectedFormId = 12;
fixture.detectChanges(); fixture.detectChanges();
const formContainer = fixture.debugElement.nativeElement.querySelector('adf-form');
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges();
const formContainer = fixture.debugElement.nativeElement.querySelector('adf-form');
expect(formContainer).toBeDefined(); expect(formContainer).toBeDefined();
expect(formContainer).not.toBeNull(); expect(formContainer).not.toBeNull();
}); });
@@ -79,32 +123,33 @@ describe('AttachFormComponent', () => {
it('should show the formPreview of the selected form', async(() => { it('should show the formPreview of the selected form', async(() => {
component.formKey = 12; component.formKey = 12;
fixture.detectChanges(); fixture.detectChanges();
const formContainer = fixture.debugElement.nativeElement.querySelector('.adf-form-container');
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges();
const formContainer = fixture.debugElement.nativeElement.querySelector('.adf-form-container');
expect(formContainer).toBeDefined(); expect(formContainer).toBeDefined();
expect(formContainer).toBeNull(); expect(formContainer).toBeNull();
}); });
})); }));
it('should remove form if it is present', (done) => { it('should remove form if it is present', async(() => {
component.taskId = 1; component.taskId = 1;
component.formId = 10; component.attachFormControl.setValue(10);
component.formKey = 12; component.formKey = 12;
spyOn(taskService, 'deleteForm').and.returnValue(of({})); spyOn(taskService, 'deleteForm').and.returnValue(of({}));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges();
expect(element.querySelector('#adf-no-form-remove-button')).toBeDefined(); expect(element.querySelector('#adf-no-form-remove-button')).toBeDefined();
const el = fixture.nativeElement.querySelector('#adf-no-form-remove-button'); const el = fixture.nativeElement.querySelector('#adf-no-form-remove-button');
el.click(); el.click();
expect(component.formId).toBeNull(); expect(component.formId).toBeNull();
done();
}); });
}); }));
it('should emit success when form is changed', async(() => { it('should emit success when a form is attached', async(() => {
component.taskId = 1; component.taskId = 1;
component.formId = 10; component.attachFormControl.setValue(10);
spyOn(taskService, 'attachFormToATask').and.returnValue(of( spyOn(taskService, 'attachFormToATask').and.returnValue(of(
{ {

View File

@@ -16,9 +16,10 @@
*/ */
import { FormService, LogService } from '@alfresco/adf-core'; import { FormService, LogService } from '@alfresco/adf-core';
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { Form } from '../models/form.model'; import { Form } from '../models/form.model';
import { TaskListService } from './../services/tasklist.service'; import { TaskListService } from './../services/tasklist.service';
import { FormControl, Validators } from '@angular/forms';
@Component({ @Component({
selector: 'adf-attach-form', selector: 'adf-attach-form',
@@ -26,7 +27,7 @@ import { TaskListService } from './../services/tasklist.service';
styleUrls: ['./attach-form.component.scss'] styleUrls: ['./attach-form.component.scss']
}) })
export class AttachFormComponent implements OnChanges { export class AttachFormComponent implements OnInit, OnChanges {
constructor(private taskService: TaskListService, constructor(private taskService: TaskListService,
private logService: LogService, private logService: LogService,
private formService: FormService) { } private formService: FormService) { }
@@ -49,20 +50,42 @@ export class AttachFormComponent implements OnChanges {
forms: Form[]; forms: Form[];
formId: number; formId: number;
disableSubmit: boolean = true;
selectedFormId: number;
attachFormControl: FormControl;
ngOnInit() {
this.attachFormControl = new FormControl('', Validators.required);
this.attachFormControl.valueChanges.subscribe( (currentValue) => {
if (this.attachFormControl.valid) {
if ( this.formId !== currentValue) {
this.disableSubmit = false;
} else {
this.disableSubmit = true;
}
}
});
}
ngOnChanges() { ngOnChanges() {
this.formId = undefined;
this.disableSubmit = true;
this.loadFormsTask(); this.loadFormsTask();
this.onFormAttached(); if (this.formKey) {
this.onFormAttached();
}
} }
onCancelButtonClick(): void { onCancelButtonClick(): void {
this.selectedFormId = this.formId;
this.cancelAttachForm.emit(); this.cancelAttachForm.emit();
} }
onRemoveButtonClick(): void { onRemoveButtonClick(): void {
this.taskService.deleteForm(this.taskId).subscribe( this.taskService.deleteForm(this.taskId).subscribe(
() => { () => {
this.formId = null; this.formId = this.selectedFormId = null;
this.success.emit(); this.success.emit();
}, },
(err) => { (err) => {
@@ -72,7 +95,7 @@ export class AttachFormComponent implements OnChanges {
} }
onAttachFormButtonClick(): void { onAttachFormButtonClick(): void {
this.attachForm(this.taskId, this.formId); this.attachForm(this.taskId, this.selectedFormId);
} }
private loadFormsTask(): void { private loadFormsTask(): void {
@@ -89,7 +112,7 @@ export class AttachFormComponent implements OnChanges {
this.formService.getTaskForm(this.taskId) this.formService.getTaskForm(this.taskId)
.subscribe((res) => { .subscribe((res) => {
this.formService.getFormDefinitionByName(res.name).subscribe((formDef) => { this.formService.getFormDefinitionByName(res.name).subscribe((formDef) => {
this.formId = formDef; this.formId = this.selectedFormId = formDef;
}); });
}, (err) => { }, (err) => {
this.error.emit(err); this.error.emit(err);

View File

@@ -31,7 +31,7 @@
<div class="adf-task-details-core-form"> <div class="adf-task-details-core-form">
<div *ngIf="isAssigned()"> <div *ngIf="isAssigned()">
<adf-form *ngIf="hasFormKey() && !showAttachForm" #activitiForm <adf-form *ngIf="isFormComponentVisible()" #activitiForm
[showDebugButton]="debugMode" [showDebugButton]="debugMode"
[taskId]="taskDetails.id" [taskId]="taskDetails.id"
[showTitle]="showFormTitle" [showTitle]="showFormTitle"
@@ -48,7 +48,7 @@
(error)='onFormError($event)' (error)='onFormError($event)'
(executeOutcome)='onFormExecuteOutcome($event)'> (executeOutcome)='onFormExecuteOutcome($event)'>
</adf-form> </adf-form>
<adf-task-standalone *ngIf="!hasFormKey() && !showAttachForm" <adf-task-standalone *ngIf="isTaskStandaloneComponentVisible()"
[taskName]="taskDetails.name" [taskName]="taskDetails.name"
[taskId]="taskDetails.id" [taskId]="taskDetails.id"
[isCompleted]="isCompletedTask()" [isCompleted]="isCompletedTask()"
@@ -57,7 +57,7 @@
(complete)="onComplete()" (complete)="onComplete()"
(showAttachForm)="onShowAttachForm()"> (showAttachForm)="onShowAttachForm()">
</adf-task-standalone> </adf-task-standalone>
<adf-attach-form *ngIf="showAttachForm" <adf-attach-form *ngIf="isShowAttachForm()"
[taskId]="taskDetails.id" [taskId]="taskDetails.id"
[formKey]="taskDetails.formKey" [formKey]="taskDetails.formKey"
(cancelAttachForm)="onCancelAttachForm()" (cancelAttachForm)="onCancelAttachForm()"

View File

@@ -25,7 +25,7 @@ import { CommentProcessService, LogService, AuthenticationService } from '@alfre
import { UserProcessModel } from '@alfresco/adf-core'; import { UserProcessModel } from '@alfresco/adf-core';
import { TaskDetailsModel } from '../models/task-details.model'; import { TaskDetailsModel } from '../models/task-details.model';
import { noDataMock, taskDetailsMock, taskFormMock, tasksMock, taskDetailsWithOutAssigneeMock } from '../../mock'; import { noDataMock, taskDetailsMock, standaloneTaskWithForm, standaloneTaskWithoutForm, taskFormMock, tasksMock, taskDetailsWithOutAssigneeMock } from '../../mock';
import { TaskListService } from './../services/tasklist.service'; import { TaskListService } from './../services/tasklist.service';
import { TaskDetailsComponent } from './task-details.component'; import { TaskDetailsComponent } from './task-details.component';
import { ProcessTestingModule } from '../../testing/process.testing.module'; import { ProcessTestingModule } from '../../testing/process.testing.module';
@@ -151,25 +151,40 @@ describe('TaskDetailsComponent', () => {
it('should display task standalone component when the task does not have an associated form', async(() => { it('should display task standalone component when the task does not have an associated form', async(() => {
component.taskId = '123'; component.taskId = '123';
getTaskDetailsSpy.and.returnValue(of(standaloneTaskWithoutForm));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.isStandaloneTaskWithoutForm()).toBeTruthy();
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull(); expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull();
}); });
})); }));
it('should not display task standalone component when the task have an associated form', async(() => { it('should not display task standalone component when the task has a form', async(() => {
component.taskId = '123'; component.taskId = '123';
component.taskDetails = new TaskDetailsModel(taskDetailsMock); getTaskDetailsSpy.and.returnValue(of(standaloneTaskWithForm));
component.taskDetails.formKey = '10';
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.isStandaloneTaskWithForm()).toBeTruthy();
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).toBeDefined(); expect(fixture.debugElement.query(By.css('adf-task-standalone'))).toBeDefined();
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull(); expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull();
}); });
})); }));
it('should display the AttachFormComponent when standaloneTaskWithForm and click on attach button', async(() => {
component.taskId = '123';
getTaskDetailsSpy.and.returnValue(of(standaloneTaskWithForm));
fixture.detectChanges();
component.onShowAttachForm();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(component.isStandaloneTaskWithForm()).toBeTruthy();
expect(fixture.debugElement.query(By.css('adf-attach-form'))).toBeDefined();
});
}));
it('should display the claim message when the task is not assigned', async(() => { it('should display the claim message when the task is not assigned', async(() => {
component.taskDetails = taskDetailsWithOutAssigneeMock; component.taskDetails = taskDetailsWithOutAssigneeMock;
fixture.detectChanges(); fixture.detectChanges();

View File

@@ -217,11 +217,34 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
if (taskId && !taskId.currentValue) { if (taskId && !taskId.currentValue) {
this.reset(); this.reset();
} else if (taskId && taskId.currentValue) { } else if (taskId && taskId.currentValue) {
this.taskFormName = null;
this.loadDetails(taskId.currentValue); this.loadDetails(taskId.currentValue);
} }
} }
isStandaloneTask(): boolean {
return !(this.taskDetails && (!!this.taskDetails.processDefinitionId));
}
isStandaloneTaskWithForm(): boolean {
return this.isStandaloneTask() && this.hasFormKey();
}
isStandaloneTaskWithoutForm(): boolean {
return this.isStandaloneTask() && !this.hasFormKey();
}
isFormComponentVisible(): boolean {
return this.hasFormKey() && !this.isShowAttachForm();
}
isTaskStandaloneComponentVisible(): boolean {
return this.isStandaloneTaskWithoutForm() && !this.isShowAttachForm();
}
isShowAttachForm(): boolean {
return this.showAttachForm;
}
/** /**
* Reset the task details * Reset the task details
*/ */
@@ -232,10 +255,8 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
/** /**
* Check if the task has a form * Check if the task has a form
*/ */
hasFormKey(): TaskDetailsModel | string | boolean { hasFormKey(): boolean {
return (this.taskDetails return (this.taskDetails && (!!this.taskDetails.formKey));
&& this.taskDetails.formKey
&& this.taskDetails.formKey !== 'null');
} }
isTaskActive() { isTaskActive() {
@@ -272,10 +293,12 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
private loadDetails(taskId: string) { private loadDetails(taskId: string) {
this.taskPeople = []; this.taskPeople = [];
this.readOnlyForm = false; this.readOnlyForm = false;
this.taskFormName = null;
if (taskId) { if (taskId) {
this.taskListService.getTaskDetails(taskId).subscribe( this.taskListService.getTaskDetails(taskId).subscribe(
(res: TaskDetailsModel) => { (res: TaskDetailsModel) => {
this.showAttachForm = false;
this.taskDetails = res; this.taskDetails = res;
if (this.taskDetails.name === 'null') { if (this.taskDetails.name === 'null') {