[ACS-4749] add button is enabled for comment contains only spaces (#10577)

* [ACS-4749] Disabled add button for comment when comment has only spaces

* [ACS-4749] Unit tests

* [ACS-4749] Removed redundant code

* [ACS-4749] Fixed sonar issue

* [ACS-4749] Resolved issues after rebase

* [ACS-4749] Addressed PR comment
This commit is contained in:
AleksanderSklorz 2025-01-28 13:49:58 +01:00 committed by GitHub
parent e6278109d8
commit 1634716bbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 131 additions and 29 deletions

View File

@ -8,22 +8,22 @@
matInput
id="comment-input"
class="adf-text-text-area"
[placeholder]='"COMMENTS.ADD" | translate'
[placeholder]='("COMMENTS.ADD" | translate) + "*"'
[attr.aria-label]="'COMMENTS.ADD' | translate"
[(ngModel)]="message"
[formControl]="commentControl"
(keydown.escape)="clearMessage($event)"
>
</textarea>
<mat-error *ngIf="commentControl.invalid && commentControl.touched">{{ 'COMMENTS.EMPTY_ERROR' | translate }}</mat-error>
</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)="addComment()"
[disabled]="!message"
[disabled]="commentControl.invalid"
>
{{ 'COMMENTS.ADD' | translate }}
</button>

View File

@ -12,10 +12,15 @@ adf-comments {
#{$mat-form-field} {
width: 100%;
}
#{$mat-form-field-subscript-wrapper} {
display: none;
&#{$mat-form-field-invalid} {
#{$mat-input-element} {
&::placeholder,
&:focus::placeholder {
color: var(--theme-warn-color);
}
}
}
}
#{$mat-form-field-wrapper} {

View File

