mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[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:
committed by
Eugenio Romano
parent
c578529b15
commit
afb91cf062
38
docs/process-services/task-standalone.component.md
Normal file
38
docs/process-services/task-standalone.component.md
Normal 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. |
|
||||
|
@@ -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": {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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">
|
||||
|
@@ -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();
|
||||
});
|
||||
}));
|
||||
|
||||
|
@@ -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>
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
});
|
||||
}));
|
||||
});
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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';
|
||||
|
@@ -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 {}
|
||||
|
Reference in New Issue
Block a user