mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[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:
parent
61dff96e8b
commit
f36f9fa862
@ -51,6 +51,9 @@
|
|||||||
"TITLE": "Metadata"
|
"TITLE": "Metadata"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"COMMENTS": {
|
||||||
|
"ADD_COMMENT": "ADD"
|
||||||
|
},
|
||||||
"APP_LAYOUT": {
|
"APP_LAYOUT": {
|
||||||
"APP_NAME": "ADF Demo Application",
|
"APP_NAME": "ADF Demo Application",
|
||||||
"HOME": "Home",
|
"HOME": "Home",
|
||||||
|
@ -30,7 +30,8 @@ var TaskDetailsPage = function () {
|
|||||||
var descriptionField = element(by.css("span[data-automation-id*='description'] span"));
|
var descriptionField = element(by.css("span[data-automation-id*='description'] span"));
|
||||||
var dueDateField = element(by.css("span[data-automation-id*='dueDate'] 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 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 activityTab = element(by.cssContainingText("div[class*='mat-tab-label ']", "Activity"));
|
||||||
var detailsTab = element(by.cssContainingText("div[class*='mat-tab-label ']", "Details"));
|
var detailsTab = element(by.cssContainingText("div[class*='mat-tab-label ']", "Details"));
|
||||||
var involvePeopleButton = element(by.css("div[class*='add-people']"));
|
var involvePeopleButton = element(by.css("div[class*='add-people']"));
|
||||||
@ -116,6 +117,12 @@ var TaskDetailsPage = function () {
|
|||||||
this.addComment = function (comment) {
|
this.addComment = function (comment) {
|
||||||
Util.waitUntilElementIsVisible(commentField);
|
Util.waitUntilElementIsVisible(commentField);
|
||||||
commentField.sendKeys(comment);
|
commentField.sendKeys(comment);
|
||||||
|
addCommentButton.click();
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clearComment = function (comment) {
|
||||||
|
Util.waitUntilElementIsVisible(commentField);
|
||||||
commentField.sendKeys(protractor.Key.ENTER);
|
commentField.sendKeys(protractor.Key.ENTER);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
@ -21,9 +21,7 @@
|
|||||||
<div matLine id="comment-user" class="adf-comment-user-name">
|
<div matLine id="comment-user" class="adf-comment-user-name">
|
||||||
{{comment.createdBy?.firstName}} {{comment.createdBy?.lastName}}
|
{{comment.createdBy?.firstName}} {{comment.createdBy?.lastName}}
|
||||||
</div>
|
</div>
|
||||||
<div matLine id="comment-message" class="adf-comment-message">
|
<div matLine id="comment-message" class="adf-comment-message" [innerHTML]="comment.message"></div>
|
||||||
{{comment.message}}
|
|
||||||
</div>
|
|
||||||
<div matLine id="comment-time" class="adf-comment-message-time">
|
<div matLine id="comment-time" class="adf-comment-message-time">
|
||||||
{{ comment.created | adfTimeAgo: currentLocale }}
|
{{ comment.created | adfTimeAgo: currentLocale }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,8 +4,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="adf-comments-input-container" *ngIf="!isReadOnly()">
|
<div class="adf-comments-input-container" *ngIf="!isReadOnly()">
|
||||||
<mat-form-field class="adf-full-width">
|
<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>
|
</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>
|
||||||
|
|
||||||
<div *ngIf="comments.length > 0">
|
<div *ngIf="comments.length > 0">
|
||||||
|
@ -20,6 +20,16 @@
|
|||||||
width: calc(100% - 30px);
|
width: calc(100% - 30px);
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
border-bottom: $header-border;
|
border-bottom: $header-border;
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-comments-input-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.adf-full-width {
|
.adf-full-width {
|
||||||
|
@ -219,11 +219,43 @@ describe('CommentsComponent', () => {
|
|||||||
fixture.whenStable();
|
fixture.whenStable();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should call service to add a comment when enter key is pressed', async(() => {
|
it('should sanitize comment when user input contains html elements', async(() => {
|
||||||
let event = new KeyboardEvent('keyup', {'key': 'Enter'});
|
let element = fixture.nativeElement.querySelector('.adf-comments-input-add');
|
||||||
let element = fixture.nativeElement.querySelector('#comment-input');
|
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';
|
component.message = 'Test Comment';
|
||||||
element.dispatchEvent(event);
|
element.dispatchEvent(new Event('click'));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -235,10 +267,9 @@ describe('CommentsComponent', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should not call service to add a comment when comment is empty', async(() => {
|
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('.adf-comments-input-add');
|
||||||
let element = fixture.nativeElement.querySelector('#comment-input');
|
|
||||||
component.message = '';
|
component.message = '';
|
||||||
element.dispatchEvent(event);
|
element.dispatchEvent(new Event('click'));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -249,7 +280,6 @@ describe('CommentsComponent', () => {
|
|||||||
it('should clear comment when escape key is pressed', async(() => {
|
it('should clear comment when escape key is pressed', async(() => {
|
||||||
let event = new KeyboardEvent('keyup', {'key': 'Escape'});
|
let event = new KeyboardEvent('keyup', {'key': 'Escape'});
|
||||||
let element = fixture.nativeElement.querySelector('#comment-input');
|
let element = fixture.nativeElement.querySelector('#comment-input');
|
||||||
component.message = 'Test comment';
|
|
||||||
element.dispatchEvent(event);
|
element.dispatchEvent(event);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
@ -277,11 +307,10 @@ describe('CommentsComponent', () => {
|
|||||||
fixture.whenStable();
|
fixture.whenStable();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should call service to add a comment when enter key is pressed', async(() => {
|
it('should call service to add a comment when add button is pressed', async(() => {
|
||||||
let event = new KeyboardEvent('keyup', {'key': 'Enter'});
|
let element = fixture.nativeElement.querySelector('.adf-comments-input-add');
|
||||||
let element = fixture.nativeElement.querySelector('#comment-input');
|
|
||||||
component.message = 'Test Comment';
|
component.message = 'Test Comment';
|
||||||
element.dispatchEvent(event);
|
element.dispatchEvent(new Event('click'));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
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(() => {
|
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('.adf-comments-input-add');
|
||||||
let element = fixture.nativeElement.querySelector('#comment-input');
|
|
||||||
component.message = '';
|
component.message = '';
|
||||||
element.dispatchEvent(event);
|
element.dispatchEvent(new Event('click'));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -307,7 +368,6 @@ describe('CommentsComponent', () => {
|
|||||||
it('should clear comment when escape key is pressed', async(() => {
|
it('should clear comment when escape key is pressed', async(() => {
|
||||||
let event = new KeyboardEvent('keyup', {'key': 'Escape'});
|
let event = new KeyboardEvent('keyup', {'key': 'Escape'});
|
||||||
let element = fixture.nativeElement.querySelector('#comment-input');
|
let element = fixture.nativeElement.querySelector('#comment-input');
|
||||||
component.message = 'Test comment';
|
|
||||||
element.dispatchEvent(event);
|
element.dispatchEvent(event);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
|
@ -126,9 +126,11 @@ export class CommentsComponent implements OnChanges {
|
|||||||
|
|
||||||
add(): void {
|
add(): void {
|
||||||
if (this.message && this.message.trim() && !this.beingAdded) {
|
if (this.message && this.message.trim() && !this.beingAdded) {
|
||||||
|
const comment = this.sanitize(this.message);
|
||||||
|
|
||||||
this.beingAdded = true;
|
this.beingAdded = true;
|
||||||
if (this.isATask()) {
|
if (this.isATask()) {
|
||||||
this.commentProcessService.addTaskComment(this.taskId, this.message)
|
this.commentProcessService.addTaskComment(this.taskId, comment)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(res: CommentModel) => {
|
(res: CommentModel) => {
|
||||||
this.comments.unshift(res);
|
this.comments.unshift(res);
|
||||||
@ -144,7 +146,7 @@ export class CommentsComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.isANode()) {
|
if (this.isANode()) {
|
||||||
this.commentContentService.addNodeComment(this.nodeId, this.message)
|
this.commentContentService.addNodeComment(this.nodeId, comment)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(res: CommentModel) => {
|
(res: CommentModel) => {
|
||||||
this.comments.unshift(res);
|
this.comments.unshift(res);
|
||||||
@ -176,4 +178,10 @@ export class CommentsComponent implements OnChanges {
|
|||||||
isANode(): boolean {
|
isANode(): boolean {
|
||||||
return this.nodeId ? true : false;
|
return this.nodeId ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sanitize(input: string) {
|
||||||
|
return input.replace(/<[^>]+>/g, '')
|
||||||
|
.replace(/^\s+|\s+$|\s+(?=\s)/g, '')
|
||||||
|
.replace(/\r?\n/g, '<br/>');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user