[ADF-2452] Task Standalone component (#3084)

* Add a new standalone component to show when there is no form attached to a task.
This commit is contained in:
camorra-skk
2018-03-20 17:53:19 +05:30
committed by Eugenio Romano
parent c578529b15
commit afb91cf062
11 changed files with 344 additions and 21 deletions

View File

@@ -0,0 +1,38 @@
---
Added: v2.0.0
Status: Active
---
# Task Standalone component
This component can be used when there is no form attached to a task.
## Contents
- [Basic Usage](#basic-usage)
- [Properties](#properties)
- [Events](#events)
## Basic Usage
```html
<adf-task-standalone
[taskName]= "taskname">
</adf-task-standalone>
```
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| taskName | `string` | | Name of the task |
| isCompleted | `boolean` | `false` | If true then Task completed message is shown and `Complete` and `Cancel` buttons are hidden |
| hasCompletePermission | `boolean` | `true` | Toggle rendering of the `Complete` button. |
| hideCancelButton | `boolean` | `true` | Toggle rendering of the `Cancel` button. |
### Events
| Name | Type | Description |
| ---- | ---- | ----------- |
| cancel | `EventEmitter<void>` | Emitted when the `Cancel` button is clicked. |
| complete | `EventEmitter<void>` | Emitted when the form associated with the task is completed. |

View File

@@ -161,6 +161,11 @@
"EMPTY-LIST": {
"HEADER": "No files are available"
}
},
"STANDALONE_TASK":{
"NO_FORM_MESSAGE": "No forms attached",
"COMPLETE_TASK_MESSAGE": "Task {{taskName}} completed",
"COMPLETE_TASK_SUB_MESSAGE": "No forms to be added"
}
},
"ADF_PROCESS_LIST": {

View File

@@ -8,6 +8,7 @@
@import '../task-list/components/start-task.component';
@import '../task-list/components/task-filters.component';
@import '../task-list/components/task-header.component';
@import '../task-list/components/task-standalone.component';
@import '../app-list/apps-list.component';
@mixin adf-process-services-theme($theme) {
@@ -22,4 +23,5 @@
@include adf-process-attachment-list-theme($theme);
@include adf-task-attachment-list-theme($theme);
@include adf-apps-theme($theme);
@include adf-task-standalone-component-theme($theme);
}

View File

@@ -38,16 +38,18 @@
(formLoaded)='onFormLoaded($event)'
(error)='onFormError($event)'
(executeOutcome)='onFormExecuteOutcome($event)'>
<div empty-form><h3 class="adf-task-title">Please select a Task</h3></div>
</adf-form>
<adf-task-standalone *ngIf="!hasFormKey()"
[taskName]= "taskDetails.name"
[isCompleted]="isCompletedTask()"
[hasCompletePermission]="isCompleteButtonEnabled()"
[hideCancelButton]="true"
(complete)="onComplete()">
</adf-task-standalone>
</div>
<div *ngIf="!isAssigned()" id="claim-message-id">
{{ 'ADF_TASK_LIST.DETAILS.MESSAGES.CLAIM' | translate }}
</div>
<button mat-raised-button class="activiti-task-details__action-button"
*ngIf="isCompleteButtonVisible()" (click)="onComplete()">
{{ 'ADF_TASK_LIST.DETAILS.BUTTON.COMPLETE' | translate }}
</button>
</div>
<div class="adf-task-details-core-sidebar">
<adf-info-drawer *ngIf="showHeaderContent" title="{{ 'ADF_TASK_LIST.DETAILS.LABELS.INFO_DRAWER_TITLE' | translate }}" class="adf-task-details-core-sidebar-drawer">

View File

@@ -52,7 +52,6 @@ describe('TaskDetailsComponent', () => {
let completeTaskSpy: jasmine.Spy;
let logService: LogService;
let commentProcessService: CommentProcessService;
let authService: AuthenticationService;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -85,7 +84,6 @@ describe('TaskDetailsComponent', () => {
service = fixture.debugElement.injector.get(TaskListService);
formService = fixture.debugElement.injector.get(FormService);
commentProcessService = TestBed.get(CommentProcessService);
authService = TestBed.get(AuthenticationService);
getTaskDetailsSpy = spyOn(service, 'getTaskDetails').and.returnValue(Observable.of(taskDetailsMock));
spyOn(formService, 'getTaskForm').and.returnValue(Observable.of(taskFormMock));
@@ -149,27 +147,21 @@ describe('TaskDetailsComponent', () => {
});
}));
it('should display complete button when task without form is assigned to current user', async(() => {
spyOn(authService, 'getBpmUsername').and.returnValue(taskDetailsMock.assignee.email);
taskDetailsMock.formKey = undefined;
it('should display task standalone component when the task does not have an associated form', async(() => {
component.taskId = '123';
fixture.detectChanges();
fixture.whenStable().then(() => {
const completeBtn = fixture.nativeElement.querySelector('button');
expect(completeBtn).toBeDefined();
expect(completeBtn.disabled).toBeFalsy();
expect(completeBtn.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.COMPLETE');
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull();
});
}));
it('should not display complete button when task without form is not assigned to current user', async(() => {
spyOn(authService, 'getBpmUsername').and.returnValue('');
it('should not display task standalone component when the task have an associated form', async(() => {
component.taskId = '123';
taskDetailsMock.formKey = undefined;
fixture.detectChanges();
fixture.whenStable().then(() => {
const completeBtn = fixture.nativeElement.querySelector('.activiti-task-details__action-button');
expect(completeBtn).toBeNull();
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull();
});
}));

View File

@@ -0,0 +1,24 @@
<mat-card class="adf-message-card">
<mat-card-content>
<div class="adf-no-form-message-container">
<div class="adf-no-form-message-list">
<div *ngIf="!isCompleted; else completedMessage" class="adf-no-form-message">
<span id="adf-no-form-message">{{'ADF_TASK_LIST.STANDALONE_TASK.NO_FORM_MESSAGE' | translate}}</span>
</div>
<ng-template #completedMessage>
<div id="adf-completed-form-message" class="adf-no-form-message">
<p>{{'ADF_TASK_LIST.STANDALONE_TASK.COMPLETE_TASK_MESSAGE' | translate : {taskName : taskName} }}</p>
</div>
<div class="adf-no-form-submessage">
{{'ADF_TASK_LIST.STANDALONE_TASK.COMPLETE_TASK_SUB_MESSAGE' | translate}}
</div>
</ng-template>
</div>
</div>
</mat-card-content>
<mat-card-actions class="adf-no-form-mat-card-actions">
<button mat-button *ngIf="hasCancelButton()" id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button>
<button mat-button *ngIf="hasCompleteButton()" id="adf-no-form-complete-button" color="primary" (click)="onCompleteButtonClick()">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.COMPLETE' | translate }}</button>
</mat-card-actions>
</mat-card>

View File

@@ -0,0 +1,53 @@
@mixin adf-task-standalone-component-theme($theme) {
$config: mat-typography-config();
$background: map-get($theme, background);
.adf {
&-message-card {
width: 60%;
box-sizing: border-box;
margin: 16px auto;
.mat-card-actions {
border-top: solid 1px mat-color($background, status-bar);
}
}
&-no-form-message-container {
height:256px;
width: 100%;
display: table;
}
&-no-form-message-list {
display: table-cell;
vertical-align: middle;
text-align: center !important;
}
&-no-form-message {
padding-bottom: 10px;
font-size: mat-font-size($config, display-1);
line-height: 36px;
letter-spacing: -1.3px;
opacity: .54;
margin: auto;
width: fit-content !important;
}
&-no-form-submessage {
font-size: mat-font-size($config, subheading-2);
opacity: .54;
margin: auto;
width: fit-content !important;
}
&-no-form-mat-card-actions {
text-align: right;
& .mat-button {
text-transform: uppercase;
border-radius: 5px;
}
& .mat-button-wrapper {
opacity: 0.54;
font-size: mat-font-size($config, button);
font-weight: bold;
}
}
}
}

View File

@@ -0,0 +1,139 @@
/*!
* @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 { TaskStandaloneComponent } from './task-standalone.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import {
MatButtonModule,
MatCardModule
} from '@angular/material';
describe('TaskStandaloneComponent', () => {
let component: TaskStandaloneComponent;
let fixture: ComponentFixture<TaskStandaloneComponent>;
let element: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
TaskStandaloneComponent
],
imports: [
MatButtonModule,
MatCardModule
]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(TaskStandaloneComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
fixture.detectChanges();
});
}));
it('should show Completed message if isCompleted is true', async(() => {
component.isCompleted = true;
fixture.detectChanges();
const completedFormElement = fixture.debugElement.nativeElement.querySelector('#adf-completed-form-message');
const completedFormSubElement = fixture.debugElement.nativeElement.querySelector('.adf-no-form-submessage');
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(element.querySelector('#adf-no-form-message')).toBeNull();
expect(completedFormElement).toBeDefined();
expect(completedFormElement.innerText.trim()).toBe('ADF_TASK_LIST.STANDALONE_TASK.COMPLETE_TASK_MESSAGE');
expect(completedFormSubElement).toBeDefined();
expect(completedFormSubElement.innerText).toBe('ADF_TASK_LIST.STANDALONE_TASK.COMPLETE_TASK_SUB_MESSAGE');
expect(element.querySelector('.adf-no-form-mat-card-actions')).toBeDefined();
});
}));
it('should show No form message if isCompleted is false', async(() => {
component.isCompleted = false;
fixture.detectChanges();
const noformElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-message');
fixture.whenStable().then(() => {
expect(noformElement).toBeDefined();
expect(noformElement.innerText).toBe('ADF_TASK_LIST.STANDALONE_TASK.NO_FORM_MESSAGE');
expect(element.querySelector('#adf-completed-form-message')).toBeNull();
expect(element.querySelector('.adf-no-form-submessage')).toBeNull();
});
}));
it('should hide Cancel button by default', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#adf-no-form-cancel-button')).toBeNull();
});
}));
it('should emit cancel event if clicked on Cancel Button ', async(() => {
component.hideCancelButton = false;
component.isCompleted = false;
fixture.detectChanges();
fixture.whenStable().then(() => {
const emitSpy = spyOn(component.cancel, 'emit');
const el = fixture.nativeElement.querySelector('#adf-no-form-cancel-button');
el.click();
expect(emitSpy).toHaveBeenCalled();
});
}));
it('should hide Cancel button if hideCancelButton is true', async(() => {
component.hideCancelButton = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#adf-no-form-cancel-button')).toBeNull();
});
}));
it('should hide Cancel button if isCompleted is true', async(() => {
component.isCompleted = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#adf-no-form-cancel-button')).toBeNull();
});
}));
it('should emit complete event if clicked on Complete Button', async(() => {
component.hasCompletePermission = true;
component.isCompleted = false;
fixture.detectChanges();
fixture.whenStable().then(() => {
const emitSpy = spyOn(component.complete, 'emit');
expect(element.querySelector('#adf-no-form-complete-button')).toBeDefined();
const el = fixture.nativeElement.querySelector('#adf-no-form-complete-button');
el.click();
expect(emitSpy).toHaveBeenCalled();
});
}));
it('should hide Complete button if isCompleted is true', async(() => {
component.isCompleted = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#adf-no-form-complete-button')).toBeNull();
});
}));
it('should hide Complete button if hasCompletePermission is false', async(() => {
component.hasCompletePermission = false;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#adf-no-form-complete-button')).toBeNull();
});
}));
});

View File

@@ -0,0 +1,64 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'adf-task-standalone',
templateUrl: './task-standalone.component.html',
styleUrls: ['./task-standalone.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class TaskStandaloneComponent {
@Input()
taskName: string;
@Input()
isCompleted: boolean = false;
@Input()
hasCompletePermission: boolean = true;
@Input()
hideCancelButton: boolean = true;
@Output()
cancel: EventEmitter<void> = new EventEmitter<void>();
@Output()
complete: EventEmitter<void> = new EventEmitter<void>();
constructor() { }
onCancelButtonClick(): void {
this.cancel.emit();
}
onCompleteButtonClick(): void {
this.complete.emit();
}
hasCompleteButton(): boolean {
return this.hasCompletePermission && !this.isCompleted;
}
hasCancelButton(): boolean {
return !this.hideCancelButton && !this.isCompleted;
}
}

View File

@@ -23,6 +23,7 @@ export * from './components/task-filters.component';
export * from './components/task-details.component';
export * from './components/task-audit.directive';
export * from './components/start-task.component';
export * from './components/task-standalone.component';
export * from './services/tasklist.service';
export * from './services/process-upload.service';

View File

@@ -41,6 +41,7 @@ import { TaskDetailsComponent } from './components/task-details.component';
import { TaskFiltersComponent } from './components/task-filters.component';
import { TaskHeaderComponent } from './components/task-header.component';
import { TaskListComponent } from './components/task-list.component';
import { TaskStandaloneComponent } from './components/task-standalone.component';
@NgModule({
imports: [
@@ -68,7 +69,8 @@ import { TaskListComponent } from './components/task-list.component';
TaskAuditDirective,
ChecklistComponent,
TaskHeaderComponent,
StartTaskComponent
StartTaskComponent,
TaskStandaloneComponent
],
providers: [
TaskListService,
@@ -85,7 +87,8 @@ import { TaskListComponent } from './components/task-list.component';
TaskAuditDirective,
ChecklistComponent,
TaskHeaderComponent,
StartTaskComponent
StartTaskComponent,
TaskStandaloneComponent
]
})
export class TaskListModule {}