diff --git a/demo-shell/src/app/components/activiti/activiti.component.html b/demo-shell/src/app/components/activiti/activiti.component.html index d623ab6ba9..7899552e9d 100644 --- a/demo-shell/src/app/components/activiti/activiti.component.html +++ b/demo-shell/src/app/components/activiti/activiti.component.html @@ -85,10 +85,20 @@ - - + + +
+
{{'ADF_TASK_LIST.ATTACHMENT.EMPTY.HEADER' | translate}}
+
+
{{'ADF_TASK_LIST.ATTACHMENT.EMPTY.DRAG-AND-DROP.TITLE' | translate}}
+
{{'ADF_TASK_LIST.ATTACHMENT.EMPTY.DRAG-AND-DROP.SUBTITLE' | translate}}
+
+
+
+
diff --git a/demo-shell/src/app/components/activiti/activiti.component.ts b/demo-shell/src/app/components/activiti/activiti.component.ts index ace8ce4e13..f1b4f26d87 100644 --- a/demo-shell/src/app/components/activiti/activiti.component.ts +++ b/demo-shell/src/app/components/activiti/activiti.component.ts @@ -49,10 +49,12 @@ import { TaskDetailsEvent, TaskFiltersComponent, TaskListComponent, - TaskListService + TaskListService, + TaskAttachmentListComponent, + ProcessUploadService } from '@alfresco/adf-process-services'; import { LogService } from '@alfresco/adf-core'; -import { AlfrescoApiService } from '@alfresco/adf-core'; +import { AlfrescoApiService, UploadService } from '@alfresco/adf-core'; import { DataSorting, ObjectDataRow, @@ -69,6 +71,9 @@ const currentTaskIdNew = '__NEW__'; selector: 'adf-activiti', templateUrl: './activiti.component.html', styleUrls: ['./activiti.component.scss'], + providers: [ + { provide: UploadService, useClass: ProcessUploadService } + ], encapsulation: ViewEncapsulation.None }) export class ActivitiComponent implements AfterViewInit, OnDestroy, OnInit { @@ -79,6 +84,9 @@ export class ActivitiComponent implements AfterViewInit, OnDestroy, OnInit { @ViewChild(TaskListComponent) taskList: TaskListComponent; + @ViewChild(TaskAttachmentListComponent) + taskAttachList: TaskAttachmentListComponent; + @ViewChild(ProcessFiltersComponent) activitiprocessfilter: ProcessFiltersComponent; @@ -147,7 +155,8 @@ export class ActivitiComponent implements AfterViewInit, OnDestroy, OnInit { private apiService: AlfrescoApiService, private logService: LogService, formRenderingService: FormRenderingService, - formService: FormService) { + formService: FormService, + private uploadService: UploadService) { this.dataTasks = new ObjectDataTableAdapter(); this.dataTasks.setSorting(new DataSorting('created', 'desc')); @@ -247,6 +256,7 @@ export class ActivitiComponent implements AfterViewInit, OnDestroy, OnInit { this.currentProcessInstanceId = null; }); this.layoutType = AppsListComponent.LAYOUT_GRID; + this.uploadService.fileUploadComplete.subscribe(value => this.onTaskFileUploadComplete(value.data)); } @@ -480,4 +490,8 @@ export class ActivitiComponent implements AfterViewInit, OnDestroy, OnInit { this.taskList.reload(); this.currentTaskId = null; } + + onTaskFileUploadComplete(content: any) { + this.taskAttachList.add(content); + } } diff --git a/docs/docassets/images/custom-no-content-drag-drop-template.png b/docs/docassets/images/custom-no-content-drag-drop-template.png new file mode 100644 index 0000000000..6b554ba234 Binary files /dev/null and b/docs/docassets/images/custom-no-content-drag-drop-template.png differ diff --git a/docs/docassets/images/default-no-content-template.png b/docs/docassets/images/default-no-content-template.png new file mode 100644 index 0000000000..a33d457b22 Binary files /dev/null and b/docs/docassets/images/default-no-content-template.png differ diff --git a/docs/task-attachment-list.component.md b/docs/task-attachment-list.component.md index f87ffac726..d2f136d8fe 100644 --- a/docs/task-attachment-list.component.md +++ b/docs/task-attachment-list.component.md @@ -1,6 +1,6 @@ # Task Attachment List Component -Displays attached documents on a specified task +Displays attached documents on a specified task. ![task-attachment-list-sample](docassets/images/task-attachment-list.png) @@ -9,6 +9,7 @@ Displays attached documents on a specified task - [Basic Usage](#basic-usage) + * [Drag and Drop Functionality](#how-to-add-drag-and-drop-functionality) * [Properties](#properties) * [Events](#events) @@ -24,6 +25,38 @@ Displays attached documents on a specified task (attachmentClick)="YOUR_HANDLER"> ``` +If the List is empty, a default no content template is displayed. + +![default-no-content-template-sample](docassets/images/default-no-content-template.png) + +### How to Add Drag and Drop Functionality + +If we want user to be able to upload attachments for empty lists, We can wrap our component with upload drag area component. In that case, We should also pass a custom *no content template* as shown below with our component urging the user to drag files to upload whenever the list is empty. + +```html + + +
//no content template + +
{{This List is empty}}
+
{{Drag and drop to upload}}
+
+
+
+
+
+
+``` + +[Upload Drag Area Component](./upload-drag-area.component.md) + +If the List is empty, the custom no-content template we passed is displayed. + +![custom-no-content-drag-drop-template-sample](docassets/images/custom-no-content-drag-drop-template.png) ### Properties diff --git a/lib/process-services/attachment/task-attachment-list.component.html b/lib/process-services/attachment/task-attachment-list.component.html index fc3ab365b7..37a87bf709 100644 --- a/lib/process-services/attachment/task-attachment-list.component.html +++ b/lib/process-services/attachment/task-attachment-list.component.html @@ -1,25 +1,29 @@ - - -
{{'ADF_TASK_LIST.ATTACHMENT.EMPTY.HEADER' | translate}}
-
-
{{'ADF_TASK_LIST.ATTACHMENT.EMPTY.DRAG-AND-DROP.TITLE' | translate}}
-
{{'ADF_TASK_LIST.ATTACHMENT.EMPTY.DRAG-AND-DROP.SUBTITLE' | translate}}
-
-
- -
-
- - - - - - - - - - - - + + + +
+ {{'ADF_TASK_LIST.ATTACHMENT.EMPTY.HEADER' | translate}} +
+
+ +
+
+ + + + + + + + + + + + +
diff --git a/lib/process-services/attachment/task-attachment-list.component.spec.ts b/lib/process-services/attachment/task-attachment-list.component.spec.ts index a11a7eebed..6edceb51a7 100644 --- a/lib/process-services/attachment/task-attachment-list.component.spec.ts +++ b/lib/process-services/attachment/task-attachment-list.component.spec.ts @@ -141,7 +141,54 @@ describe('TaskAttachmentList', () => { }); })); - it('should display all actions if attachements are not read only', () => { + it('emit document when a user wants to view the document', () => { + component.emitDocumentContent(mockAttachment.data[1]); + fixture.detectChanges(); + expect(getFileRawContentSpy).toHaveBeenCalled(); + }); + + it('download document when a user wants to view the document', () => { + component.downloadContent(mockAttachment.data[1]); + fixture.detectChanges(); + expect(getFileRawContentSpy).toHaveBeenCalled(); + }); + + it('should show the empty list drag and drop component when the task is not completed', async(() => { + getTaskRelatedContentSpy.and.returnValue(Observable.of({ + 'size': 0, + 'total': 0, + 'start': 0, + 'data': [] + })); + let change = new SimpleChange(null, '123', true); + component.ngOnChanges({'taskId': change}); + component.disabled = false; + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('.adf-custom-empty-template')).not.toBeNull(); + }); + })); + + it('should not show the empty list drag and drop component when is disabled', async(() => { + getTaskRelatedContentSpy.and.returnValue(Observable.of({ + 'size': 0, + 'total': 0, + 'start': 0, + 'data': [] + })); + let change = new SimpleChange(null, '123', true); + component.ngOnChanges({'taskId': change}); + component.disabled = true; + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('.adf-custom-empty-template')).toBeNull(); + expect(fixture.nativeElement.querySelector('div[adf-empty-list-header]').innerText.trim()).toEqual('ADF_TASK_LIST.ATTACHMENT.EMPTY.HEADER'); + }); + })); + + it('should display all actions if attachments are not read only', () => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({'taskId': change}); fixture.detectChanges(); @@ -190,40 +237,6 @@ describe('TaskAttachmentList', () => { }); })); - it('should show the empty list drag and drop component when the task is not completed', async(() => { - getTaskRelatedContentSpy.and.returnValue(Observable.of({ - 'size': 0, - 'total': 0, - 'start': 0, - 'data': [] - })); - let change = new SimpleChange(null, '123', true); - component.ngOnChanges({'taskId': change}); - - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('adf-empty-list .adf-empty-list-drag_drop').innerText.trim()).toEqual('ADF_TASK_LIST.ATTACHMENT.EMPTY.DRAG-AND-DROP.TITLE'); - }); - })); - - it('should not show the empty list drag and drop component when is disabled', async(() => { - getTaskRelatedContentSpy.and.returnValue(Observable.of({ - 'size': 0, - 'total': 0, - 'start': 0, - 'data': [] - })); - let change = new SimpleChange(null, '123', true); - component.ngOnChanges({'taskId': change}); - component.disabled = true; - - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('adf-empty-list .adf-empty-list-drag_drop')).toBeNull(); - expect(fixture.nativeElement.querySelector('div[adf-empty-list-header]').innerText.trim()).toEqual('ADF_TASK_LIST.ATTACHMENT.EMPTY.HEADER'); - }); - })); - it('should show the empty list component when the attachments list is empty for completed task', async(() => { getTaskRelatedContentSpy.and.returnValue(Observable.of({ 'size': 0, @@ -270,7 +283,7 @@ describe('TaskAttachmentList', () => { expect(getTaskRelatedContentSpy).toHaveBeenCalledWith('456'); }); - it('should NOT fetch new attachments when empty changeset made', () => { + it('should NOT fetch new attachments when empty change set made', () => { component.ngOnChanges({}); expect(getTaskRelatedContentSpy).not.toHaveBeenCalled(); }); @@ -292,5 +305,11 @@ describe('TaskAttachmentList', () => { expect(true).toBe(true); }); + it('delete content by contentId', () => { + component.deleteAttachmentById(5); + fixture.detectChanges(); + expect(deleteContentSpy).toHaveBeenCalled(); + }); + }); }); diff --git a/lib/process-services/attachment/task-attachment-list.component.ts b/lib/process-services/attachment/task-attachment-list.component.ts index fd159f69ec..f291df18fc 100644 --- a/lib/process-services/attachment/task-attachment-list.component.ts +++ b/lib/process-services/attachment/task-attachment-list.component.ts @@ -16,17 +16,16 @@ */ import { ContentService, ThumbnailService } from '@alfresco/adf-core'; -import { Component, EventEmitter, Input, NgZone, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, NgZone, OnChanges, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; import { ProcessContentService } from '@alfresco/adf-core'; -declare var require: any; - @Component({ selector: 'adf-task-attachment-list', styleUrls: ['./task-attachment-list.component.scss'], - templateUrl: './task-attachment-list.component.html' + templateUrl: './task-attachment-list.component.html', + encapsulation: ViewEncapsulation.None }) -export class TaskAttachmentListComponent implements OnChanges { +export class TaskAttachmentListComponent implements OnChanges, AfterViewInit { @Input() taskId: string; @@ -43,8 +42,9 @@ export class TaskAttachmentListComponent implements OnChanges { @Output() error: EventEmitter = new EventEmitter(); - @Input() - emptyListImageUrl: string = require('../assets/images/empty_doc_lib.svg'); + hasCustomTemplate: boolean; + + @ViewChild('customEmptyListTemplate') customTemplateRef: ElementRef; attachments: any[] = []; isLoading: boolean = true; @@ -61,6 +61,13 @@ export class TaskAttachmentListComponent implements OnChanges { } } + ngAfterViewInit() { + if (this.customTemplateRef && this.customTemplateRef.nativeElement && + this.customTemplateRef.nativeElement.children && this.customTemplateRef.nativeElement.children.length > 0) { + this.hasCustomTemplate = true; + } + } + reset(): void { this.attachments = []; } @@ -110,7 +117,7 @@ export class TaskAttachmentListComponent implements OnChanges { } } - private deleteAttachmentById(contentId: number) { + deleteAttachmentById(contentId: number) { if (contentId) { this.activitiContentService.deleteRelatedContent(contentId).subscribe( (res: any) => { @@ -128,6 +135,10 @@ export class TaskAttachmentListComponent implements OnChanges { return this.attachments && this.attachments.length === 0; } + isCustomTemplateDefined(): boolean { + return this.hasCustomTemplate; + } + onShowRowActionsMenu(event: any) { let viewAction = { title: 'ADF_TASK_LIST.MENU_ACTIONS.VIEW_CONTENT',