@ -25,6 +25,7 @@ import { CommentsService } from './interfaces/comments-service.interface';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { NoopTranslateModule } from '../testing/noop-translate.module';
import { UnitTestingUtils } from '../testing/unit-testing-utils';
import { MatError } from '@angular/material/form-field';
describe('CommentsComponent', () => {
let component: CommentsComponent;
@ -156,6 +157,10 @@ describe('CommentsComponent', () => {
});
describe('Add comment', () => {
const getError = (): string => testingUtils.getByDirective(MatError)?.nativeElement?.textContent;
const getAddCommentButton = (): HTMLButtonElement => testingUtils.getByDataAutomationId('comments-input-add').nativeElement;
beforeEach(() => {
component.id = '123';
fixture.detectChanges();
@ -163,8 +168,8 @@ describe('CommentsComponent', () => {
});
it('should normalize comment when user input contains spaces sequence', async () => {
component.message = 'test comment';
testingUtils.clickByCSS('.adf-comments-input-add');
component.commentControl.setValue('test comment');
getAddCommentButton().dispatchEvent(new Event('click'));
fixture.detectChanges();
await fixture.whenStable();
@ -184,8 +189,8 @@ describe('CommentsComponent', () => {
getCommentSpy.and.returnValue(of([]));
addCommentSpy.and.returnValue(commentsResponseMock.addComment(commentText));
component.message = commentText;
testingUtils.clickByCSS('.adf-comments-input-add');
component.commentControl.setValue(commentText);
getAddCommentButton().dispatchEvent(new Event('click'));
fixture.detectChanges();
await fixture.whenStable();
@ -195,9 +200,10 @@ describe('CommentsComponent', () => {
});
it('should call service to add a comment when add button is pressed', async () => {
component.message = 'Test Comment';
addCommentSpy.and.returnValue(commentsResponseMock.addComment(component.message));
testingUtils.clickByCSS('.adf-comments-input-add');
const comment = 'Test Comment';
component.commentControl.setValue(comment);
addCommentSpy.and.returnValue(commentsResponseMock.addComment(comment));
getAddCommentButton().dispatchEvent(new Event('click'));
fixture.detectChanges();
await fixture.whenStable();
@ -209,8 +215,8 @@ describe('CommentsComponent', () => {
});
it('should not call service to add a comment when comment is empty', async () => {
component.message = '';
testingUtils.clickByCSS('.adf-comments-input-add');
component.commentControl.setValue('');
getAddCommentButton().dispatchEvent(new Event('click'));
fixture.detectChanges();
await fixture.whenStable();
@ -231,7 +237,7 @@ describe('CommentsComponent', () => {
it('should emit an error when an error occurs adding the comment', () => {
const emitSpy = spyOn(component.error, 'emit');
addCommentSpy.and.returnValue(throwError(() => new Error('error')));
component.message = 'Test comment';
component.commentControl.setValue('Test comment');
component.addComment();
expect(emitSpy).toHaveBeenCalled();
});
@ -255,9 +261,85 @@ describe('CommentsComponent', () => {
});
it('should not add comment if message is empty', () => {
component.message = '';
component.commentControl.setValue('');
component.addComment();
expect(addCommentSpy).not.toHaveBeenCalled();
});
it('should not display error message initially', () => {
expect(getError()).toBeUndefined();
});
it('should display error message when comment is empty and was touched', () => {
component.commentControl.setValue('');
component.commentControl.markAsTouched();
fixture.detectChanges();
expect(getError()).toBe('COMMENTS.EMPTY_ERROR');
});
it('should display error message when comment has only spaces and was touched', () => {
component.commentControl.setValue(' ');
component.commentControl.markAsTouched();
fixture.detectChanges();
expect(getError()).toBe('COMMENTS.EMPTY_ERROR');
});
it('should not display error message when comment is empty but was not touched', () => {
component.commentControl.setValue('');
fixture.detectChanges();
expect(getError()).toBeUndefined();
});
it('should not display error message when comment has only spaces but was not touched', () => {
component.commentControl.setValue(' ');
fixture.detectChanges();
expect(getError()).toBeUndefined();
});
it('should not display error message when comment is not empty and was touched', () => {
component.commentControl.setValue('Some comment');
component.commentControl.markAsTouched();
fixture.detectChanges();
expect(getError()).toBeUndefined();
});
it('should not display error message when comment is not empty and was not touched', () => {
component.commentControl.setValue('Some comment');
fixture.detectChanges();
expect(getError()).toBeUndefined();
});
it('should disable add button initially', () => {
expect(getAddCommentButton().disabled).toBeTrue();
});
it('should disable add button when comment is empty', () => {
component.commentControl.setValue('Some comment');
component.commentControl.setValue('');
fixture.detectChanges();
expect(getAddCommentButton().disabled).toBeTrue();
});
it('should disable add button when comment has only spaces', () => {
component.commentControl.setValue('Some comment');
component.commentControl.setValue(' ');
fixture.detectChanges();
expect(getAddCommentButton().disabled).toBeTrue();
});
it('should enable add button when comment is not empty', () => {
component.commentControl.setValue('Some comment');
fixture.detectChanges();
expect(getAddCommentButton().disabled).toBeFalse();
});
});
});

View File

@ -23,14 +23,23 @@ import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { FormsModule } from '@angular/forms';
import { FormControl, FormsModule, ReactiveFormsModule, ValidationErrors } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { CommentListComponent } from './comment-list';
@Component({
selector: 'adf-comments',
standalone: true,
imports: [CommonModule, TranslateModule, MatFormFieldModule, MatInputModule, FormsModule, MatButtonModule, CommentListComponent],
imports: [
CommonModule,
TranslateModule,
MatFormFieldModule,
MatInputModule,
FormsModule,
MatButtonModule,
CommentListComponent,
ReactiveFormsModule
],
templateUrl: './comments.component.html',
styleUrls: ['./comments.component.scss'],
encapsulation: ViewEncapsulation.None
@ -49,11 +58,16 @@ export class CommentsComponent implements OnChanges {
error = new EventEmitter<any>();
comments: CommentModel[] = [];
message: string;
beingAdded: boolean = false;
private commentsService = inject<CommentsService>(ADF_COMMENTS_SERVICE);
private readonly _commentControl = new FormControl('', [this.validateEmptyComment]);
get commentControl(): FormControl<string> {
return this._commentControl;
}
ngOnChanges(changes: SimpleChanges): void {
this.id = null;
@ -95,10 +109,10 @@ export class CommentsComponent implements OnChanges {
this.beingAdded = true;
this.commentsService.add(this.id, this.message).subscribe({
this.commentsService.add(this.id, this.commentControl.value).subscribe({
next: (res) => {
this.addToComments(res);
this.resetMessage();
this.commentControl.reset();
},
error: (err) => {
this.error.emit(err);
@ -111,19 +125,15 @@ export class CommentsComponent implements OnChanges {
clearMessage(event: Event): void {
event.stopPropagation();
this.resetMessage();
this.commentControl.reset();
}
private addToComments(comment: CommentModel): void {
this.comments.unshift(comment);
}
private resetMessage(): void {
this.message = '';
}
private canAddComment(): boolean {
return this.hasId() && this.message && this.message.trim() && !this.beingAdded;
return this.hasId() && this.commentControl.value?.trim() && !this.beingAdded;
}
private hasId(): boolean {
@ -146,4 +156,8 @@ export class CommentsComponent implements OnChanges {
private resetComments(): void {
this.comments = [];
}
private validateEmptyComment(commentControl: FormControl<string>): ValidationErrors {
return commentControl.value?.trim() ? null : { emptyComment: true };
}
}

View File

@ -306,6 +306,7 @@
"HEADER": "Comments ({{ count }})",
"CREATED_BY_HEADER": "Created by",
"MESSAGE_HEADER": "Message",
"EMPTY_ERROR": "Comment can't be empty",
"DIALOG": {
"TITLE": "New comment",
"LABELS": {