Skip to content

Commit 624b513

Browse files
committed
Add notification component
1 parent bc94930 commit 624b513

9 files changed

+638
-0
lines changed

src/i18n/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"MODAL": {
7070
"CLOSE": "Close modal"
7171
},
72+
"NOTIFICATION": {
73+
"CLOSE_BUTTON": "Close alert notification"
74+
},
7275
"OVERFLOW_MENU": {
7376
"OVERFLOW": "Overflow"
7477
},

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ export * from "./progress-indicator/progress-indicator.module";
2525
export * from "./i18n/i18n.module";
2626
export * from "./pagination/pagination.module";
2727
export * from "./loading/loading.module";
28+
export * from "./notification/notification.module";
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import {
2+
Component,
3+
Input,
4+
Output,
5+
EventEmitter,
6+
ComponentRef,
7+
ViewChild,
8+
OnInit
9+
} from "@angular/core";
10+
11+
import { NotificationService } from "./notification.service";
12+
import { InlineNotificationContent } from "./notification-content.interface";
13+
import { I18n } from "./../i18n/i18n.module";
14+
15+
/**
16+
* InlineNotification messages are displayed toward the top of the UI and do not interrupt user’s work.
17+
*
18+
* @export
19+
* @class InlineNotification
20+
*/
21+
@Component({
22+
selector: "ibm-inline-notification",
23+
template: `
24+
<div
25+
#notification
26+
class="bx--inline-notification bx--inline-notification--{{notificationObj.type}}"
27+
role="alert">
28+
<div class="bx--inline-notification__details">
29+
<svg class="bx--inline-notification__icon" width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
30+
<path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zM3.293 4.707l8 8 1.414-1.414-8-8-1.414 1.414z" fill-rule="evenodd"/>
31+
</svg>
32+
<div class="bx--inline-notification__text-wrapper">
33+
<p [innerHTML]="notificationObj.title" class="bx--inline-notification__title"></p>
34+
<p [innerHTML]="notificationObj.message" class="bx--inline-notification__subtitle"></p>
35+
</div>
36+
</div>
37+
<button
38+
(click)="onClose()"
39+
class="bx--inline-notification__close-button"
40+
[attr.aria-label]="notificationObj.closeLabel"
41+
type="button">
42+
<svg
43+
class="bx--inline-notification__close-icon"
44+
width="10"
45+
height="10"
46+
viewBox="0 0 10 10"
47+
xmlns="http://www.w3.org/2000/svg">
48+
<path d="M6.32 5L10 8.68 8.68 10 5 6.32 1.32 10 0 8.68 3.68 5 0 1.32 1.32 0 5 3.68 8.68 0 10 1.32 6.32 5z" fill-rule="nonzero"/>
49+
</svg>
50+
</button>
51+
</div>
52+
`,
53+
providers: [NotificationService]
54+
})
55+
export class InlineNotification implements OnInit {
56+
/**
57+
* Can have `type`, `title`, and `message` members.
58+
*
59+
* `type` can be one of `"info"`, `"warning"`, `"danger"`, `"success"`
60+
*
61+
* `message` is message for notification to display
62+
*
63+
*/
64+
@Input() notificationObj: InlineNotificationContent;
65+
66+
/**
67+
* Emits on close.
68+
*
69+
* @type {EventEmitter<any>}
70+
* @memberof InlineNotification
71+
*/
72+
@Output() close: EventEmitter<any> = new EventEmitter();
73+
74+
componentRef: ComponentRef<InlineNotification>;
75+
76+
@ViewChild("notification") notification;
77+
78+
constructor(protected notificationService: NotificationService, protected i18n: I18n) {}
79+
80+
ngOnInit() {
81+
if (!this.notificationObj.closeLabel) {
82+
this.notificationObj.closeLabel = this.i18n.get().NOTIFICATION.CLOSE_BUTTON;
83+
}
84+
}
85+
86+
/**
87+
* Emits close event.
88+
*
89+
* @memberof InlineNotification
90+
*/
91+
onClose() {
92+
this.close.emit();
93+
}
94+
95+
destroy() {
96+
this.notificationService.close(this);
97+
}
98+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export interface InlineNotificationContent {
2+
type: string;
3+
title: string;
4+
target?: string;
5+
duration?: number;
6+
smart?: boolean;
7+
closeLabel?: string;
8+
message: string;
9+
}
10+
11+
export interface ToastNotificationContent extends InlineNotificationContent {
12+
subtitle: string;
13+
caption: string;
14+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { TestBed } from "@angular/core/testing";
2+
3+
import { StaticIconModule } from "./../icon/static-icon.module";
4+
5+
import { InlineNotification, NotificationService } from "./notification.module";
6+
import { I18nModule } from "../i18n/i18n.module";
7+
8+
9+
describe("Notification", () => {
10+
beforeEach(() => {
11+
TestBed.configureTestingModule({
12+
declarations: [InlineNotification],
13+
providers: [NotificationService],
14+
imports: [
15+
StaticIconModule,
16+
I18nModule
17+
]
18+
});
19+
});
20+
21+
it("should work", () => {
22+
const fixture = TestBed.createComponent(InlineNotification);
23+
expect(fixture.componentInstance instanceof InlineNotification).toBe(true);
24+
});
25+
26+
it("should render info notification", () => {
27+
const fixture = TestBed.createComponent(InlineNotification);
28+
fixture.componentInstance.notificationObj = {
29+
type: "info",
30+
title: "sample",
31+
message: "sample message"
32+
};
33+
fixture.detectChanges();
34+
35+
let notification = fixture.nativeElement.querySelector(".bx--inline-notification");
36+
expect(notification.classList.contains("bx--inline-notification--info")).toBeTruthy();
37+
});
38+
39+
it("should render danger notification", () => {
40+
const fixture = TestBed.createComponent(InlineNotification);
41+
fixture.componentInstance.notificationObj = {
42+
type: "danger",
43+
title: "sample",
44+
message: "sample message"
45+
};
46+
fixture.detectChanges();
47+
48+
let notification = fixture.nativeElement.querySelector(".bx--inline-notification");
49+
expect(notification.classList.contains("bx--inline-notification--danger")).toBeTruthy();
50+
});
51+
52+
it("should render info warning", () => {
53+
const fixture = TestBed.createComponent(InlineNotification);
54+
fixture.componentInstance.notificationObj = {
55+
type: "warning",
56+
title: "sample",
57+
message: "sample message"
58+
};
59+
fixture.detectChanges();
60+
61+
let notification = fixture.nativeElement.querySelector(".bx--inline-notification");
62+
expect(notification.classList.contains("bx--inline-notification--warning")).toBeTruthy();
63+
});
64+
65+
it("should render info success", () => {
66+
const fixture = TestBed.createComponent(InlineNotification);
67+
fixture.componentInstance.notificationObj = {
68+
type: "success",
69+
title: "sample",
70+
message: "sample message"
71+
};
72+
fixture.detectChanges();
73+
74+
let notification = fixture.nativeElement.querySelector(".bx--inline-notification");
75+
expect(notification.classList.contains("bx--inline-notification--success")).toBeTruthy();
76+
});
77+
78+
it("should display correct message", () => {
79+
const fixture = TestBed.createComponent(InlineNotification);
80+
fixture.componentInstance.notificationObj = {
81+
type: "success",
82+
title: "sample",
83+
message: "sample message"
84+
};
85+
fixture.detectChanges();
86+
87+
let p = fixture.nativeElement.querySelector(".bx--inline-notification__subtitle");
88+
89+
expect(p.innerHTML.trim()).toEqual("sample message");
90+
});
91+
92+
it("should emit change when close button is clicked", () => {
93+
const fixture = TestBed.createComponent(InlineNotification);
94+
fixture.componentInstance.notificationObj = {
95+
type: "success",
96+
title: "sample",
97+
message: "sample message"
98+
};
99+
fixture.detectChanges();
100+
101+
spyOn(fixture.componentInstance.close, "emit");
102+
103+
let button = fixture.nativeElement.querySelector(".bx--inline-notification__close-button");
104+
105+
button.click();
106+
expect(fixture.componentInstance.close.emit).toHaveBeenCalled();
107+
});
108+
109+
it("should emit change when notification is closed programmatically", () => {
110+
const fixture = TestBed.createComponent(InlineNotification);
111+
fixture.componentInstance.notificationObj = {
112+
type: "info",
113+
title: "sample",
114+
message: "sample message"
115+
};
116+
fixture.detectChanges();
117+
118+
fixture.componentInstance.destroy();
119+
120+
expect(fixture.componentInstance);
121+
});
122+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { NgModule } from "@angular/core";
2+
import { CommonModule } from "@angular/common";
3+
4+
import { StaticIconModule } from "./../icon/static-icon.module";
5+
6+
import { ToastNotification } from "./toast-notification.component";
7+
import { InlineNotification } from "./inline-notification.component";
8+
import { NotificationService } from "./notification.service";
9+
import { I18nModule } from "./../i18n/i18n.module";
10+
11+
export { NotificationService } from "./notification.service";
12+
export { InlineNotification } from "./inline-notification.component";
13+
export { ToastNotification } from "./toast-notification.component";
14+
15+
@NgModule({
16+
declarations: [
17+
InlineNotification,
18+
ToastNotification
19+
],
20+
exports: [
21+
InlineNotification,
22+
ToastNotification
23+
],
24+
entryComponents: [InlineNotification, ToastNotification],
25+
imports: [CommonModule, StaticIconModule, I18nModule],
26+
providers: [NotificationService]
27+
})
28+
export class NotificationModule {}

0 commit comments

Comments
 (0)