diff --git a/ng2-components/ng2-activiti-tasklist/README.md b/ng2-components/ng2-activiti-tasklist/README.md index 7d2a86c13d..c36398bb05 100644 --- a/ng2-components/ng2-activiti-tasklist/README.md +++ b/ng2-components/ng2-activiti-tasklist/README.md @@ -39,6 +39,9 @@ - [Task People Component](#task-people-component) * [Properties](#properties-8) + [Events](#events-7) +- [ADF Comments Component](#adf-comments-component) + * [Properties](#properties-9) + + [Events](#events-8) - [Build from sources](#build-from-sources) - [NPM scripts](#npm-scripts) - [Demo](#demo) @@ -460,6 +463,32 @@ This component displays involved users to a specified task No Events +## ADF Comments Component + +This component displays comments entered by involved users to a specified task. It also allows an involved user to add his/her comment to the task. + +```html + + +``` + +![adf-comments](docs/assets/adf-comments.png) + +### Properties + +| Name | Type | Description | +| --- | --- | --- | +| taskId | string | The numeric ID of the task | +| readOnly | boolean | The boolean flag | + +#### Events + +| Name | Description | +| --- | --- | +| error | Raised when an error occurs while displaying/adding a comment | + ## Build from sources You can build component from sources with the following commands: diff --git a/ng2-components/ng2-activiti-tasklist/docs/assets/adf-comments.png b/ng2-components/ng2-activiti-tasklist/docs/assets/adf-comments.png new file mode 100644 index 0000000000..40a31a3c4d Binary files /dev/null and b/ng2-components/ng2-activiti-tasklist/docs/assets/adf-comments.png differ diff --git a/ng2-components/ng2-activiti-tasklist/index.ts b/ng2-components/ng2-activiti-tasklist/index.ts index 70814134a0..92591a486d 100644 --- a/ng2-components/ng2-activiti-tasklist/index.ts +++ b/ng2-components/ng2-activiti-tasklist/index.ts @@ -15,8 +15,9 @@ * limitations under the License. */ +import { DatePipe } from '@angular/common'; import { ModuleWithProviders, NgModule } from '@angular/core'; -import { MdButtonModule, MdIconModule } from '@angular/material'; +import { MdButtonModule, MdIconModule, MdInputModule } from '@angular/material'; import { ActivitiFormModule } from 'ng2-activiti-form'; import { CoreModule } from 'ng2-alfresco-core'; import { DataTableModule } from 'ng2-alfresco-datatable'; @@ -35,6 +36,7 @@ import { ActivitiTaskDetails, ActivitiTaskHeader, ActivitiTaskList, + AdfCommentListComponent, NoTaskDetailsTemplateComponent, PeopleList, TaskAttachmentListComponent @@ -59,7 +61,8 @@ export const ACTIVITI_TASKLIST_DIRECTIVES: any[] = [ ActivitiPeopleSearch, TaskAttachmentListComponent, ActivitiCreateTaskAttachmentComponent, - PeopleList + PeopleList, + AdfCommentListComponent ]; export const ACTIVITI_TASKLIST_PROVIDERS: any[] = [ @@ -73,13 +76,15 @@ export const ACTIVITI_TASKLIST_PROVIDERS: any[] = [ DataTableModule, ActivitiFormModule, MdIconModule, - MdButtonModule + MdButtonModule, + MdInputModule ], declarations: [ ...ACTIVITI_TASKLIST_DIRECTIVES ], providers: [ - ...ACTIVITI_TASKLIST_PROVIDERS + ...ACTIVITI_TASKLIST_PROVIDERS, + DatePipe ], exports: [ ...ACTIVITI_TASKLIST_DIRECTIVES, diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.css b/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.css index 9ca19c95cd..73006380d2 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.css +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.css @@ -1,32 +1,25 @@ -:host { +.adf-comments-container { + height: 100%; width: 100%; -} - -.activiti-label { - font-weight: bolder; -} - -.material-icons:hover { - color: rgb(255, 152, 0); -} - -.mdl-tooltip { - will-change: unset; -} - -.material-icons { - cursor: pointer; -} - -.list-wrap { - word-wrap: break-word; - word-break: break-all; - -moz-hyphens:auto; - -webkit-hyphens:auto; - -o-hyphens:auto; - hyphens:auto; -} - -.hide-long-names { overflow: auto; } + +.adf-comments-header { + padding: 10px 20px; + font-size: 14px; + font-family: Muli; + font-weight: 600; + border-bottom: 1px solid #e1e1e1; + color: #a1a1a1; +} + +.adf-comments-input-container { + padding: 0 15px; + width: calc(100% - 30px); + padding-top: 8px; + border-bottom: 1px solid #e1e1e1; +} + +.adf-full-width { + width: 100%; +} \ No newline at end of file diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.html b/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.html index f5670baefb..c17f0c6154 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.html +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.html @@ -1,32 +1,13 @@ -{{ 'TASK_DETAILS.LABELS.COMMENTS' |translate }} -
add
-
- {{ 'TASK_DETAILS.COMMENTS.ADD' | translate }} -
- - -
- {{ 'TASK_DETAILS.COMMENTS.NONE' | translate }} -
- - - -

{{ 'TASK_DETAILS.COMMENTS.DIALOG.TITLE' | translate }}

-
-
- - -
+
+
+ {{'TASK_DETAILS.COMMENTS.HEADER' | translate}}({{comments?.length}})
-
- - +
+ + +
-
+ + + + diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.spec.ts b/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.spec.ts index 55c448a210..198d0be7eb 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.spec.ts +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.spec.ts @@ -17,14 +17,17 @@ import { SimpleChange } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; import { Observable } from 'rxjs/Rx'; import { ActivitiFormModule } from 'ng2-activiti-form'; import { AlfrescoTranslationService, CoreModule } from 'ng2-alfresco-core'; +import { DatePipe } from '@angular/common'; +import { MdInputModule } from '@angular/material'; +import { DataTableModule } from 'ng2-alfresco-datatable'; import { ActivitiTaskListService } from './../services/activiti-tasklist.service'; import { ActivitiComments } from './activiti-comments.component'; +import { AdfCommentListComponent } from './adf-comment-list.component'; describe('ActivitiComments', () => { @@ -39,13 +42,17 @@ describe('ActivitiComments', () => { TestBed.configureTestingModule({ imports: [ CoreModule.forRoot(), - ActivitiFormModule.forRoot() + ActivitiFormModule.forRoot(), + DataTableModule, + MdInputModule ], declarations: [ - ActivitiComments + ActivitiComments, + AdfCommentListComponent ], providers: [ - ActivitiTaskListService + ActivitiTaskListService, + DatePipe ] }).compileComponents(); @@ -60,11 +67,11 @@ describe('ActivitiComments', () => { service = fixture.debugElement.injector.get(ActivitiTaskListService); getCommentsSpy = spyOn(service, 'getTaskComments').and.returnValue(Observable.of([ - { message: 'Test1' }, - { message: 'Test2' }, - { message: 'Test3'} + { message: 'Test1', created: Date.now(), createdBy: {firstName: 'Admin', lastName: 'User'} }, + { message: 'Test2', created: Date.now(), createdBy: {firstName: 'Admin', lastName: 'User'} }, + { message: 'Test3', created: Date.now(), createdBy: {firstName: 'Admin', lastName: 'User'} } ])); - addCommentSpy = spyOn(service, 'addTaskComment').and.returnValue(Observable.of({id: 123, message: 'Test'})); + addCommentSpy = spyOn(service, 'addTaskComment').and.returnValue(Observable.of({id: 123, message: 'Test Comment'})); componentHandler = jasmine.createSpyObj('componentHandler', [ 'upgradeAllRegistered', @@ -90,7 +97,7 @@ describe('ActivitiComments', () => { expect(emitSpy).toHaveBeenCalled(); }); - it('should not comments when no taskId is specified', () => { + it('should not load comments when no taskId is specified', () => { fixture.detectChanges(); expect(getCommentsSpy).not.toHaveBeenCalled(); }); @@ -101,17 +108,45 @@ describe('ActivitiComments', () => { fixture.whenStable().then(() => { fixture.detectChanges(); - expect(fixture.debugElement.queryAll(By.css('ul.mdl-list li')).length).toBe(3); + expect(fixture.nativeElement.querySelectorAll('#comment-message').length).toBe(3); + expect(fixture.nativeElement.querySelector('#comment-message:empty')).toBeNull(); }); })); + it('should display comments count when the task has comments', () => { + let change = new SimpleChange(null, '123', true); + component.ngOnChanges({ 'taskId': change }); + fixture.whenStable().then(() => { + fixture.detectChanges(); + let element = fixture.nativeElement.querySelector('#comment-header'); + expect(element.innerText).toContain('(3)'); + }); + }); + it('should not display comments when the task has no comments', async(() => { component.taskId = '123'; getCommentsSpy.and.returnValue(Observable.of([])); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('#comment-container')).toBeNull(); + }); + })); + + it('should display comments input by default', async(() => { + let change = new SimpleChange(null, '123', true); + component.ngOnChanges({ 'taskId': change }); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('#comment-input')).not.toBeNull(); + }); + })); + + it('should not display comments input when the task is readonly', async(() => { + component.readOnly = true; fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); - expect(fixture.debugElement.queryAll(By.css('ul.mdl-list li')).length).toBe(0); + expect(fixture.nativeElement.querySelector('#comment-input')).toBeNull(); }); })); @@ -133,21 +168,15 @@ describe('ActivitiComments', () => { expect(getCommentsSpy).toHaveBeenCalledWith('456'); }); - it('should NOT fetch new comments when empty changeset made', () => { + it('should not fetch new comments when empty changeset made', () => { component.ngOnChanges({}); expect(getCommentsSpy).not.toHaveBeenCalled(); }); - it('should NOT fetch new comments when taskId changed to null', () => { + it('should not fetch new comments when taskId changed to null', () => { component.ngOnChanges({ 'taskId': nullChange }); expect(getCommentsSpy).not.toHaveBeenCalled(); }); - - it('should set a placeholder message when taskId changed to null', () => { - component.ngOnChanges({ 'taskId': nullChange }); - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('[data-automation-id="comments-none"]'))).not.toBeNull(); - }); }); describe('Add comment', () => { @@ -158,37 +187,54 @@ describe('ActivitiComments', () => { fixture.whenStable(); })); - it('should display a dialog to the user when the Add button clicked', () => { - let dialogEl = fixture.debugElement.query(By.css('.mdl-dialog')).nativeElement; - let showSpy: jasmine.Spy = spyOn(dialogEl, 'showModal'); - component.showDialog(); - expect(showSpy).toHaveBeenCalled(); - }); + it('should call service to add a comment when enter key is pressed', async(() => { + let event = new KeyboardEvent('keyup', {'key': 'Enter'}); + let element = fixture.nativeElement.querySelector('#comment-input'); + component.message = 'Test Comment'; + element.dispatchEvent(event); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(addCommentSpy).toHaveBeenCalled(); + let elements = fixture.nativeElement.querySelectorAll('#comment-message'); + expect(elements.length).toBe(1); + expect(elements[0].innerText).toBe('Test Comment'); + }); + })); - it('should call service to add a comment', () => { - component.showDialog(); + it('should not call service to add a comment when comment is empty', async(() => { + let event = new KeyboardEvent('keyup', {'key': 'Enter'}); + let element = fixture.nativeElement.querySelector('#comment-input'); + component.message = ''; + element.dispatchEvent(event); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(addCommentSpy).not.toHaveBeenCalled(); + }); + })); + + it('should clear comment when escape key is pressed', async(() => { + let event = new KeyboardEvent('keyup', {'key': 'Escape'}); + let element = fixture.nativeElement.querySelector('#comment-input'); component.message = 'Test comment'; - component.add(); - expect(addCommentSpy).toHaveBeenCalledWith('123', 'Test comment'); - }); + element.dispatchEvent(event); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + element = fixture.nativeElement.querySelector('#comment-input'); + expect(element.value).toBe(''); + }); + })); it('should emit an error when an error occurs adding the comment', () => { let emitSpy = spyOn(component.error, 'emit'); addCommentSpy.and.returnValue(Observable.throw({})); - component.showDialog(); component.message = 'Test comment'; component.add(); expect(emitSpy).toHaveBeenCalled(); }); - it('should close add dialog when close button clicked', () => { - let dialogEl = fixture.debugElement.query(By.css('.mdl-dialog')).nativeElement; - let closeSpy: jasmine.Spy = spyOn(dialogEl, 'close'); - component.showDialog(); - component.cancel(); - expect(closeSpy).toHaveBeenCalled(); - }); - }); }); diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.ts b/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.ts index 86cb09e0b8..b081fc6675 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.ts +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-comments.component.ts @@ -15,13 +15,12 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { AlfrescoTranslationService } from 'ng2-alfresco-core'; import { Observable, Observer } from 'rxjs/Rx'; -import { Comment } from '../models/comment.model'; -import { ActivitiTaskListService } from './../services/activiti-tasklist.service'; -declare let dialogPolyfill: any; +import { Comment } from '../models/comment.model'; +import { ActivitiTaskListService } from '../services/activiti-tasklist.service'; @Component({ selector: 'adf-comments, activiti-comments', @@ -40,9 +39,6 @@ export class ActivitiComments implements OnChanges { @Output() error: EventEmitter = new EventEmitter(); - @ViewChild('dialog') - dialog: any; - comments: Comment [] = []; private commentObserver: Observer; @@ -50,6 +46,8 @@ export class ActivitiComments implements OnChanges { message: string; + beingAdded: boolean = false; + /** * Constructor * @param translate Translation service @@ -79,11 +77,17 @@ export class ActivitiComments implements OnChanges { } } - private getTaskComments(taskId: string) { + private getTaskComments(taskId: string): void { this.resetComments(); if (taskId) { this.activitiTaskList.getTaskComments(taskId).subscribe( (res: Comment[]) => { + res = res.sort((comment1: Comment, comment2: Comment) => { + let date1 = new Date(comment1.created); + let date2 = new Date(comment2.created); + return date1 > date2 ? -1 : date1 < date2 ? 1 : 0; + }); + res.forEach((comment) => { this.commentObserver.next(comment); }); @@ -92,38 +96,35 @@ export class ActivitiComments implements OnChanges { this.error.emit(err); } ); - } else { - this.resetComments(); } } - private resetComments() { + private resetComments(): void { this.comments = []; } - public showDialog() { - if (!this.dialog.nativeElement.showModal) { - dialogPolyfill.registerDialog(this.dialog.nativeElement); + add(): void { + if (this.message && this.message.trim() && !this.beingAdded) { + this.beingAdded = true; + this.activitiTaskList.addTaskComment(this.taskId, this.message).subscribe( + (res: Comment) => { + this.comments.unshift(res); + this.message = ''; + this.beingAdded = false; + }, + (err) => { + this.error.emit(err); + this.beingAdded = false; + } + ); } - this.dialog.nativeElement.showModal(); } - public add() { - this.activitiTaskList.addTaskComment(this.taskId, this.message).subscribe( - (res: Comment) => { - this.comments.push(res); - this.message = ''; - }, - (err) => { - this.error.emit(err); - } - ); - this.cancel(); + clear(): void { + this.message = ''; } - public cancel() { - if (this.dialog) { - this.dialog.nativeElement.close(); - } + isReadOnly(): boolean { + return this.readOnly; } } diff --git a/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.css b/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.css new file mode 100644 index 0000000000..662e6a8cfe --- /dev/null +++ b/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.css @@ -0,0 +1,64 @@ +.adf-comment-img-container { + float: left; + width: 40px; + padding: 5px 10px; + height: 100%; +} + +.adf-comment-user-icon { + padding: 10px 5px; + width: 30px; + background-color: #01bcd4; + border-radius: 50%; + font-family: Muli; + font-size: 16px; + color: #fff; + text-align: center; + height: 18px; +} + +.adf-comment-user-name { + float: left; + width: calc(100% - 120px); + padding: 2px 10px; + font-family: Muli; + font-weight: 600; + color: #595959; +} + +.adf-comment-message { + float: left; + width: calc(100% - 20px); + padding: 2px 10px; + font-family: Muli; + font-style: italic; + color: #595959; + white-space: initial; +} + +.adf-comment-message-time { + float: left; + width: calc(100% - 120px); + padding: 2px 10px; + font-family: Muli; + font-size: 12px; + color: #595959; +} + +.adf-comment-contents { + float: left; + width: calc(100% - 60px); +} + +adf-datatable >>> table thead { + display: none; +} + +adf-datatable >>> table { + border: none; +} + +adf-datatable >>> table tbody td { + padding: 0px!important; + border-top: none; +} diff --git a/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.html b/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.html new file mode 100644 index 0000000000..73956721ca --- /dev/null +++ b/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.html @@ -0,0 +1,30 @@ + + + + + +
+
{{getUserShortName(entry.row.obj.createdBy)}}
+
+
+
+ + +
+
+ {{entry.row.obj.createdBy?.firstName}} {{entry.row.obj.createdBy?.lastName}} +
+
+ {{entry.row.obj.message}} +
+
+ {{transformDate(entry.row.obj.created)}} +
+
+
+
+
+ +
\ No newline at end of file diff --git a/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.spec.ts b/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.spec.ts new file mode 100644 index 0000000000..c1eadccc7b --- /dev/null +++ b/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.spec.ts @@ -0,0 +1,148 @@ +/*! + * @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 { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { CoreModule } from 'ng2-alfresco-core'; +import { DataRowEvent, DataTableModule, ObjectDataRow } from 'ng2-alfresco-datatable'; +import { Comment, User } from '../models/index'; +import { AdfCommentListComponent } from './adf-comment-list.component'; + +declare let jasmine: any; + +const testUser: User = new User({ + id: '1', + firstName: 'Test', + lastName: 'User', + email: 'tu@domain.com' +}); +const testDate = new Date(); +const testComment: Comment = new Comment(1, 'Test Comment', testDate.toDateString(), testUser); + +describe('AdfCommentListComponent', () => { + + let commentList: AdfCommentListComponent; + let fixture: ComponentFixture; + let element: HTMLElement; + let componentHandler; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + CoreModule.forRoot(), + DataTableModule + ], + declarations: [ + AdfCommentListComponent + ], + providers: [ + DatePipe + ] + }).compileComponents().then(() => { + + fixture = TestBed.createComponent(AdfCommentListComponent); + commentList = fixture.componentInstance; + element = fixture.nativeElement; + componentHandler = jasmine.createSpyObj('componentHandler', [ + 'upgradeAllRegistered' + ]); + + window['componentHandler'] = componentHandler; + fixture.detectChanges(); + }); + })); + + it('should emit row click event', (done) => { + let row = new ObjectDataRow(testComment); + let rowEvent = new DataRowEvent(row, null); + + commentList.clickRow.subscribe(selectedComment => { + expect(selectedComment.id).toEqual(1); + expect(selectedComment.message).toEqual('Test Comment'); + expect(selectedComment.createdBy).toEqual(testUser); + expect(selectedComment.created).toEqual(testDate.toDateString()); + done(); + }); + + commentList.selectComment(rowEvent); + }); + + it('should not show comment list if no input is given', () => { + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('adf-datatable')).toBeNull(); + }); + + it('should show comment message when input is given', () => { + commentList.comments = [testComment]; + fixture.detectChanges(); + let elements = fixture.nativeElement.querySelectorAll('#comment-message'); + expect(elements.length).toBe(1); + expect(elements[0].innerText).toBe(testComment.message); + expect(fixture.nativeElement.querySelector('#comment-message:empty')).toBeNull(); + }); + + it('should show comment user when input is given', () => { + commentList.comments = [testComment]; + fixture.detectChanges(); + let elements = fixture.nativeElement.querySelectorAll('#comment-user'); + expect(elements.length).toBe(1); + expect(elements[0].innerText).toBe(testComment.createdBy.firstName + ' ' + testComment.createdBy.lastName); + expect(fixture.nativeElement.querySelector('#comment-user:empty')).toBeNull(); + }); + + it('should show comment date time when input is given', () => { + commentList.comments = [testComment]; + fixture.detectChanges(); + let elements = fixture.nativeElement.querySelectorAll('#comment-time'); + expect(elements.length).toBe(1); + expect(elements[0].innerText).toBe(commentList.transformDate(testDate.toDateString())); + expect(fixture.nativeElement.querySelector('#comment-time:empty')).toBeNull(); + }); + + it('comment date time should start with Today when comment date is today', () => { + commentList.comments = [testComment]; + fixture.detectChanges(); + element = fixture.nativeElement.querySelector('#comment-time'); + expect(element.innerText).toContain('Today'); + }); + + it('comment date time should start with Yesterday when comment date is yesterday', () => { + testComment.created = (Date.now() - 24 * 3600 * 1000).toString(); + commentList.comments = [testComment]; + fixture.detectChanges(); + element = fixture.nativeElement.querySelector('#comment-time'); + expect(element.innerText).toContain('Yesterday'); + }); + + it('comment date time should not start with Today/Yesterday when comment date is before yesterday', () => { + testComment.created = (Date.now() - 24 * 3600 * 1000 * 2).toString(); + commentList.comments = [testComment]; + fixture.detectChanges(); + element = fixture.nativeElement.querySelector('#comment-time'); + expect(element.innerText).not.toContain('Today'); + expect(element.innerText).not.toContain('Yesterday'); + }); + + it('should show user icon when input is given', () => { + commentList.comments = [testComment]; + fixture.detectChanges(); + let elements = fixture.nativeElement.querySelectorAll('#comment-user-icon'); + expect(elements.length).toBe(1); + expect(elements[0].innerText).toContain(commentList.getUserShortName(testComment.createdBy)); + expect(fixture.nativeElement.querySelector('#comment-user-icon:empty')).toBeNull(); + }); +}); diff --git a/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.ts b/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.ts new file mode 100644 index 0000000000..5b44ef5f1e --- /dev/null +++ b/ng2-components/ng2-activiti-tasklist/src/components/adf-comment-list.component.ts @@ -0,0 +1,80 @@ +/*! + * @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 { DatePipe } from '@angular/common'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Comment, User } from '../models/index'; + +@Component({ + selector: 'adf-comment-list', + templateUrl: './adf-comment-list.component.html', + styleUrls: ['./adf-comment-list.component.css'] +}) + +export class AdfCommentListComponent { + + @Input() + comments: Comment[]; + + @Output() + clickRow: EventEmitter = new EventEmitter(); + + selectedComment: Comment; + + constructor(private datePipe: DatePipe) { + } + + selectComment(event: any): void { + this.selectedComment = event.value.obj; + this.clickRow.emit(this.selectedComment); + } + + getUserShortName(user: User): string { + let shortName = ''; + if (user) { + if (user.firstName) { + shortName = user.firstName[0].toUpperCase(); + } + if (user.lastName) { + shortName += user.lastName[0].toUpperCase(); + } + } + return shortName; + } + + transformDate(aDate: string): string { + let formattedDate: string; + let givenDate = Number.parseInt(this.datePipe.transform(aDate, 'yMMdd')); + let today = Number.parseInt(this.datePipe.transform(Date.now(), 'yMMdd')); + if (givenDate === today) { + formattedDate = 'Today, ' + this.datePipe.transform(aDate, 'hh:mm a'); + }else { + let yesterday = Number.parseInt(this.datePipe.transform(Date.now() - 24 * 3600 * 1000, 'yMMdd')); + if (givenDate === yesterday) { + formattedDate = 'Yesterday, ' + this.datePipe.transform(aDate, 'hh:mm a'); + }else { + formattedDate = this.datePipe.transform(aDate, 'MMM dd y, hh:mm a'); + } + } + return formattedDate; + } + + hasComments(): boolean { + return this.comments && this.comments.length && true; + } + +} diff --git a/ng2-components/ng2-activiti-tasklist/src/components/index.ts b/ng2-components/ng2-activiti-tasklist/src/components/index.ts index 9429b9990f..140f54bfc5 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/index.ts +++ b/ng2-components/ng2-activiti-tasklist/src/components/index.ts @@ -29,3 +29,4 @@ export * from './activiti-start-task.component'; export * from './activiti-people-search.component'; export * from './adf-create-task-attachment.component'; export * from './adf-people-list.component'; +export * from './adf-comment-list.component'; diff --git a/ng2-components/ng2-activiti-tasklist/src/i18n/en.json b/ng2-components/ng2-activiti-tasklist/src/i18n/en.json index 11d5d39917..5a8f83c021 100644 --- a/ng2-components/ng2-activiti-tasklist/src/i18n/en.json +++ b/ng2-components/ng2-activiti-tasklist/src/i18n/en.json @@ -43,6 +43,7 @@ "COMMENTS": { "NONE": "No comments.", "ADD": "Add a comment", + "HEADER": "Comments", "DIALOG": { "TITLE": "New comment", "LABELS": {