diff --git a/demo-shell/resources/i18n/en.json b/demo-shell/resources/i18n/en.json index 7e4240ec39..72fd75c4fd 100644 --- a/demo-shell/resources/i18n/en.json +++ b/demo-shell/resources/i18n/en.json @@ -51,6 +51,9 @@ "TITLE": "Metadata" } }, + "COMMENTS": { + "ADD_COMMENT": "ADD" + }, "APP_LAYOUT": { "APP_NAME": "ADF Demo Application", "HOME": "Home", diff --git a/e2e/pages/adf/process_services/taskDetailsPage.js b/e2e/pages/adf/process_services/taskDetailsPage.js index 29528e2b74..fd573c07ce 100644 --- a/e2e/pages/adf/process_services/taskDetailsPage.js +++ b/e2e/pages/adf/process_services/taskDetailsPage.js @@ -30,7 +30,8 @@ var TaskDetailsPage = function () { var descriptionField = element(by.css("span[data-automation-id*='description'] span")); var dueDateField = element(by.css("span[data-automation-id*='dueDate'] span")); var activitiesTitle = element(by.css("div[class*='adf-info-drawer-layout-header-title'] div")); - var commentField = element(by.css("input[id='comment-input']")); + var commentField = element(by.id("comment-input")); + var addCommentButton = element(by.css("[data-automation-id='comments-input-add']")); var activityTab = element(by.cssContainingText("div[class*='mat-tab-label ']", "Activity")); var detailsTab = element(by.cssContainingText("div[class*='mat-tab-label ']", "Details")); var involvePeopleButton = element(by.css("div[class*='add-people']")); @@ -116,6 +117,12 @@ var TaskDetailsPage = function () { this.addComment = function (comment) { Util.waitUntilElementIsVisible(commentField); commentField.sendKeys(comment); + addCommentButton.click(); + return this; + }; + + this.clearComment = function (comment) { + Util.waitUntilElementIsVisible(commentField); commentField.sendKeys(protractor.Key.ENTER); return this; }; diff --git a/lib/core/comments/comment-list.component.html b/lib/core/comments/comment-list.component.html index 8f497a0039..734fa6ea4f 100644 --- a/lib/core/comments/comment-list.component.html +++ b/lib/core/comments/comment-list.component.html @@ -21,9 +21,7 @@
{{comment.createdBy?.firstName}} {{comment.createdBy?.lastName}}
-
- {{comment.message}} -
+
{{ comment.created | adfTimeAgo: currentLocale }}
diff --git a/lib/core/comments/comments.component.html b/lib/core/comments/comments.component.html index 70fc4f4863..5308eb3f13 100644 --- a/lib/core/comments/comments.component.html +++ b/lib/core/comments/comments.component.html @@ -4,12 +4,23 @@
- + + +
+ +
- + \ No newline at end of file diff --git a/lib/core/comments/comments.component.scss b/lib/core/comments/comments.component.scss index de8c836fc1..49041e8b1b 100644 --- a/lib/core/comments/comments.component.scss +++ b/lib/core/comments/comments.component.scss @@ -20,6 +20,16 @@ width: calc(100% - 30px); padding-top: 8px; border-bottom: $header-border; + + textarea { + resize: vertical; + } + } + + .adf-comments-input-actions { + display: flex; + justify-content: flex-end; + margin-bottom: 10px; } .adf-full-width { diff --git a/lib/core/comments/comments.component.spec.ts b/lib/core/comments/comments.component.spec.ts index 4fb5b43c33..1787e3c47b 100644 --- a/lib/core/comments/comments.component.spec.ts +++ b/lib/core/comments/comments.component.spec.ts @@ -219,11 +219,43 @@ describe('CommentsComponent', () => { fixture.whenStable(); })); - 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'); + it('should sanitize comment when user input contains html elements', async(() => { + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); + component.message = '
'; + element.dispatchEvent(new Event('click')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(addProcessCommentSpy).toHaveBeenCalledWith('123', 'action'); + }); + })); + + it('should normalize comment when user input contains spaces sequence', async(() => { + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); + component.message = 'test comment'; + element.dispatchEvent(new Event('click')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(addProcessCommentSpy).toHaveBeenCalledWith('123', 'test comment'); + }); + })); + + it('should add break lines to comment when user input contains new line characters', async(() => { + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); + component.message = 'these\nare\nparagraphs\n'; + element.dispatchEvent(new Event('click')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(addProcessCommentSpy).toHaveBeenCalledWith('123', 'these
are
paragraphs'); + }); + })); + + it('should call service to add a comment when add button is pressed', async(() => { + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); component.message = 'Test Comment'; - element.dispatchEvent(event); + element.dispatchEvent(new Event('click')); fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -235,10 +267,9 @@ describe('CommentsComponent', () => { })); 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'); + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); component.message = ''; - element.dispatchEvent(event); + element.dispatchEvent(new Event('click')); fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -249,7 +280,6 @@ describe('CommentsComponent', () => { 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'; element.dispatchEvent(event); fixture.detectChanges(); fixture.whenStable().then(() => { @@ -277,11 +307,10 @@ describe('CommentsComponent', () => { fixture.whenStable(); })); - 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'); + it('should call service to add a comment when add button is pressed', async(() => { + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); component.message = 'Test Comment'; - element.dispatchEvent(event); + element.dispatchEvent(new Event('click')); fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -292,11 +321,43 @@ describe('CommentsComponent', () => { }); })); + it('should sanitize comment when user input contains html elements', async(() => { + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); + component.message = '
'; + element.dispatchEvent(new Event('click')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(addContentCommentSpy).toHaveBeenCalledWith('123', 'action'); + }); + })); + + it('should normalize comment when user input contains spaces sequence', async(() => { + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); + component.message = 'test comment'; + element.dispatchEvent(new Event('click')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(addContentCommentSpy).toHaveBeenCalledWith('123', 'test comment'); + }); + })); + + it('should add break lines to comment when user input contains new line characters', async(() => { + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); + component.message = 'these\nare\nparagraphs\n'; + element.dispatchEvent(new Event('click')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(addContentCommentSpy).toHaveBeenCalledWith('123', 'these
are
paragraphs'); + }); + })); + 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'); + let element = fixture.nativeElement.querySelector('.adf-comments-input-add'); component.message = ''; - element.dispatchEvent(event); + element.dispatchEvent(new Event('click')); fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -307,7 +368,6 @@ describe('CommentsComponent', () => { 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'; element.dispatchEvent(event); fixture.detectChanges(); fixture.whenStable().then(() => { diff --git a/lib/core/comments/comments.component.ts b/lib/core/comments/comments.component.ts index b58fc62bde..5e56f6d341 100644 --- a/lib/core/comments/comments.component.ts +++ b/lib/core/comments/comments.component.ts @@ -126,9 +126,11 @@ export class CommentsComponent implements OnChanges { add(): void { if (this.message && this.message.trim() && !this.beingAdded) { + const comment = this.sanitize(this.message); + this.beingAdded = true; if (this.isATask()) { - this.commentProcessService.addTaskComment(this.taskId, this.message) + this.commentProcessService.addTaskComment(this.taskId, comment) .subscribe( (res: CommentModel) => { this.comments.unshift(res); @@ -144,7 +146,7 @@ export class CommentsComponent implements OnChanges { } if (this.isANode()) { - this.commentContentService.addNodeComment(this.nodeId, this.message) + this.commentContentService.addNodeComment(this.nodeId, comment) .subscribe( (res: CommentModel) => { this.comments.unshift(res); @@ -176,4 +178,10 @@ export class CommentsComponent implements OnChanges { isANode(): boolean { return this.nodeId ? true : false; } + + private sanitize(input: string) { + return input.replace(/<[^>]+>/g, '') + .replace(/^\s+|\s+$|\s+(?=\s)/g, '') + .replace(/\r?\n/g, '
'); + } }