forked from NationalBankBelgium/stark
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(stark-ui): implement Material dialogs presets: alert, confirm an…
…d prompt ISSUES CLOSED: #793
- Loading branch information
1 parent
14e432d
commit 34381b8
Showing
36 changed files
with
1,442 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./dialogs/dialogs.module"; | ||
export * from "./dialogs/components"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export * from "./components/alert-dialog.component"; | ||
export * from "./components/alert-dialog-content.intf"; | ||
export * from "./components/confirm-dialog.component"; | ||
export * from "./components/confirm-dialog-content.intf"; | ||
export * from "./components/prompt-dialog.component"; | ||
export * from "./components/prompt-dialog-content.intf"; |
6 changes: 6 additions & 0 deletions
6
packages/stark-ui/src/modules/dialogs/components/alert-dialog-content.intf.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { StarkBaseDialogContent } from "./dialog-content.intf"; | ||
|
||
/** | ||
* Content that can be shown in the {@link StarkAlertDialogComponent} | ||
*/ | ||
export interface StarkAlertDialogContent extends StarkBaseDialogContent {} |
5 changes: 5 additions & 0 deletions
5
packages/stark-ui/src/modules/dialogs/components/alert-dialog-theme.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.stark-alert-dialog { | ||
.button-ok { | ||
color: mat-color($primary-palette, 500); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
packages/stark-ui/src/modules/dialogs/components/alert-dialog.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<h2 mat-dialog-title>{{ content.title || "" | translate }}</h2> | ||
|
||
<div mat-dialog-content>{{ content.textContent || "" | translate }}</div> | ||
|
||
<div mat-dialog-actions> | ||
<button mat-button (click)="onOk()" class="button-ok">{{ content.ok || "OK" | translate }}</button> | ||
</div> |
221 changes: 221 additions & 0 deletions
221
packages/stark-ui/src/modules/dialogs/components/alert-dialog.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
/* tslint:disable:completed-docs no-big-function */ | ||
import { async, ComponentFixture, fakeAsync, inject, TestBed, tick } from "@angular/core/testing"; | ||
import { CommonModule } from "@angular/common"; | ||
import { Component, ComponentFactoryResolver } from "@angular/core"; | ||
import { MatDialog, MatDialogModule, MatDialogRef } from "@angular/material/dialog"; | ||
import { NoopAnimationsModule } from "@angular/platform-browser/animations"; | ||
import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"; | ||
import { OverlayContainer } from "@angular/cdk/overlay"; | ||
import { ESCAPE } from "@angular/cdk/keycodes"; | ||
import { TranslateModule } from "@ngx-translate/core"; | ||
import { Observer } from "rxjs"; | ||
import { StarkAlertDialogContent } from "./alert-dialog-content.intf"; | ||
import { StarkAlertDialogComponent, StarkAlertDialogResult } from "./alert-dialog.component"; | ||
import createSpyObj = jasmine.createSpyObj; | ||
import SpyObj = jasmine.SpyObj; | ||
|
||
@Component({ | ||
selector: `host-component`, | ||
template: ` | ||
no content | ||
` | ||
}) | ||
class TestHostComponent {} | ||
|
||
describe("AlertDialogComponent", () => { | ||
let hostFixture: ComponentFixture<TestHostComponent>; | ||
let hostComponent: TestHostComponent; | ||
let dialogService: MatDialog; | ||
let overlayContainer: OverlayContainer; | ||
let overlayContainerElement: HTMLElement; | ||
let dialogComponentSelector: string; | ||
|
||
const dummyDialogContent: StarkAlertDialogContent = { | ||
title: "This is the dialog title", | ||
textContent: "Here goes the content", | ||
ok: "Ok button label" | ||
}; | ||
|
||
function triggerClick(element: HTMLElement): void { | ||
element.click(); | ||
} | ||
|
||
/** | ||
* Angular Material dialogs listen to the Escape key on the keydown event | ||
*/ | ||
function triggerKeydownEscape(element: HTMLElement): void { | ||
// more verbose way to create and trigger an event (the only way it works in IE) | ||
// https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events | ||
const keydownEvent: Event = document.createEvent("Event"); | ||
keydownEvent.initEvent("keydown", true, true); | ||
keydownEvent["key"] = "Escape"; | ||
keydownEvent["keyCode"] = ESCAPE; | ||
element.dispatchEvent(keydownEvent); | ||
} | ||
|
||
beforeEach(async(() => { | ||
return TestBed.configureTestingModule({ | ||
declarations: [TestHostComponent, StarkAlertDialogComponent], | ||
imports: [CommonModule, NoopAnimationsModule, MatDialogModule, TranslateModule.forRoot()], | ||
providers: [] | ||
}) | ||
.overrideModule(BrowserDynamicTestingModule, { | ||
// add entryComponent to TestingModule (suggested in https://github.com/angular/angular/issues/12079) | ||
set: { entryComponents: [StarkAlertDialogComponent] } | ||
}) | ||
.compileComponents(); | ||
})); | ||
|
||
beforeEach(inject( | ||
[MatDialog, OverlayContainer, ComponentFactoryResolver], | ||
(d: MatDialog, oc: OverlayContainer, cfr: ComponentFactoryResolver) => { | ||
dialogService = d; | ||
overlayContainer = oc; | ||
overlayContainerElement = oc.getContainerElement(); | ||
dialogComponentSelector = cfr.resolveComponentFactory(StarkAlertDialogComponent).selector; | ||
} | ||
)); | ||
|
||
afterEach(() => { | ||
overlayContainer.ngOnDestroy(); | ||
}); | ||
|
||
beforeEach(() => { | ||
hostFixture = TestBed.createComponent(TestHostComponent); | ||
hostComponent = hostFixture.componentInstance; | ||
hostFixture.detectChanges(); | ||
}); | ||
|
||
it("should be correctly opened via the MatDialog service", () => { | ||
expect(hostComponent).toBeDefined(); | ||
|
||
expect(dialogService).toBeDefined(); | ||
const dialogRef: MatDialogRef<StarkAlertDialogComponent, StarkAlertDialogResult> = dialogService.open< | ||
StarkAlertDialogComponent, | ||
StarkAlertDialogContent, | ||
StarkAlertDialogResult | ||
>(StarkAlertDialogComponent, { | ||
data: dummyDialogContent | ||
}); | ||
hostFixture.detectChanges(); | ||
|
||
expect(dialogRef.componentInstance instanceof StarkAlertDialogComponent).toBe(true); | ||
|
||
const dialogElement: HTMLElement | null = (<HTMLElement>overlayContainerElement).querySelector<HTMLElement>( | ||
"mat-dialog-container " + dialogComponentSelector | ||
); | ||
|
||
const dialogTitleElement: HTMLElement | null = (<HTMLElement>dialogElement).querySelector<HTMLElement>("[mat-dialog-title]"); | ||
expect(dialogTitleElement).toBeDefined(); | ||
expect((<HTMLElement>dialogTitleElement).innerHTML).toEqual(<string>dummyDialogContent.title); | ||
|
||
const dialogContentElement: HTMLElement | null = (<HTMLElement>dialogElement).querySelector<HTMLElement>("[mat-dialog-content]"); | ||
expect(dialogContentElement).toBeDefined(); | ||
expect((<HTMLElement>dialogContentElement).innerHTML).toBe(<string>dummyDialogContent.textContent); | ||
|
||
const dialogActionsElement: HTMLElement | null = (<HTMLElement>dialogElement).querySelector<HTMLElement>("[mat-dialog-actions]"); | ||
expect(dialogActionsElement).toBeDefined(); | ||
const dialogButtonElements: NodeListOf<HTMLElement> = (<HTMLElement>dialogActionsElement).querySelectorAll("button"); | ||
expect(dialogButtonElements.length).toBe(1); | ||
expect(dialogButtonElements[0].innerHTML).toBe(<string>dummyDialogContent.ok); | ||
}); | ||
|
||
it("should return 'ok' as result when the 'Ok' button is clicked", fakeAsync(() => { | ||
expect(dialogService).toBeDefined(); | ||
const dialogRef: MatDialogRef<StarkAlertDialogComponent, StarkAlertDialogResult> = dialogService.open< | ||
StarkAlertDialogComponent, | ||
StarkAlertDialogContent, | ||
StarkAlertDialogResult | ||
>(StarkAlertDialogComponent, { | ||
data: dummyDialogContent | ||
}); | ||
hostFixture.detectChanges(); | ||
|
||
const dialogElement: HTMLElement | null = (<HTMLElement>overlayContainerElement).querySelector<HTMLElement>( | ||
"mat-dialog-container " + dialogComponentSelector | ||
); | ||
|
||
const mockObserver: SpyObj<Observer<StarkAlertDialogResult>> = createSpyObj<Observer<StarkAlertDialogResult>>("observerSpy", [ | ||
"next", | ||
"error", | ||
"complete" | ||
]); | ||
dialogRef.afterClosed().subscribe(mockObserver); | ||
|
||
const dialogActionsElement: HTMLElement | null = (<HTMLElement>dialogElement).querySelector<HTMLElement>("[mat-dialog-actions]"); | ||
expect(dialogActionsElement).toBeDefined(); | ||
const dialogButtonElements: NodeListOf<HTMLElement> = (<HTMLElement>dialogActionsElement).querySelectorAll("button"); | ||
expect(dialogButtonElements.length).toBe(1); | ||
|
||
triggerClick(dialogButtonElements[0]); | ||
hostFixture.detectChanges(); | ||
|
||
// to avoid NgZone error: "Error: 3 timer(s) still in the queue" | ||
tick(500); // the amount of mills can be known by calling flush(): const remainingMills: number = flush(); | ||
|
||
expect(mockObserver.next).toHaveBeenCalledTimes(1); | ||
expect(mockObserver.next).toHaveBeenCalledWith("ok"); | ||
expect(mockObserver.error).not.toHaveBeenCalled(); | ||
expect(mockObserver.complete).toHaveBeenCalled(); | ||
})); | ||
|
||
it("should return undefined as result when it is cancelled by clicking outside of the dialog", fakeAsync(() => { | ||
expect(dialogService).toBeDefined(); | ||
const dialogRef: MatDialogRef<StarkAlertDialogComponent, StarkAlertDialogResult> = dialogService.open< | ||
StarkAlertDialogComponent, | ||
StarkAlertDialogContent, | ||
StarkAlertDialogResult | ||
>(StarkAlertDialogComponent, { | ||
data: dummyDialogContent | ||
}); | ||
hostFixture.detectChanges(); | ||
|
||
const mockObserver: SpyObj<Observer<StarkAlertDialogResult>> = createSpyObj<Observer<StarkAlertDialogResult>>("observerSpy", [ | ||
"next", | ||
"error", | ||
"complete" | ||
]); | ||
dialogRef.afterClosed().subscribe(mockObserver); | ||
|
||
triggerClick(<HTMLElement>overlayContainerElement.querySelector(".cdk-overlay-backdrop")); // clicking on the backdrop | ||
hostFixture.detectChanges(); | ||
|
||
// to avoid NgZone error: "Error: 3 timer(s) still in the queue" | ||
tick(500); // the amount of mills can be known by calling flush(): const remainingMills: number = flush(); | ||
|
||
expect(mockObserver.next).toHaveBeenCalledTimes(1); | ||
expect(mockObserver.next).toHaveBeenCalledWith(undefined); | ||
expect(mockObserver.error).not.toHaveBeenCalled(); | ||
expect(mockObserver.complete).toHaveBeenCalled(); | ||
})); | ||
|
||
it("should return undefined as result when it is cancelled by pressing the ESC key", fakeAsync(() => { | ||
expect(dialogService).toBeDefined(); | ||
const dialogRef: MatDialogRef<StarkAlertDialogComponent, StarkAlertDialogResult> = dialogService.open< | ||
StarkAlertDialogComponent, | ||
StarkAlertDialogContent, | ||
StarkAlertDialogResult | ||
>(StarkAlertDialogComponent, { | ||
data: dummyDialogContent | ||
}); | ||
hostFixture.detectChanges(); | ||
|
||
const mockObserver: SpyObj<Observer<StarkAlertDialogResult>> = createSpyObj<Observer<StarkAlertDialogResult>>("observerSpy", [ | ||
"next", | ||
"error", | ||
"complete" | ||
]); | ||
dialogRef.afterClosed().subscribe(mockObserver); | ||
|
||
triggerKeydownEscape(overlayContainerElement); // pressing Esc key in the overlay | ||
hostFixture.detectChanges(); | ||
|
||
// to avoid NgZone error: "Error: 3 timer(s) still in the queue" | ||
tick(500); // the amount of mills can be known by calling flush(): const remainingMills: number = flush(); | ||
|
||
expect(mockObserver.next).toHaveBeenCalledTimes(1); | ||
expect(mockObserver.next).toHaveBeenCalledWith(undefined); | ||
expect(mockObserver.error).not.toHaveBeenCalled(); | ||
expect(mockObserver.complete).toHaveBeenCalled(); | ||
})); | ||
}); |
43 changes: 43 additions & 0 deletions
43
packages/stark-ui/src/modules/dialogs/components/alert-dialog.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Component, Inject, ViewEncapsulation } from "@angular/core"; | ||
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; | ||
import { StarkAlertDialogContent } from "./alert-dialog-content.intf"; | ||
|
||
/** | ||
* Possible results of the {@link StarkAlertDialogComponent} after being closed. | ||
* | ||
* - "ok": The user clicked on the "Ok" button | ||
* - `undefined`: The dialog was cancelled by either clicking outside of dialog or by pressing the ESC key | ||
*/ | ||
export type StarkAlertDialogResult = "ok" | undefined; | ||
|
||
/** | ||
* Alert dialog component to be opened via the Angular Material's {@link MatDialog} service | ||
*/ | ||
@Component({ | ||
selector: "stark-alert-dialog", | ||
templateUrl: "./alert-dialog.component.html", | ||
encapsulation: ViewEncapsulation.None, | ||
// We need to use host instead of @HostBinding: https://github.com/NationalBankBelgium/stark/issues/664 | ||
host: { | ||
class: "stark-alert-dialog" | ||
} | ||
}) | ||
export class StarkAlertDialogComponent { | ||
/** | ||
* Class constructor | ||
* @param dialogRef - Reference this dialog instance | ||
* @param content - Content to be shown in the alert dialog (dynamically translated via the Translate service if the | ||
* provided text is defined in the translation keys) | ||
*/ | ||
public constructor( | ||
public dialogRef: MatDialogRef<StarkAlertDialogComponent, StarkAlertDialogResult>, | ||
@Inject(MAT_DIALOG_DATA) public content: StarkAlertDialogContent | ||
) {} | ||
|
||
/** | ||
* Callback method to be triggered when the "Ok" button is clicked | ||
*/ | ||
public onOk(): void { | ||
this.dialogRef.close("ok"); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
packages/stark-ui/src/modules/dialogs/components/confirm-dialog-content.intf.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { StarkBaseDialogContent } from "./dialog-content.intf"; | ||
|
||
/** | ||
* Content that can be shown in the {@link StarkConfirmDialogComponent} | ||
*/ | ||
export interface StarkConfirmDialogContent extends StarkBaseDialogContent { | ||
/** | ||
* Label to be set in the "Cancel" button. | ||
*/ | ||
cancel?: string; | ||
} |
6 changes: 6 additions & 0 deletions
6
packages/stark-ui/src/modules/dialogs/components/confirm-dialog-theme.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.stark-confirm-dialog { | ||
.button-ok, | ||
.button-cancel { | ||
color: mat-color($primary-palette, 500); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
packages/stark-ui/src/modules/dialogs/components/confirm-dialog.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<h2 mat-dialog-title>{{ content.title || "" | translate }}</h2> | ||
|
||
<div mat-dialog-content>{{ content.textContent || "" | translate }}</div> | ||
|
||
<div mat-dialog-actions> | ||
<button mat-button (click)="onCancel()" class="button-cancel">{{ content.cancel || "CANCEL" | translate }}</button> | ||
<button mat-button cdkFocusInitial (click)="onOk()" class="button-ok">{{ content.ok || "OK" | translate }}</button> | ||
</div> |
Oops, something went wrong.