[ADF-3374] Comments - add/view multiline comments (#3607)

* multiline comment

* add comment e2e

* fix locator syntax

* lint

* clear textarea on ESCAPE event

* multiline comment

* add comment e2e

* fix locator syntax

* lint

* clear textarea on ESCAPE event
This commit is contained in:
Cilibiu Bogdan 2018-07-27 22:24:29 +03:00 committed by Eugenio Romano
parent 61dff96e8b
commit f36f9fa862
7 changed files with 121 additions and 24 deletions

View File

@ -51,6 +51,9 @@
"TITLE": "Metadata"
}
},
"COMMENTS": {
"ADD_COMMENT": "ADD"
},
"APP_LAYOUT": {
"APP_NAME": "ADF Demo Application",
"HOME": "Home",

View File

@ -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;
};

View File

@ -21,9 +21,7 @@
<div matLine id="comment-user" class="adf-comment-user-name">
{{comment.createdBy?.firstName}} {{comment.createdBy?.lastName}}
</div>
<div matLine id="comment-message" class="adf-comment-message">
{{comment.message}}
</div>
<div matLine id="comment-message" class="adf-comment-message" [innerHTML]="comment.message"></div>
<div matLine id="comment-time" class="adf-comment-message-time">
{{ comment.created | adfTimeAgo: currentLocale }}
</div>

View File

@ -4,8 +4,19 @@
</div>
<div class="adf-comments-input-container" *ngIf="!isReadOnly()">
<mat-form-field class="adf-full-width">
<input matInput id="comment-input" placeholder="{{'COMMENTS.ADD' | translate}}" [(ngModel)]="message" (keyup.enter)="add()" (keyup.esc)="clear()">
<textarea (keyup.escape)="clear()" matInput id="comment-input" placeholder="{{'COMMENTS.ADD' | translate}}" [(ngModel)]="message"></textarea>
</mat-form-field>
<div class="adf-comments-input-actions">
<button mat-button
class="adf-comments-input-add"
data-automation-id="comments-input-add"
color="primary"
(click)="add()"
[disabled]="!message">
{{ 'COMMENTS.ADD_COMMENT' | translate }}
</button>
</div>
</div>
<div *ngIf="comments.length > 0">

View File

@ -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 {

View File

@ -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 = '<div class="text-class"><button onclick=""><h1>action</h1></button></div>';
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<br/>are<br/>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 = '<div class="text-class"><button onclick=""><h1>action</h1></button></div>';
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<br/>are<br/>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(() => {

View File

@ -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, '<br/>');
}
}