Skip to content

Commit

Permalink
feat(stark-ui): implement toast notification feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Dedepon committed Sep 21, 2018
1 parent 75c709b commit 98390c2
Show file tree
Hide file tree
Showing 40 changed files with 1,138 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/rollup.config.common-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const globals = {
"@angular/material/paginator": "ngMaterial.paginator",
"@angular/material/select": "ngMaterial.select",
"@angular/material/sidenav": "ngMaterial.sidenav",
"@angular/material/snack-bar": "ngMaterial.snack-bar",
"@angular/material/sort": "ngMaterial.sort",
"@angular/material/table": "ngMaterial.table",
"@angular/material/tooltip": "ngMaterial.tooltip",
Expand Down
1 change: 1 addition & 0 deletions packages/stark-ui/src/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./common/message";
2 changes: 2 additions & 0 deletions packages/stark-ui/src/common/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./message/message.intf";
export * from "./message/message-type.intf";
8 changes: 8 additions & 0 deletions packages/stark-ui/src/common/message/message-type.intf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Message types supported by Stark.
*/
export enum StarkMessageType {
INFO,
WARNING,
ERROR
}
40 changes: 40 additions & 0 deletions packages/stark-ui/src/common/message/message.intf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { StarkMessageType } from "./message-type.intf";

/**
* Simple message with text, type, ...
*/
export interface StarkMessage {
/**
* Id of the message
*/
id: string;

/**
* Translation key of the message to be displayed. If no translation found, it is displayed as is
*/
key: string;

/**
* An object containing variable values to interpolate translations against
*/
interpolateValues?: object;

/**
* Message code
*/
code: string;

/**
* Message type
*/
type: StarkMessageType;

/**
* Message priority
* determines the position of a message in a list, the highest priority is shown first
* messages are ordered ascending by priority
* a lower value means a higher priority
* the default value is 999
*/
priority?: number;
}
1 change: 1 addition & 0 deletions packages/stark-ui/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export * from "./modules/pretty-print";
export * from "./modules/slider";
export * from "./modules/svg-view-box";
export * from "./modules/table";
export * from "./modules/toast-notification";
3 changes: 3 additions & 0 deletions packages/stark-ui/src/modules/toast-notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./toast-notification/toast-notification.module";
export * from "./toast-notification/components";
export * from "./toast-notification/services";
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./components/toast-notification.component";
export * from "./components/toast-message.intf";
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.stark-toast {
&.stark-toast-message-error {
background-color: $message-alert-700;
}

&.stark-toast-message-warning {
background-color: $message-warning-700;
}

&.stark-toast-message-info {
background-color: $message-info-700;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.mat-snack-bar-container {
margin: 8px !important;
padding: 0 !important;
}

.stark-toast {
min-height: 0;
display: flex;
white-space: normal;
align-items: center;
padding: 6px 16px 6px 16px;
span {
line-height: 24px;
margin: 6px;
}
mat-icon {
margin: 6px;
min-width: 24px;
width: 24px;
min-height: 24px;
height: 24px;
}
.stark-toast-action {
float: none;
margin: 0;
padding: 0;
}
.stark-toast-text {
flex: 1 1 auto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { StarkMessage } from "../../../common";

/**
* Stark Toast Notification Message Interface
*/
export interface StarkToastMessage extends StarkMessage {
/**
* How many milliseconds the message will be displayed before automatically closing
* If set to 0, the toast will stay open until closed manually
*/
delay?: number;

/**
* If provided, an action button with the label provided will be added in the notification
* The return value is "ok" when this button is clicked
*/
actionLabel?: string;

/**
* Array containing the css classes to be applied to the action button (if the message contains an action to be displayed)
*/
actionClasses?: string[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div class="stark-toast" [ngClass]="getMessageTypeClass()">

<mat-icon svgIcon="information" starkSvgViewBox *ngIf="message.type === 0"></mat-icon>
<mat-icon svgIcon="alert-circle" starkSvgViewBox *ngIf="message.type === 1"></mat-icon>
<mat-icon svgIcon="alert" starkSvgViewBox *ngIf="message.type === 2"></mat-icon>

<span class="stark-toast-text"
role="alert"
[innerHTML]="message.key | translate:message.interpolateValues">
</span>

<button class="stark-toast-action"
*ngIf="message.actionLabel"
[ngClass]="message.actionClasses"
(click)="closeToast()"
mat-button>
<span translate>{{ message.actionLabel }}</span>
</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*tslint:disable:completed-docs*/
import { Component, NO_ERRORS_SCHEMA, ViewChild } from "@angular/core";
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
import { MAT_SNACK_BAR_DATA, MatSnackBar, MatSnackBarRef } from "@angular/material/snack-bar";
import { MatButtonModule } from "@angular/material/button";
import { MatIconModule } from "@angular/material/icon";
import { TranslateModule } from "@ngx-translate/core";
import { STARK_LOGGING_SERVICE } from "@nationalbankbelgium/stark-core";
import { MockStarkLoggingService } from "@nationalbankbelgium/stark-core/testing";
import { StarkMessageType } from "../../../common";
import { StarkToastNotificationComponent } from "../components";
import { StarkSvgViewBoxModule } from "../../svg-view-box";

@Component({
selector: `host-component`,
template: `<stark-toast-notification></stark-toast-notification>`
})
class TestHostComponent {
@ViewChild(StarkToastNotificationComponent)
public toastNotificationComponent: StarkToastNotificationComponent;
}

describe("ToastNotificationComponent", () => {
let component: StarkToastNotificationComponent;
let hostComponent: TestHostComponent;
let fixture: ComponentFixture<TestHostComponent>;

const mockMatSnackBarConfig: any = {
delay: 10,
actionLabel: "action",
actionClasses: []
};

const mockSnackBarRef: Partial<MatSnackBarRef<any>> = {
dismissWithAction: jasmine.createSpy('dismissWithAction')
};

const mockSnackBar: Partial<MatSnackBar> = {
_openedSnackBarRef: <MatSnackBarRef<any>>mockSnackBarRef
};
/**
* async beforeEach
*/
beforeEach(async(() => {
return (
TestBed.configureTestingModule({
declarations: [StarkToastNotificationComponent, TestHostComponent],
imports: [TranslateModule.forRoot(), MatButtonModule, StarkSvgViewBoxModule, MatIconModule],
providers: [
{ provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() },
{ provide: MatSnackBar, useValue: mockSnackBar },
{ provide: MAT_SNACK_BAR_DATA, useValue: mockMatSnackBarConfig }
],
schemas: [NO_ERRORS_SCHEMA] // to avoid errors due to "mat-icon" directive not known (which we don't want to add in these tests)
})
/**
* Compile template and css
*/
.compileComponents()
);
}));

/**
* Synchronous beforeEach
*/
beforeEach(() => {
fixture = TestBed.createComponent(TestHostComponent);
hostComponent = fixture.componentInstance;
fixture.detectChanges(); // trigger initial data binding

component = hostComponent.toastNotificationComponent;
});

describe("on initialization", () => {
it("should set internal component properties", () => {
expect(fixture).toBeDefined();
expect(component).toBeDefined();

expect(component.logger).not.toBeNull();
expect(component.logger).toBeDefined();

expect(component.snackBar).not.toBeNull();
expect(component.snackBar).toBeDefined();

expect(component.data).not.toBeNull();
expect(component.data).toBeDefined();
expect(component.data.delay).toBeDefined();
expect(component.data.delay).toBe(10);
expect(component.data.actionLabel).toBeDefined();
expect(component.data.actionLabel).toBe("action");
expect(component.data.actionClasses).toBeDefined();
expect(component.data.type).toBeUndefined();
});
});

describe("on closeToast() call", () => {
it("should call hide service method", () => {
component.closeToast();
if (mockSnackBar._openedSnackBarRef) { expect(mockSnackBar._openedSnackBarRef.dismissWithAction).toHaveBeenCalled(); }
});
});

describe("on getMessageTypeClass() call", () => {
it("should return the correct class name", () => {
let cssClass: string = component.getMessageTypeClass();
expect(cssClass).toBe("");

component.data.type = StarkMessageType.WARNING;
cssClass = component.getMessageTypeClass();
expect(cssClass).toBe("stark-toast-message-warning");

component.data.type = StarkMessageType.INFO;
cssClass = component.getMessageTypeClass();
expect(cssClass).toBe("stark-toast-message-info");

component.data.type = StarkMessageType.ERROR;
cssClass = component.getMessageTypeClass();
expect(cssClass).toBe("stark-toast-message-error");
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Component, HostBinding, Inject, OnInit, ViewEncapsulation } from "@angular/core";
import { MAT_SNACK_BAR_DATA, MatSnackBar } from "@angular/material/snack-bar";
import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
import { StarkMessageType } from "../../../common/message";
import { StarkToastMessage } from "../components";

/**
* Name of the component
*/
const componentName: string = "stark-toast-notification";

/**
* Component display stark's toast notification (based on Angular Material's MatSnackBar) with custom html
*/
@Component({
selector: "stark-toast-notification",
templateUrl: "./toast-notification.component.html",
encapsulation: ViewEncapsulation.None
})
export class StarkToastNotificationComponent implements OnInit {
/**
* Adds class="stark-toast-notification" attribute on the host component
*/
@HostBinding("class")
public class: string = componentName;

/**
* The message data linked to the toast notification.
*/
public message: StarkToastMessage;

/**
* Class constructor
* @param logger - The logger of the application
* @param snackBar - Tha snackBar used to display the toast
* @param data - the data linked to the toast notification
*/
public constructor(
@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService,
public snackBar: MatSnackBar,
@Inject(MAT_SNACK_BAR_DATA) public data: StarkToastMessage
) {
this.message = data;
this.logger.debug(componentName + ": data received : %o", this.message);
}

/**
* Component lifecycle hook
*/
public ngOnInit(): void {
this.logger.debug(componentName + ": controller initialized");
}

/**
* Closes the toast
*/
public closeToast(): void {
// get the reference to the current open toast (this one) from the MatSnackBar service and dismiss it
if (this.snackBar._openedSnackBarRef) { this.snackBar._openedSnackBarRef.dismissWithAction() }
}

/**
* Generate the css class of the toast notification based on its type
* @returns a string containing the css class of the toast notification
*/
public getMessageTypeClass(): string {
switch (this.message.type) {
case StarkMessageType.WARNING:
return "stark-toast-message-warning";
case StarkMessageType.ERROR:
return "stark-toast-message-error";
case StarkMessageType.INFO:
return "stark-toast-message-info";
default:
this.logger.error(componentName + ": unknown message type.");
return "";
}
}
}
4 changes: 4 additions & 0 deletions packages/stark-ui/src/modules/toast-notification/services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./services/toast-notification.service.intf";
export * from "./services/toast-notification.service";
export * from "./services/toast-notification-result.intf";
export * from "./services/toast-notification-option.intf";
Loading

0 comments on commit 98390c2

Please sign in to comment.