diff --git a/demo-shell/resources/i18n/en.json b/demo-shell/resources/i18n/en.json
index 9c2ba8c9ce..3b33c436f0 100644
--- a/demo-shell/resources/i18n/en.json
+++ b/demo-shell/resources/i18n/en.json
@@ -270,14 +270,16 @@
},
"TOOLTIP_MESSAGE": {
"START_INPUT": "Starting page"
- }
+ },
+ "TASK_CONTEXT_MENU": "Task List Context Menu"
},
"PROCESS_LIST_DEMO": {
"ERROR_MESSAGE": {
"APP_ID_REQUIRED_ERROR": "Insert App ID",
"APP_ID_TYPE_ERROR": "App ID must be a number",
"NUMBER_GREATER_THAN": "Value must be greater than or equal to {{ value }}"
- }
+ },
+ "PROCESS_CONTEXT_MENU": "Process List Context Menu"
},
"GROUP-TITLE1-TRANSLATION-KEY": "CUSTOM TITLE TRANSLATION ONE",
"GROUP-TITLE2-TRANSLATION-KEY": "CUSTOM TITLE TRANSLATION TWO",
diff --git a/demo-shell/src/app/components/process-service/process-service.component.html b/demo-shell/src/app/components/process-service/process-service.component.html
index 680eac1af1..4b6263aa97 100644
--- a/demo-shell/src/app/components/process-service/process-service.component.html
+++ b/demo-shell/src/app/components/process-service/process-service.component.html
@@ -47,6 +47,8 @@
[state]="taskFilter?.filter?.state"
[sort]="taskFilter?.filter?.sort"
[landingTaskId]="currentTaskId"
+ [showContextMenu]="taskContextMenu"
+ (showRowContextMenu)="onShowTaskRowContextMenu($event)"
(rowClick)="onTaskRowClick($event)"
(success)="onSuccessTaskList()"
(row-click)="onRowClick($event)"
@@ -161,6 +163,8 @@
[page]="processPage"
[size]="paginationPageSize"
[sort]="processFilter?.filter?.sort"
+ [showContextMenu]="processContextMenu"
+ (showRowContextMenu)="onShowProcessRowContextMenu($event)"
(rowClick)="onProcessRowClick($event)"
(row-dblclick)="onProcessRowDblClick($event)"
[multiselect]="multiSelectProcess"
@@ -280,6 +284,12 @@
Multiselect Task List
+
+
+
+
+
+
multiple
diff --git a/demo-shell/src/app/components/process-service/process-service.component.ts b/demo-shell/src/app/components/process-service/process-service.component.ts
index b80519620a..ebc8959b0e 100644
--- a/demo-shell/src/app/components/process-service/process-service.component.ts
+++ b/demo-shell/src/app/components/process-service/process-service.component.ts
@@ -36,7 +36,7 @@ import {
import {
FORM_FIELD_VALIDATORS, FormRenderingService, FormService,
DynamicTableRow, ValidateDynamicTableRowEvent, AppConfigService, PaginationComponent, UserPreferenceValues,
- AlfrescoApiService, UserPreferencesService, LogService
+ AlfrescoApiService, UserPreferencesService, LogService, DataCellEvent
} from '@alfresco/adf-core';
import { AnalyticsReportListComponent } from '@alfresco/adf-insights';
@@ -118,6 +118,8 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit
multiSelectTask = false;
multiSelectProcess = false;
selectionMode = 'single';
+ taskContextMenu = false;
+ processContextMenu = false;
private tabs = { tasks: 0, processes: 1, reports: 2 };
@@ -526,4 +528,33 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit
this.currentTaskId = null;
}
+ onShowTaskRowContextMenu(event: DataCellEvent) {
+ event.value.actions = [
+ {
+ data: event.value.row['obj'],
+ model: {
+ key: 'taskDetails',
+ icon: 'open',
+ title: 'TASK_LIST_DEMO.TASK_CONTEXT_MENU',
+ visible: true
+ },
+ subject: new Subject()
+ }
+ ];
+ }
+
+ onShowProcessRowContextMenu(event: DataCellEvent) {
+ event.value.actions = [
+ {
+ data: event.value.row['obj'],
+ model: {
+ key: 'processDetails',
+ icon: 'open',
+ title: 'PROCESS_LIST_DEMO.PROCESS_CONTEXT_MENU',
+ visible: true
+ },
+ subject: new Subject()
+ }
+ ];
+ }
}
diff --git a/docs/docassets/images/process-instance-list-context-menu.png b/docs/docassets/images/process-instance-list-context-menu.png
new file mode 100644
index 0000000000..f64a927c5d
Binary files /dev/null and b/docs/docassets/images/process-instance-list-context-menu.png differ
diff --git a/docs/docassets/images/task-list-context-menu.png b/docs/docassets/images/task-list-context-menu.png
new file mode 100644
index 0000000000..7a4990aee4
Binary files /dev/null and b/docs/docassets/images/task-list-context-menu.png differ
diff --git a/docs/process-services/components/process-list.component.md b/docs/process-services/components/process-list.component.md
index d53e36abcb..6fe5700df9 100644
--- a/docs/process-services/components/process-list.component.md
+++ b/docs/process-services/components/process-list.component.md
@@ -64,6 +64,7 @@ when the process list is empty:
| size | `number` | | The number of processes to fetch in each page. |
| sort | `string` | | Defines the sort ordering of the list. Possible values are `created-desc`, `created-asc`, `ended-desc`, `ended-asc`. |
| state | `string` | | Defines the state of the processes. Possible values are `running`, `completed` and `all` |
+| showContextMenu | `boolean` | false | Toggles custom context menu for the component. |
### Events
@@ -72,6 +73,7 @@ when the process list is empty:
| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when an error occurs while loading the list of process instances from the server. |
| rowClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when a row in the process list is clicked. |
| success | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`ProcessListModel`](../../../lib/process-services/src/lib/process-list/models/process-list.model.ts)`>` | Emitted when the list of process instances has been loaded successfully from the server. |
+| showRowContextMenu | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataCellEvent`](../../../lib/core/datatable/components/datatable/data-cell.event.ts)`>` | Emitted before the context menu is displayed for a row. |
## Details
@@ -186,6 +188,48 @@ The Process Instance List also supports pagination:
```
+#### showRowContextMenu event
+
+Emitted before the context menu is displayed for a row.
+
+Note that the ProcessInstanceListComponent itself does not populate the context menu with items. You can provide all necessary content via the handler.
+
+```html
+
+
+```
+
+Event properties:
+
+```ts
+value: {
+ row: DataRow,
+ col: DataColumn,
+ actions: []
+}
+```
+
+Handler example:
+
+```ts
+onShowRowContextMenu(event: DataCellEvent) {
+ event.value.actions = [
+ { title: 'Process List Context Menu' },
+ { ... }
+ ]
+}
+```
+
+
+This event is cancellable. You can use `event.preventDefault()` to prevent the default behavior.
+
+The ProcessInstanceList will automatically render the supplied menu items.
+
+See the [ContextMenu](https://www.npmjs.com/package/ng2-alfresco-core)
+documentation for more details on the format and behavior of context actions.
+
## See also
- [Data column component](../../core/components/data-column.component.md)
diff --git a/docs/process-services/components/task-list.component.md b/docs/process-services/components/task-list.component.md
index adaa840e61..15ec26bf81 100644
--- a/docs/process-services/components/task-list.component.md
+++ b/docs/process-services/components/task-list.component.md
@@ -73,6 +73,7 @@ when the task list is empty:
| start | `number` | | Starting point of the list within the full set of tasks. |
| state | `string` | | Current state of the process. Possible values are: `completed`, `active`. |
| taskId | `string` | | The id of a task |
+| showContextMenu | `boolean` | false | Toggles custom context menu for the component. |
### Events
@@ -82,6 +83,7 @@ when the task list is empty:
| rowClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when a task in the list is clicked |
| rowsSelected | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when rows are selected/unselected |
| success | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when the task list is loaded |
+| showRowContextMenu | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataCellEvent`](../../../lib/core/datatable/components/datatable/data-cell.event.ts)`>` | Emitted before the context menu is displayed for a row. |
## Details
@@ -228,6 +230,50 @@ typical tasklist:
You can customize the styling of a column and also add features like tooltips and automatic translation of column titles. See the [Data Column component](../../core/components/data-column.component.md) page for more information about these features.
+#### showRowContextMenu event
+
+Emitted before the context menu is displayed for a row.
+
+Note that the TaskListComponent itself does not populate the context menu with items.
+You can provide all necessary content via the handler.
+
+```html
+
+
+```
+
+Event properties:
+
+```ts
+value: {
+ row: DataRow,
+ col: DataColumn,
+ actions: []
+}
+```
+
+Handler example:
+
+```ts
+onShowRowContextMenu(event: DataCellEvent) {
+ event.value.actions = [
+ { title: 'Task List Context Menu' },
+ { ... }
+ ]
+}
+```
+
+
+
+This event is cancellable. You can use `event.preventDefault()` to prevent the default behavior.
+
+The TaskListComponent will automatically render the supplied menu items.
+
+See the [ContextMenu](https://www.npmjs.com/package/ng2-alfresco-core)
+documentation for more details on the format and behavior of context actions.
+
## See also
- [Data column component](../../core/components/data-column.component.md)
diff --git a/lib/process-services/src/lib/process-list/components/process-list.component.html b/lib/process-services/src/lib/process-list/components/process-list.component.html
index e67ec4916a..4469bec092 100644
--- a/lib/process-services/src/lib/process-list/components/process-list.component.html
+++ b/lib/process-services/src/lib/process-list/components/process-list.component.html
@@ -7,6 +7,8 @@
[selectionMode]="selectionMode"
[multiselect]="multiselect"
[resolverFn]="resolverFn"
+ [contextMenu]="showContextMenu"
+ (showRowContextMenu)="onShowRowContextMenu($event)"
(rowClick)="onRowClick($event)"
(row-keyup)="onRowKeyUp($event)">
diff --git a/lib/process-services/src/lib/process-list/components/process-list.component.spec.ts b/lib/process-services/src/lib/process-list/components/process-list.component.spec.ts
index 16bac42410..f9ce269517 100644
--- a/lib/process-services/src/lib/process-list/components/process-list.component.spec.ts
+++ b/lib/process-services/src/lib/process-list/components/process-list.component.spec.ts
@@ -15,14 +15,14 @@
* limitations under the License.
*/
-import { Component, SimpleChange, ViewChild } from '@angular/core';
+import { Component, SimpleChange, ViewChild, OnInit, Output, EventEmitter } from '@angular/core';
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
-import { of, throwError } from 'rxjs';
+import { of, throwError, Subject } from 'rxjs';
import { By } from '@angular/platform-browser';
import { ProcessInstanceListComponent } from './process-list.component';
import {
AppConfigService, setupTestBed, CoreModule, DataTableModule, DataRow, DataColumn,
- DataRowEvent, ObjectDataRow, ObjectDataTableAdapter
+ DataRowEvent, ObjectDataRow, ObjectDataTableAdapter, DataCellEvent
} from '@alfresco/adf-core';
import { fakeProcessInstance, fakeProcessInstancesWithNoName, fakeProcessInstancesEmpty, fakeProcessCustomSchema } from '../../mock';
import { ProcessService } from '../services/process.service';
@@ -527,3 +527,115 @@ describe('Process List: Custom EmptyTemplateComponent', () => {
});
});
});
+
+@Component({
+ template: `
+
+
+
+
+
+
+ {{entry.row.obj.startedBy | fullName}}
+
+
+
+ `
+})
+
+class ProcessListContextMenuComponent implements OnInit {
+
+ appId: number;
+ @Output()
+ contextAction = new EventEmitter();
+ private performAction$ = new Subject();
+
+ ngOnInit() {
+ this.performContextActions();
+ }
+
+ onShowRowContextMenu(event: DataCellEvent) {
+ event.value.actions = [
+ {
+ data: event.value.row['obj'],
+ model:
+ {
+ key: 'processDetails',
+ icon: 'open',
+ title: 'View Process Details',
+ visible: true
+ },
+ subject: this.performAction$
+ },
+ {
+ data: event.value.row['obj'],
+ model:
+ {
+ key: 'cancel',
+ icon: 'open',
+ title: 'Cancel Process',
+ visible: true
+ },
+ subject: this.performAction$
+ }
+ ];
+ }
+
+ performContextActions() {
+ this.performAction$
+ .subscribe((action: any) => {
+ this.contextAction.emit(action.data);
+ });
+ }
+}
+
+describe('ProcessListContextMenuComponent', () => {
+ let fixture: ComponentFixture;
+ let customComponent: ProcessListContextMenuComponent;
+ let processService: ProcessService;
+ let element: HTMLElement;
+
+ setupTestBed({
+ imports: [CoreModule.forRoot()],
+ declarations: [ProcessInstanceListComponent, ProcessListContextMenuComponent],
+ providers: [ProcessService]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ProcessListContextMenuComponent);
+ customComponent = fixture.componentInstance;
+ element = fixture.nativeElement;
+ processService = TestBed.get(ProcessService);
+ customComponent.appId = 12345;
+ spyOn(processService, 'getProcesses').and.returnValue(of(fakeProcessInstance));
+ fixture.detectChanges();
+ });
+
+ afterEach(() => {
+ const event = new KeyboardEvent('keydown', {
+ bubbles : true, cancelable : true, key : 'Escape'
+ });
+ document.querySelector('.cdk-overlay-backdrop').dispatchEvent(event);
+ fixture.detectChanges();
+ });
+
+ it('Should be able to show context menu on process list', async () => {
+ const contextMenu = element.querySelector(`[data-automation-id="text_${fakeProcessInstance.data[0].name}"]`);
+ const contextActionSpy = spyOn(customComponent.contextAction, 'emit').and.callThrough();
+ contextMenu.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true }));
+ fixture.detectChanges();
+ await fixture.whenStable();
+ const contextActions = document.querySelectorAll('.mat-menu-item');
+
+ expect(contextActions.length).toBe(2);
+ expect(contextActions[0]['disabled']).toBe(false, 'View Process Details action not enabled');
+ expect(contextActions[1]['disabled']).toBe(false, 'Cancel Process action not enabled');
+ contextActions[0].dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+ expect(contextActionSpy).toHaveBeenCalled();
+ });
+});
diff --git a/lib/process-services/src/lib/process-list/components/process-list.component.ts b/lib/process-services/src/lib/process-list/components/process-list.component.ts
index 502292995a..5cd9a1a8ca 100644
--- a/lib/process-services/src/lib/process-list/components/process-list.component.ts
+++ b/lib/process-services/src/lib/process-list/components/process-list.component.ts
@@ -27,7 +27,8 @@ import {
PaginatedComponent,
PaginationComponent,
PaginationModel,
- UserPreferencesService
+ UserPreferencesService,
+ DataCellEvent
} from '@alfresco/adf-core';
import {
AfterContentInit,
@@ -110,6 +111,14 @@ export class ProcessInstanceListComponent extends DataTableSchema implements OnC
@Input()
selectFirstRow: boolean = true;
+ /** Toggles custom context menu for the component. */
+ @Input()
+ showContextMenu: boolean = false;
+
+ /** Emitted before the context menu is displayed for a row. */
+ @Output()
+ showRowContextMenu = new EventEmitter();
+
/**
* Resolver function is used to show dynamic complex column objects
* see the docs to learn how to configure a resolverFn.
@@ -284,6 +293,10 @@ export class ProcessInstanceListComponent extends DataTableSchema implements OnC
}
}
+ onShowRowContextMenu(event: DataCellEvent) {
+ this.showRowContextMenu.emit(event);
+ }
+
private createRequestNode(): ProcessFilterParamRepresentationModel {
return new ProcessFilterParamRepresentationModel({
appDefinitionId: this.appId,
diff --git a/lib/process-services/src/lib/task-list/components/task-list.component.html b/lib/process-services/src/lib/task-list/components/task-list.component.html
index 33f805cf8f..4264ea60e3 100644
--- a/lib/process-services/src/lib/task-list/components/task-list.component.html
+++ b/lib/process-services/src/lib/task-list/components/task-list.component.html
@@ -8,6 +8,8 @@
[loading]="isLoading"
[multiselect]="multiselect"
[selectionMode]="selectionMode"
+ [contextMenu]="showContextMenu"
+ (showRowContextMenu)="onShowRowContextMenu($event)"
(row-select)="onRowSelect($event)"
(row-unselect)="onRowUnselect($event)"
(rowClick)="onRowClick($event)"
diff --git a/lib/process-services/src/lib/task-list/components/task-list.component.spec.ts b/lib/process-services/src/lib/task-list/components/task-list.component.spec.ts
index a7c915514b..eee621373c 100644
--- a/lib/process-services/src/lib/task-list/components/task-list.component.spec.ts
+++ b/lib/process-services/src/lib/task-list/components/task-list.component.spec.ts
@@ -15,16 +15,16 @@
* limitations under the License.
*/
-import { Component, SimpleChange, ViewChild } from '@angular/core';
+import { Component, SimpleChange, ViewChild, OnInit, Output, EventEmitter } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
-import { AppConfigService, setupTestBed, CoreModule, DataTableModule, DataRowEvent, ObjectDataRow } from '@alfresco/adf-core';
+import { AppConfigService, setupTestBed, CoreModule, DataTableModule, DataRowEvent, ObjectDataRow, DataCellEvent } from '@alfresco/adf-core';
import { TaskListService } from '../services/tasklist.service';
import { TaskListComponent } from './task-list.component';
import { ProcessTestingModule } from '../../testing/process.testing.module';
import { fakeGlobalTask, fakeCustomSchema, fakeEmptyTask, paginatedTask } from '../../mock';
import { TranslateService } from '@ngx-translate/core';
-import { of } from 'rxjs';
+import { of, Subject } from 'rxjs';
import { TaskListModule } from '../task-list.module';
declare let jasmine: any;
@@ -807,3 +807,112 @@ describe('Task List: Custom EmptyTemplateComponent', () => {
});
});
});
+
+@Component({
+ template: `
+
+
+
+
+
+
+ {{entry.row?.obj?.startedBy | fullName}}
+
+
+
+ `
+})
+
+class TaskListContextMenuComponent implements OnInit {
+
+ @Output()
+ contextAction = new EventEmitter();
+ private performAction$ = new Subject();
+
+ ngOnInit() {
+ this.performContextActions();
+ }
+
+ onShowRowContextMenu(event: DataCellEvent) {
+ event.value.actions = [
+ {
+ data: event.value.row['obj'],
+ model:
+ {
+ key: 'taskDetails',
+ icon: 'open',
+ title: 'View Task Details',
+ visible: true
+ },
+ subject: this.performAction$
+ },
+ {
+ data: event.value.row['obj'],
+ model:
+ {
+ key: 'cancel',
+ icon: 'open',
+ title: 'Cancel Process',
+ visible: true
+ },
+ subject: this.performAction$
+ }
+ ];
+ }
+
+ performContextActions() {
+ this.performAction$
+ .subscribe((action: any) => {
+ this.contextAction.emit(action.data);
+ });
+ }
+}
+
+describe('TaskListContextMenuComponent', () => {
+ let fixture: ComponentFixture;
+ let customComponent: TaskListContextMenuComponent;
+ let taskListService: TaskListService;
+ let element: HTMLElement;
+
+ setupTestBed({
+ imports: [CoreModule.forRoot()],
+ declarations: [TaskListComponent, TaskListContextMenuComponent],
+ providers: [TaskListService]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TaskListContextMenuComponent);
+ customComponent = fixture.componentInstance;
+ element = fixture.nativeElement;
+ taskListService = TestBed.get(TaskListService);
+ spyOn(taskListService, 'findTasksByState').and.returnValues(of(fakeGlobalTask));
+ fixture.detectChanges();
+ });
+
+ afterEach(() => {
+ const event = new KeyboardEvent('keydown', {
+ bubbles : true, cancelable : true, key : 'Escape'
+ });
+ document.querySelector('.cdk-overlay-backdrop').dispatchEvent(event);
+ fixture.detectChanges();
+ });
+
+ it('Should be able to show context menu on task list', async () => {
+ const contextMenu = element.querySelector(`[data-automation-id="text_${fakeGlobalTask.data[0].name}"]`);
+ const contextActionSpy = spyOn(customComponent.contextAction, 'emit').and.callThrough();
+ contextMenu.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true }));
+ fixture.detectChanges();
+ await fixture.whenStable();
+ const contextActions = document.querySelectorAll('.mat-menu-item');
+
+ expect(contextActions.length).toBe(2);
+ expect(contextActions[0]['disabled']).toBe(false, 'View Task Details action not enabled');
+ expect(contextActions[1]['disabled']).toBe(false, 'Cancel Task action not enabled');
+ contextActions[0].dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+ expect(contextActionSpy).toHaveBeenCalled();
+ });
+});
diff --git a/lib/process-services/src/lib/task-list/components/task-list.component.ts b/lib/process-services/src/lib/task-list/components/task-list.component.ts
index 31f8411e00..ed851203d7 100644
--- a/lib/process-services/src/lib/task-list/components/task-list.component.ts
+++ b/lib/process-services/src/lib/task-list/components/task-list.component.ts
@@ -18,7 +18,7 @@
import {
DataRowEvent, DataTableAdapter, DataTableSchema, CustomEmptyContentTemplateDirective, CustomLoadingContentTemplateDirective,
AppConfigService, PaginationComponent, PaginatedComponent,
- UserPreferencesService, UserPreferenceValues, PaginationModel } from '@alfresco/adf-core';
+ UserPreferencesService, UserPreferenceValues, PaginationModel, DataCellEvent } from '@alfresco/adf-core';
import {
AfterContentInit, Component, ContentChild, EventEmitter,
Input, OnChanges, Output, SimpleChanges, OnDestroy, OnInit } from '@angular/core';
@@ -123,6 +123,14 @@ export class TaskListComponent extends DataTableSchema implements OnChanges, Aft
@Input()
start: number;
+ /** Toggles custom context menu for the component. */
+ @Input()
+ showContextMenu: boolean = false;
+
+ /** Emitted before the context menu is displayed for a row. */
+ @Output()
+ showRowContextMenu = new EventEmitter();
+
/** Emitted when a task in the list is clicked */
@Output()
rowClick = new EventEmitter();
@@ -356,6 +364,10 @@ export class TaskListComponent extends DataTableSchema implements OnChanges, Aft
}
}
+ onShowRowContextMenu(event: DataCellEvent) {
+ this.showRowContextMenu.emit(event);
+ }
+
/**
* Optimize name field
* @param instances