[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 matInput
id="comment-input" id="comment-input"
class="adf-text-text-area" class="adf-text-text-area"
[placeholder]='"COMMENTS.ADD" | translate' [placeholder]='("COMMENTS.ADD" | translate) + "*"'
[attr.aria-label]="'COMMENTS.ADD' | translate" [attr.aria-label]="'COMMENTS.ADD' | translate"
[(ngModel)]="message" [formControl]="commentControl"
(keydown.escape)="clearMessage($event)" (keydown.escape)="clearMessage($event)"
> >
</textarea> </textarea>
<mat-error *ngIf="commentControl.invalid && commentControl.touched">{{ 'COMMENTS.EMPTY_ERROR' | translate }}</mat-error>
</mat-form-field> </mat-form-field>
<div class="adf-comments-input-actions"> <div class="adf-comments-input-actions">
<button <button
mat-button mat-button
class="adf-comments-input-add"
data-automation-id="comments-input-add" data-automation-id="comments-input-add"
color="primary" color="primary"
(click)="addComment()" (click)="addComment()"
[disabled]="!message" [disabled]="commentControl.invalid"
> >
{{ 'COMMENTS.ADD' | translate }} {{ 'COMMENTS.ADD' | translate }}
</button> </button>

View File

@ -12,10 +12,15 @@ adf-comments {
#{$mat-form-field} { #{$mat-form-field} {
width: 100%; width: 100%;
}
#{$mat-form-field-subscript-wrapper} { &#{$mat-form-field-invalid} {
display: none; #{$mat-input-element} {
&::placeholder,
&:focus::placeholder {
color: var(--theme-warn-color);
}
}
}
} }
#{$mat-form-field-wrapper} { #{$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 { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { NoopTranslateModule } from '../testing/noop-translate.module'; import { NoopTranslateModule } from '../testing/noop-translate.module';
import { UnitTestingUtils } from '../testing/unit-testing-utils'; import { UnitTestingUtils } from '../testing/unit-testing-utils';
import { MatError } from '@angular/material/form-field';
describe('CommentsComponent', () => { describe('CommentsComponent', () => {
let component: CommentsComponent; let component: CommentsComponent;
@ -156,6 +157,10 @@ describe('CommentsComponent', () => {
}); });
describe('Add comment', () => { describe('Add comment', () => {
const getError = (): string => testingUtils.getByDirective(MatError)?.nativeElement?.textContent;
const getAddCommentButton = (): HTMLButtonElement => testingUtils.getByDataAutomationId('comments-input-add').nativeElement;
beforeEach(() => { beforeEach(() => {
component.id = '123'; component.id = '123';
fixture.detectChanges(); fixture.detectChanges();
@ -163,8 +168,8 @@ describe('CommentsComponent', () => {
}); });
it('should normalize comment when user input contains spaces sequence', async () => { it('should normalize comment when user input contains spaces sequence', async () => {
component.message = 'test comment'; component.commentControl.setValue('test comment');
testingUtils.clickByCSS('.adf-comments-input-add'); getAddCommentButton().dispatchEvent(new Event('click'));
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
@ -184,8 +189,8 @@ describe('CommentsComponent', () => {
getCommentSpy.and.returnValue(of([])); getCommentSpy.and.returnValue(of([]));
addCommentSpy.and.returnValue(commentsResponseMock.addComment(commentText)); addCommentSpy.and.returnValue(commentsResponseMock.addComment(commentText));
component.message = commentText; component.commentControl.setValue(commentText);
testingUtils.clickByCSS('.adf-comments-input-add'); getAddCommentButton().dispatchEvent(new Event('click'));
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
@ -195,9 +200,10 @@ describe('CommentsComponent', () => {
}); });
it('should call service to add a comment when add button is pressed', async () => { it('should call service to add a comment when add button is pressed', async () => {
component.message = 'Test Comment'; const comment = 'Test Comment';
addCommentSpy.and.returnValue(commentsResponseMock.addComment(component.message)); component.commentControl.setValue(comment);
testingUtils.clickByCSS('.adf-comments-input-add'); addCommentSpy.and.returnValue(commentsResponseMock.addComment(comment));
getAddCommentButton().dispatchEvent(new Event('click'));
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
@ -209,8 +215,8 @@ 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 () => {
component.message = ''; component.commentControl.setValue('');
testingUtils.clickByCSS('.adf-comments-input-add'); getAddCommentButton().dispatchEvent(new Event('click'));
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
@ -231,7 +237,7 @@ describe('CommentsComponent', () => {
it('should emit an error when an error occurs adding the comment', () => { it('should emit an error when an error occurs adding the comment', () => {
const emitSpy = spyOn(component.error, 'emit'); const emitSpy = spyOn(component.error, 'emit');
addCommentSpy.and.returnValue(throwError(() => new Error('error'))); addCommentSpy.and.returnValue(throwError(() => new Error('error')));
component.message = 'Test comment'; component.commentControl.setValue('Test comment');
component.addComment(); component.addComment();
expect(emitSpy).toHaveBeenCalled(); expect(emitSpy).toHaveBeenCalled();
}); });
@ -255,9 +261,85 @@ describe('CommentsComponent', () => {
}); });
it('should not add comment if message is empty', () => { it('should not add comment if message is empty', () => {
component.message = ''; component.commentControl.setValue('');
component.addComment(); component.addComment();
expect(addCommentSpy).not.toHaveBeenCalled(); 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 { TranslateModule } from '@ngx-translate/core';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; 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 { MatButtonModule } from '@angular/material/button';
import { CommentListComponent } from './comment-list'; import { CommentListComponent } from './comment-list';
@Component({ @Component({
selector: 'adf-comments', selector: 'adf-comments',
standalone: true, standalone: true,
imports: [CommonModule, TranslateModule, MatFormFieldModule, MatInputModule, FormsModule, MatButtonModule, CommentListComponent], imports: [
CommonModule,
TranslateModule,
MatFormFieldModule,
MatInputModule,
FormsModule,
MatButtonModule,
CommentListComponent,
ReactiveFormsModule
],
templateUrl: './comments.component.html', templateUrl: './comments.component.html',
styleUrls: ['./comments.component.scss'], styleUrls: ['./comments.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
@ -49,11 +58,16 @@ export class CommentsComponent implements OnChanges {
error = new EventEmitter<any>(); error = new EventEmitter<any>();
comments: CommentModel[] = []; comments: CommentModel[] = [];
message: string;
beingAdded: boolean = false; beingAdded: boolean = false;
private commentsService = inject<CommentsService>(ADF_COMMENTS_SERVICE); 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 { ngOnChanges(changes: SimpleChanges): void {
this.id = null; this.id = null;
@ -95,10 +109,10 @@ export class CommentsComponent implements OnChanges {
this.beingAdded = true; this.beingAdded = true;
this.commentsService.add(this.id, this.message).subscribe({ this.commentsService.add(this.id, this.commentControl.value).subscribe({
next: (res) => { next: (res) => {
this.addToComments(res); this.addToComments(res);
this.resetMessage(); this.commentControl.reset();
}, },
error: (err) => { error: (err) => {
this.error.emit(err); this.error.emit(err);
@ -111,19 +125,15 @@ export class CommentsComponent implements OnChanges {
clearMessage(event: Event): void { clearMessage(event: Event): void {
event.stopPropagation(); event.stopPropagation();
this.resetMessage(); this.commentControl.reset();
} }
private addToComments(comment: CommentModel): void { private addToComments(comment: CommentModel): void {
this.comments.unshift(comment); this.comments.unshift(comment);
} }
private resetMessage(): void {
this.message = '';
}
private canAddComment(): boolean { 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 { private hasId(): boolean {
@ -146,4 +156,8 @@ export class CommentsComponent implements OnChanges {
private resetComments(): void { private resetComments(): void {
this.comments = []; this.comments = [];
} }
private validateEmptyComment(commentControl: FormControl<string>): ValidationErrors {
return commentControl.value?.trim() ? null : { emptyComment: true };
}
} }

View File

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