Skip to content

Commit

Permalink
frontend: customizable ConfirmDialog
Browse files Browse the repository at this point in the history
  • Loading branch information
clean-coder committed Nov 13, 2024
1 parent 53376a5 commit 30b85e4
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 8 deletions.
105 changes: 104 additions & 1 deletion frontend/src/app/services/dialog.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { TestBed } from '@angular/core/testing';

import { DialogService } from './dialog.service';
import { ConfirmDialogData, DialogService } from './dialog.service';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { ConfirmDialogComponent } from '../shared/dialog/confirm-dialog/confirm-dialog.component';
import { AddEditTeamDialog } from '../team-management/add-edit-team-dialog/add-edit-team-dialog.component';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { provideHttpClient } from '@angular/common/http';
import { ButtonState } from '../shared/types/enums/ButtonState';

describe('DialogService', () => {
let service: DialogService;
Expand All @@ -24,6 +25,34 @@ describe('DialogService', () => {
jest.spyOn(matDialogSpy, 'open');
});

function expectData(current: ConfirmDialogData, expected: ConfirmDialogData) {
expect(current.title).toBe(expected.title);
expect(current.text).toBe(expected.text);
expect(current.yesButtonState).toBe(expected.yesButtonState);
expect(current.noButtonState).toBe(expected.noButtonState);
expect(current.closeButtonState).toBe(expected.closeButtonState);
}

function expectYesButtonIsVisibleAndEnabled(dialog: ConfirmDialogComponent) {
expect(dialog.isYesButtonVisible()).toBe(true);
expect(dialog.isYesButtonDisabled()).toBe(false);
}

function expectNoButtonIsVisibleAndEnabled(dialog: ConfirmDialogComponent) {
expect(dialog.isNoButtonVisible()).toBe(true);
expect(dialog.isNoButtonDisabled()).toBe(false);
}

function expectCloseButtonIsHiddenAndEnabled(dialog: ConfirmDialogComponent) {
expect(dialog.isCloseButtonVisible()).toBe(false);
expect(dialog.isCloseButtonDisabled()).toBe(false);
}

function expectNoButtonIsVisibleAndDisabled(dialog: ConfirmDialogComponent) {
expect(dialog.isNoButtonVisible()).toBe(true);
expect(dialog.isNoButtonDisabled()).toBe(true);
}

it('should be created', () => {
expect(service).toBeTruthy();
});
Expand Down Expand Up @@ -76,4 +105,78 @@ describe('DialogService', () => {
},
});
});

it('should open customized confirm dialog with default visibility properties', () => {
// arrange
const data: ConfirmDialogData = {
title: 'Test title',
text: 'Test description',
yesButtonState: undefined,
noButtonState: undefined,
closeButtonState: undefined,
};

jest.spyOn(service, 'open');
jest.spyOn(translateServiceSpy, 'instant');

// act
const dialog = service.openCustomizedConfirmDialog(data);
dialog.componentInstance.ngOnInit(); // trigger ngOnInit() manually in the test

// assert
expect(service.open).toHaveBeenCalledTimes(1);
expect(dialog).toBeInstanceOf(MatDialogRef);
expect(matDialogSpy.open).toHaveBeenCalledTimes(1);

let confirmDialogInstance = dialog.componentInstance;
expect(confirmDialogInstance).toBeInstanceOf(ConfirmDialogComponent);
expectData(confirmDialogInstance.data, data);

expect(matDialogSpy.open).toHaveBeenCalledWith(ConfirmDialogComponent, {
panelClass: service.DIALOG_PANEL_CLASS_SMALL,
...service.DIALOG_CONFIG,
data,
});

expectYesButtonIsVisibleAndEnabled(confirmDialogInstance);
expectNoButtonIsVisibleAndEnabled(confirmDialogInstance);
expectCloseButtonIsHiddenAndEnabled(confirmDialogInstance);
});

it('should open customized confirm dialog with explicit visibility properties', () => {
// arrange
const data: ConfirmDialogData = {
title: 'Test title',
text: 'Test description',
yesButtonState: ButtonState.Visible_Enabled,
noButtonState: ButtonState.Visible_Disabled,
closeButtonState: ButtonState.Hidden,
};

jest.spyOn(service, 'open');
jest.spyOn(translateServiceSpy, 'instant');

// act
const dialog = service.openCustomizedConfirmDialog(data);
dialog.componentInstance.ngOnInit(); // trigger ngOnInit() manually in the test

// assert
expect(service.open).toHaveBeenCalledTimes(1);
expect(dialog).toBeInstanceOf(MatDialogRef);
expect(matDialogSpy.open).toHaveBeenCalledTimes(1);

let confirmDialogInstance = dialog.componentInstance;
expect(confirmDialogInstance).toBeInstanceOf(ConfirmDialogComponent);
expectData(confirmDialogInstance.data, data);

expect(matDialogSpy.open).toHaveBeenCalledWith(ConfirmDialogComponent, {
panelClass: service.DIALOG_PANEL_CLASS_SMALL,
...service.DIALOG_CONFIG,
data,
});

expectYesButtonIsVisibleAndEnabled(confirmDialogInstance);
expectNoButtonIsVisibleAndDisabled(confirmDialogInstance);
expectCloseButtonIsHiddenAndEnabled(confirmDialogInstance);
});
});
17 changes: 17 additions & 0 deletions frontend/src/app/services/dialog.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material/dial
import { ComponentType } from '@angular/cdk/overlay';
import { ConfirmDialogComponent } from '../shared/dialog/confirm-dialog/confirm-dialog.component';
import { TranslateService } from '@ngx-translate/core';
import { ButtonState } from '../shared/types/enums/ButtonState';

export interface ConfirmDialogData {
title: string;
text: string;
yesButtonState?: ButtonState;
noButtonState?: ButtonState;
closeButtonState?: ButtonState;
}

@Injectable({
Expand Down Expand Up @@ -46,4 +50,17 @@ export class DialogService {
},
});
}

openCustomizedConfirmDialog(data: ConfirmDialogData): MatDialogRef<ConfirmDialogComponent> {
return this.open(ConfirmDialogComponent, {
panelClass: this.DIALOG_PANEL_CLASS_SMALL,
data: {
title: data.title,
text: data.text,
yesButtonState: data.yesButtonState,
noButtonState: data.noButtonState,
closeButtonState: data.closeButtonState,
},
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,37 @@
<ng-container actions>
<div class="col-auto">
<button
*ngIf="this.isYesButtonVisible()"
[disabled]="this.isYesButtonDisabled()"
[attr.data-testId]="'confirm-yes'"
type="submit"
(click)="closeAndDelete()"
(click)="this.closeAndDelete()"
color="primary"
mat-flat-button
>
Ja
</button>

<button mat-button mat-dialog-close color="primary" [attr.data-testId]="'confirm-no'">Nein</button>
<button
*ngIf="this.isNoButtonVisible()"
[disabled]="this.isNoButtonDisabled()"
mat-button
mat-dialog-close
color="primary"
[attr.data-testId]="'confirm-no'"
>
Nein
</button>
<button
*ngIf="this.isCloseButtonVisible()"
[disabled]="this.isCloseButtonDisabled()"
mat-button
mat-dialog-close
color="primary"
[attr.data-testId]="'confirm-close'"
>
Schliessen
</button>
</div>
</ng-container>
</app-dialog-template-core>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, Inject, InjectionToken, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogConfig, MatDialogRef } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ConfirmDialogData } from '../../../services/dialog.service';
import { ButtonState } from '../../types/enums/ButtonState';

@Component({
selector: 'app-confirm-dialog',
Expand All @@ -11,17 +11,50 @@ import { ConfirmDialogData } from '../../../services/dialog.service';
export class ConfirmDialogComponent implements OnInit {
dialogTitle: string = '';
dialogText: string = '';
yesButtonState?: ButtonState;
noButtonState?: ButtonState;
closeButtonState?: ButtonState;

constructor(
@Inject(MAT_DIALOG_DATA) public data: ConfirmDialogData,
public dialogRef: MatDialogRef<ConfirmDialogComponent>,
) {}

ngOnInit() {
this.dialogTitle = this.data.title || 'Are you sure?';
this.dialogText = this.data.text || 'Are you sure you want to delete this item?';
this.dialogTitle = this.data.title;
this.dialogText = this.data.text;
this.yesButtonState = this.data.yesButtonState ?? ButtonState.Visible_Enabled;
this.noButtonState = this.data.noButtonState ?? ButtonState.Visible_Enabled;
this.closeButtonState = this.data.closeButtonState ?? ButtonState.Hidden;
}

closeAndDelete() {
this.dialogRef.close(true);
}

isYesButtonVisible() {
return this.yesButtonState === ButtonState.Visible_Enabled || this.yesButtonState === ButtonState.Visible_Disabled;
}

isYesButtonDisabled() {
return this.yesButtonState === ButtonState.Visible_Disabled;
}

isNoButtonVisible() {
return this.noButtonState === ButtonState.Visible_Enabled || this.noButtonState === ButtonState.Visible_Disabled;
}

isNoButtonDisabled() {
return this.noButtonState === ButtonState.Visible_Disabled;
}

isCloseButtonVisible() {
return (
this.closeButtonState === ButtonState.Visible_Enabled || this.closeButtonState === ButtonState.Visible_Disabled
);
}

isCloseButtonDisabled() {
return this.closeButtonState === ButtonState.Visible_Disabled;
}
}
5 changes: 5 additions & 0 deletions frontend/src/app/shared/types/enums/ButtonState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum ButtonState {
Visible_Enabled,
Visible_Disabled,
Hidden,
}

0 comments on commit 30b85e4

Please sign in to comment.