[AAE-4529] Refactor Notification History Component (#6620)

* [AAE-4529] Refactor Notification History Component

* Improve code

* Add maxNotifications

* More changes

* Add documentation

* Rebase branch

* Fix build

* Update notification-history.component.md

* Fix e2e tests

Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
This commit is contained in:
davidcanonieto 2021-02-08 16:12:53 +01:00 committed by GitHub
parent 7c1efe48c4
commit 94ec7d06a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 374 additions and 98 deletions

View File

@ -26,3 +26,8 @@ The main purpose of the [Notification history component](../../core/components/n
| --- | --- | --- | --- | | --- | --- | --- | --- |
| menuPositionX | `string` | "after" | Custom choice for opening the menu at the bottom. Can be `before` or `after`. | | menuPositionX | `string` | "after" | Custom choice for opening the menu at the bottom. Can be `before` or `after`. |
| menuPositionY | `string` | "below" | Custom choice for opening the menu at the bottom. Can be `above` or `below`. | | menuPositionY | `string` | "below" | Custom choice for opening the menu at the bottom. Can be `above` or `below`. |
| maxNotifications | `number` | 5 | Maximum number of notifications to display. The rest will remain hidden until load more is clicked. |
## See also
* [Notification Service](../../core/services/notification.service.md)

View File

@ -120,3 +120,12 @@ You can also change the default 5000 ms adding the following configuration in th
```json ```json
"notificationDefaultDuration" : "7000" "notificationDefaultDuration" : "7000"
``` ```
#### Notification types
| Name | Description |
| --- | --- |
| info | To notify messages. It will be displayed with an info icon next to it. |
| warn | To notify warning messages. It will be displayed with a warning icon next to it. |
| error | To notify errors. It will be displayed with an error icon next to it. |
| recursive | To notify recursive messages. If a message is prompt to duplicate an existing notification and you don't want to overload the notification history component with the same message use the recursive type. I.e. notifications coming from an API call that are triggered every time a component is initialized. It will be displayed with an info icon next to it. |

View File

@ -6,10 +6,11 @@
"UNCLAIM": "RELEASE", "UNCLAIM": "RELEASE",
"START PROCESS": "START PROCESS", "START PROCESS": "START PROCESS",
"NOTIFICATIONS": { "NOTIFICATIONS": {
"NO_MESSAGE": "No messages", "NO_MESSAGE": "You have no notifications at this time.",
"TITLE": "Notifications", "TITLE": "Notifications",
"MARK_AS_READ": "Mark all as read", "MARK_AS_READ": "Mark all as read",
"SYSTEM": "System" "SYSTEM": "System",
"LOAD_MORE": "Load more"
}, },
"FORM": { "FORM": {
"START_FORM": { "START_FORM": {

View File

@ -43,6 +43,7 @@ import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { MatBadgeModule } from '@angular/material/badge';
@NgModule({ @NgModule({
imports: [ imports: [
@ -51,7 +52,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule, MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule,
MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule, MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule,
MatMenuModule, MatProgressBarModule, MatSidenavModule, MatSnackBarModule, MatToolbarModule, MatMenuModule, MatProgressBarModule, MatSidenavModule, MatSnackBarModule, MatToolbarModule,
MatTooltipModule, MatDatetimepickerModule, MatNativeDatetimeModule, MatExpansionModule MatTooltipModule, MatDatetimepickerModule, MatNativeDatetimeModule, MatExpansionModule, MatBadgeModule
], ],
exports: [ exports: [
MatAutocompleteModule, MatButtonModule, MatCardModule, MatCheckboxModule, MatAutocompleteModule, MatButtonModule, MatCardModule, MatCheckboxModule,
@ -59,7 +60,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule, MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule,
MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule, MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule,
MatMenuModule, MatProgressBarModule, MatSidenavModule, MatSnackBarModule, MatToolbarModule, MatMenuModule, MatProgressBarModule, MatSidenavModule, MatSnackBarModule, MatToolbarModule,
MatTooltipModule, MatDatetimepickerModule, MatNativeDatetimeModule, MatExpansionModule MatTooltipModule, MatDatetimepickerModule, MatNativeDatetimeModule, MatExpansionModule, MatBadgeModule
] ]
}) })
export class MaterialModule {} export class MaterialModule {}

View File

@ -1,34 +1,73 @@
<div (keyup)="onKeyPress($event)"> <div (keyup)="onKeyPress($event)">
<button mat-button [matMenuTriggerFor]="menu" class="adf-notification-history-menu_button" <button mat-button
id="adf-notification-history-open-button"> [matMenuTriggerFor]="menu"
<mat-icon>mail_outline</mat-icon> class="adf-notification-history-menu_button"
id="adf-notification-history-open-button"
(menuOpened)="onMenuOpened()">
<mat-icon matBadge="&#8288;"
[matBadgeHidden]="!notifications.length"
matBadgeColor="accent"
matBadgeSize="small">notifications</mat-icon>
</button> </button>
<mat-menu #menu="matMenu" [xPosition]="menuPositionX" [yPosition]="menuPositionY" <mat-menu #menu="matMenu"
[overlapTrigger]="false" id="adf-notification-history-menu" class="adf-notification-history-menu"> [xPosition]="menuPositionX"
[yPosition]="menuPositionY"
id="adf-notification-history-menu"
class="adf-notification-history-menu">
<div class="adf-notification-history-list"
(click)="$event.stopPropagation()">
<div mat-subheader>
<span>{{ 'NOTIFICATIONS.TITLE' | translate }}</span>
<button (click)="markAsRead()"
id="adf-notification-history-mark-as-read"
mat-button
color="accent"
*ngIf="notifications.length">
{{ 'NOTIFICATIONS.MARK_AS_READ' | translate }}
</button>
</div>
<div id="adf-notification-history-list">
<mat-list>
<mat-list-item>
<h6 mat-line>{{ 'NOTIFICATIONS.TITLE' | translate }}</h6>
</mat-list-item>
</mat-list>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<mat-list> <mat-list>
<mat-list-item *ngFor="let notification of notifications"> <ng-container *ngIf="notifications.length; else empty_list_template">
<mat-icon mat-list-icon>{{ notification | noticicationIcon }}</mat-icon> <mat-list-item *ngFor="let notification of paginatedNotifications"
<h4 *ngFor="let message of notification.messages" mat-line>{{ message }}</h4> class="adf-notification-history-menu-item"
<p mat-line> {{notification.datetime | date}} </p> (click)="onNotificationClick(notification)">
<p mat-line> {{notification.initiator.displayName | translate}} </p> <div *ngIf="notification.initiator; else no_avatar"
</mat-list-item> matListAvatar
<mat-list-item *ngIf="isEmptyNotification()" id="adf-notification-history-component-no-message"> [outerHTML]="notification.initiator | usernameInitials:'adf-notification-initiator-pic'">
<h4 mat-line>{{ 'NOTIFICATIONS.NO_MESSAGE' | translate }}</h4> </div>
</mat-list-item> <ng-template #no_avatar>
<mat-action-list *ngIf="!isEmptyNotification()" id="adf-notification-history-mark-as-read"> <mat-icon mat-list-icon
<button mat-list-item (click)="markAsRead()">{{ 'NOTIFICATIONS.MARK_AS_READ' | translate }} class="adf-notification-history-menu-initiator">{{ notification | notificationIcon
</button> }}</mat-icon>
</mat-action-list> </ng-template>
<p class="adf-notification-history-menu-message"
*ngFor="let message of notification.messages"
mat-line>{{ message }}</p>
<p class="adf-notification-history-menu-date"
mat-line> {{notification.datetime | adfTimeAgo}} </p>
</mat-list-item>
</ng-container>
<ng-template #empty_list_template>
<mat-list-item id="adf-notification-history-component-no-message"
class="adf-notification-history-menu-no-message">
<h4 mat-line>{{ 'NOTIFICATIONS.NO_MESSAGE' | translate }}</h4>
</mat-list-item>
</ng-template>
</mat-list> </mat-list>
<mat-divider></mat-divider>
<div class="adf-notification-history-load-more"
*ngIf="hasMoreNotifications()">
<button mat-button
(click)="loadMore()">
{{ 'NOTIFICATIONS.LOAD_MORE' | translate }}
</button>
</div>
</div> </div>
</mat-menu> </mat-menu>
</div> </div>

View File

@ -1,23 +1,76 @@
.adf { @mixin adf-notification-history-theme($theme) {
$background: map-get($theme, background);
$accent: map-get($theme, accent);
&-notification-history-menu_button.mat-button { .adf {
margin-right: 0; &-notification-history-menu_button.mat-button {
border-radius: 90%; margin-right: 0;
padding: 0; border-radius: 90%;
min-width: 40px; padding: 0;
height: 40px; min-width: 40px;
height: 40px;
}
&-notification-history-list .mat-subheader {
justify-content: space-between;
}
&-notification-history-menu {
&-item {
cursor: pointer;
}
&-item:focus {
outline: none;
background: mat-color($background, 'hover');
}
&-item:hover {
background-color: mat-color($background, 'hover');
}
&-message, &-no-message {
font-size: 13px !important;
}
&-date {
font-size: 12px !important;
}
&-initiator {
margin: 4px;
}
}
&-notification-initiator-pic {
min-width: 40px;
background: mat-color($accent);
display: inline-block;
height: 40px;
border-radius: 100px;
text-align: center;
font-weight: bolder;
font-size: 18px;
text-transform: uppercase;
vertical-align: middle;
line-height: 40px;
color: mat-color($mat-grey, 50);
}
&-notification-history-load-more {
display: flex;
justify-content: center;
padding: 10px;
}
} }
}
@media only screen and (min-device-width: 480px) {
.mat-menu-panel.adf-notification-history-menu { .mat-menu-panel.adf-notification-history-menu {
max-height: 450px; min-width: 320px;
min-width: 450px; max-height: 500px;
overflow: auto;
padding: 0; .mat-menu-content {
padding: 0;
}
} }
} }
.mat-menu-panel.adf-notification-history-menu .mat-menu-content {
padding: 0;
}

View File

@ -23,10 +23,12 @@ import { OverlayContainer } from '@angular/cdk/overlay';
import { NotificationService } from '../services/notification.service'; import { NotificationService } from '../services/notification.service';
import { StorageService } from '../../services/storage.service'; import { StorageService } from '../../services/storage.service';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { NotificationModel, NOTIFICATION_TYPE } from '../models/notification.model';
describe('Notification History Component', () => { describe('Notification History Component', () => {
let fixture: ComponentFixture<NotificationHistoryComponent>; let fixture: ComponentFixture<NotificationHistoryComponent>;
let component: NotificationHistoryComponent;
let element: HTMLElement; let element: HTMLElement;
let notificationService: NotificationService; let notificationService: NotificationService;
let overlayContainerElement: HTMLElement; let overlayContainerElement: HTMLElement;
@ -48,10 +50,12 @@ describe('Notification History Component', () => {
beforeEach(async(() => { beforeEach(async(() => {
fixture = TestBed.createComponent(NotificationHistoryComponent); fixture = TestBed.createComponent(NotificationHistoryComponent);
component = fixture.componentInstance;
element = fixture.nativeElement; element = fixture.nativeElement;
storage = TestBed.inject(StorageService); storage = TestBed.inject(StorageService);
notificationService = TestBed.inject(NotificationService); notificationService = TestBed.inject(NotificationService);
component.notifications = [];
})); }));
beforeEach(inject([OverlayContainer], (oc: OverlayContainer) => { beforeEach(inject([OverlayContainer], (oc: OverlayContainer) => {
@ -59,6 +63,7 @@ describe('Notification History Component', () => {
})); }));
afterEach(() => { afterEach(() => {
storage.removeItem('notifications');
fixture.destroy(); fixture.destroy();
}); });
@ -73,27 +78,110 @@ describe('Notification History Component', () => {
}); });
}); });
it('should message be present and empty message not be present when there are notifications in the history', (done) => { it('should remove notification when mark all as read is clicked', (done) => {
fixture.detectChanges();
notificationService.showInfo('Example Message Removed');
openNotification();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(component.notifications.length).toBe(1);
const markAllAsRead = <HTMLButtonElement> overlayContainerElement.querySelector('#adf-notification-history-mark-as-read');
markAllAsRead.click();
fixture.detectChanges();
expect(storage.getItem('notifications')).toBeNull();
expect(component.notifications.length).toBe(0);
done();
});
});
it('should message be present and empty message not be present when there are notifications in the history', (done) => {
fixture.detectChanges();
notificationService.showInfo('Example Message'); notificationService.showInfo('Example Message');
openNotification(); openNotification();
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges();
expect(overlayContainerElement.querySelector('#adf-notification-history-component-no-message')).toBeNull(); expect(overlayContainerElement.querySelector('#adf-notification-history-component-no-message')).toBeNull();
expect(overlayContainerElement.querySelector('#adf-notification-history-list').innerHTML).toContain('Example Message'); expect(overlayContainerElement.querySelector('.adf-notification-history-list').innerHTML).toContain('Example Message');
done(); done();
}); });
}); });
it('should remove notification from storage when mark all as read', (done) => { it('should show message when pushed directly to Notification History', (done) => {
const callBackSpy = jasmine.createSpy('callBack');
fixture.detectChanges();
notificationService.pushToNotificationHistory(
{
clickCallBack: callBackSpy,
messages: ['My new message'],
datetime: new Date(),
type: NOTIFICATION_TYPE.RECURSIVE
} as NotificationModel
);
openNotification(); openNotification();
fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
const markAllAsRead = <HTMLButtonElement> overlayContainerElement.querySelector('#adf-notification-history-mark-as-read button');
markAllAsRead.click();
fixture.detectChanges(); fixture.detectChanges();
expect(storage.getItem('notifications')).toBeNull(); const notification = <HTMLButtonElement> overlayContainerElement.querySelector('.adf-notification-history-menu-item');
notification.click();
expect(callBackSpy).toHaveBeenCalled();
done(); done();
}); });
});
it('should show load more button when there are more notifications', (done) => {
fixture.detectChanges();
notificationService.showInfo('Example Message 1');
notificationService.showInfo('Example Message 2');
notificationService.showInfo('Example Message 3');
notificationService.showInfo('Example Message 4');
notificationService.showInfo('Example Message 5');
notificationService.showInfo('Example Message 6');
openNotification();
fixture.whenStable().then(() => {
fixture.detectChanges();
const loadMoreButton = <HTMLButtonElement> overlayContainerElement.querySelector('.adf-notification-history-load-more');
expect(component.paginatedNotifications.length).toBe(5);
expect(loadMoreButton).toBeDefined();
done();
});
});
it('should read notifications from local storage', (done) => {
storage.setItem('notifications', JSON.stringify([{
messages: ['My new message'],
datetime: new Date(),
type: NOTIFICATION_TYPE.RECURSIVE
} as NotificationModel]));
fixture.detectChanges();
openNotification();
fixture.whenStable().then(() => {
fixture.detectChanges();
const notification = <HTMLButtonElement> overlayContainerElement.querySelector('.adf-notification-history-menu-item');
expect(notification).toBeDefined();
done();
});
});
it('should be able to change the maximum number of notifications displayed', (done) => {
component.maxNotifications = 10;
fixture.detectChanges();
notificationService.showInfo('Example Message 1');
notificationService.showInfo('Example Message 2');
notificationService.showInfo('Example Message 3');
notificationService.showInfo('Example Message 4');
notificationService.showInfo('Example Message 5');
notificationService.showInfo('Example Message 6');
openNotification();
fixture.whenStable().then(() => {
fixture.detectChanges();
const notifications = overlayContainerElement.querySelectorAll('.adf-notification-history-menu-item');
expect(notifications.length).toBe(6);
done();
});
}); });
}); });
}); });

View File

@ -15,26 +15,23 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, Input, ViewChild, OnDestroy } from '@angular/core'; import { Component, Input, ViewChild, OnDestroy, OnInit, AfterViewInit, ChangeDetectorRef } from '@angular/core';
import { NotificationService } from '../services/notification.service'; import { NotificationService } from '../services/notification.service';
import { NotificationModel } from '../models/notification.model'; import { NotificationModel, NOTIFICATION_TYPE } from '../models/notification.model';
import { MatMenuTrigger } from '@angular/material/menu'; import { MatMenuTrigger } from '@angular/material/menu';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { StorageService } from '../../services/storage.service'; import { StorageService } from '../../services/storage.service';
import { Pagination } from '@alfresco/js-api';
@Component({ @Component({
selector: 'adf-notification-history', selector: 'adf-notification-history',
styleUrls: ['notification-history.component.scss'], styleUrls: ['notification-history.component.scss'],
templateUrl: 'notification-history.component.html' templateUrl: 'notification-history.component.html'
}) })
export class NotificationHistoryComponent implements OnDestroy { export class NotificationHistoryComponent implements OnDestroy, OnInit, AfterViewInit {
onDestroy$ = new Subject<boolean>(); public static MAX_NOTIFICATION_STACK_LENGTH = 100;
notifications: NotificationModel[] = [];
MAX_NOTIFICATION_STACK_LENGTH = 100;
@ViewChild(MatMenuTrigger, { static: true }) @ViewChild(MatMenuTrigger, { static: true })
trigger: MatMenuTrigger; trigger: MatMenuTrigger;
@ -47,33 +44,33 @@ export class NotificationHistoryComponent implements OnDestroy {
@Input() @Input()
menuPositionY: string = 'below'; menuPositionY: string = 'below';
/** Maximum number of notifications to display. The rest will remain hidden until load more is clicked */
@Input()
maxNotifications: number = 5;
onDestroy$ = new Subject<boolean>();
notifications: NotificationModel[] = [];
paginatedNotifications = [];
pagination: Pagination;
constructor( constructor(
private notificationService: NotificationService, public storageService: StorageService) { private notificationService: NotificationService,
this.notifications = JSON.parse(storageService.getItem('notifications')) || []; public storageService: StorageService,
public cd: ChangeDetectorRef) {
}
ngOnInit() {
this.notifications = JSON.parse(this.storageService.getItem('notifications')) || [];
}
ngAfterViewInit(): void {
this.notificationService.notifications$ this.notificationService.notifications$
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))
.subscribe((message) => { .subscribe((notification: NotificationModel) => {
this.notifications.push(message); this.addNewNotification(notification);
this.cd.detectChanges();
if (this.notifications.length > this.MAX_NOTIFICATION_STACK_LENGTH) { });
this.notifications.shift();
}
storageService.setItem('notifications', JSON.stringify(this.notifications));
});
}
isEmptyNotification(): boolean {
return (!this.notifications || this.notifications.length === 0);
}
onKeyPress(event: KeyboardEvent) {
this.closeUserModal(event);
}
markAsRead() {
this.storageService.removeItem('notifications');
this.notifications = [];
} }
ngOnDestroy() { ngOnDestroy() {
@ -81,9 +78,69 @@ export class NotificationHistoryComponent implements OnDestroy {
this.onDestroy$.complete(); this.onDestroy$.complete();
} }
addNewNotification(notification: NotificationModel) {
this.notifications.unshift(notification);
if (this.notifications.length > NotificationHistoryComponent.MAX_NOTIFICATION_STACK_LENGTH) {
this.notifications.shift();
}
this.saveNotifications();
this.createPagination();
}
saveNotifications() {
this.storageService.setItem('notifications', JSON.stringify(this.notifications.filter((notification) =>
notification.type !== NOTIFICATION_TYPE.RECURSIVE
)));
}
onMenuOpened() {
this.createPagination();
}
onKeyPress(event: KeyboardEvent) {
this.closeUserModal(event);
}
private closeUserModal($event: KeyboardEvent) { private closeUserModal($event: KeyboardEvent) {
if ($event.keyCode === 27) { if ($event.keyCode === 27) {
this.trigger.closeMenu(); this.trigger.closeMenu();
} }
} }
markAsRead() {
this.notifications = [];
this.paginatedNotifications = [];
this.storageService.removeItem('notifications');
this.createPagination();
this.trigger.closeMenu();
}
createPagination() {
this.pagination = {
skipCount: this.maxNotifications,
maxItems: this.maxNotifications,
totalItems: this.notifications.length,
hasMoreItems: this.notifications.length > this.maxNotifications
};
this.paginatedNotifications = this.notifications.slice(0, this.pagination.skipCount);
}
loadMore() {
this.pagination.skipCount = this.pagination.maxItems + this.pagination.skipCount;
this.pagination.hasMoreItems = this.notifications.length > this.pagination.skipCount;
this.paginatedNotifications = this.notifications.slice(0, this.pagination.skipCount);
}
hasMoreNotifications(): boolean {
return this.pagination?.hasMoreItems;
}
onNotificationClick(notification: NotificationModel) {
if (notification.clickCallBack) {
notification.clickCallBack(notification.args);
this.trigger.closeMenu();
}
}
} }

View File

@ -23,7 +23,7 @@ import {
export const rootInitiator: NotificationInitiator = { export const rootInitiator: NotificationInitiator = {
key: '*', key: '*',
displayName: 'NOTIFICATIONS.SYSTEM' displayName: 'SYSTEM'
}; };
export function info(messages: string | string[], initiator: NotificationInitiator = rootInitiator): NotificationModel { export function info(messages: string | string[], initiator: NotificationInitiator = rootInitiator): NotificationModel {

View File

@ -18,12 +18,15 @@
export enum NOTIFICATION_TYPE { export enum NOTIFICATION_TYPE {
INFO = 'info', INFO = 'info',
WARN = 'warning', WARN = 'warning',
ERROR = 'error' ERROR = 'error',
RECURSIVE = 'recursive'
} }
export interface NotificationInitiator { export interface NotificationInitiator {
key: string | Symbol; key: string | Symbol;
displayName: string; displayName: string;
firstName?: string;
lastName?: string;
extra?: any; extra?: any;
} }
@ -32,4 +35,7 @@ export interface NotificationModel {
initiator: NotificationInitiator; initiator: NotificationInitiator;
datetime: Date; datetime: Date;
messages: string[]; messages: string[];
icon?: string;
clickCallBack?: any;
args?: any;
} }

View File

@ -18,16 +18,19 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { MaterialModule } from '../material.module'; import { MaterialModule } from '../material.module';
import { PipeModule } from './../pipes/pipe.module';
import { NotificationHistoryComponent } from './components/notification-history.component'; import { NotificationHistoryComponent } from './components/notification-history.component';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { NotificationIconPipe } from './pipes/notification-icon.pipe'; import { NotificationIconPipe } from './pipes/notification-icon.pipe';
import { PaginationModule } from './../pagination/pagination.module';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
MaterialModule, MaterialModule,
TranslateModule TranslateModule,
PipeModule,
PaginationModule
], ],
declarations: [ declarations: [
NotificationHistoryComponent, NotificationHistoryComponent,

View File

@ -19,18 +19,22 @@ import { Pipe, PipeTransform } from '@angular/core';
import { NotificationModel, NOTIFICATION_TYPE } from '../models/notification.model'; import { NotificationModel, NOTIFICATION_TYPE } from '../models/notification.model';
@Pipe({ @Pipe({
name: 'noticicationIcon' name: 'notificationIcon'
}) })
export class NotificationIconPipe implements PipeTransform { export class NotificationIconPipe implements PipeTransform {
transform(notification: NotificationModel): string { transform(notification: NotificationModel): string {
switch (notification.type) { if (notification.icon) {
case NOTIFICATION_TYPE.ERROR: return notification.icon;
return 'error'; } else {
case NOTIFICATION_TYPE.WARN: switch (notification.type) {
return 'warning'; case NOTIFICATION_TYPE.ERROR:
default: return 'error';
return 'info'; case NOTIFICATION_TYPE.WARN:
return 'warning';
default:
return 'info';
}
} }
} }
} }

View File

@ -102,6 +102,14 @@ export class NotificationService {
return this.snackBar.dismiss(); return this.snackBar.dismiss();
} }
/**
* Push new notification to Notification History
* @param notification - Notification model to be pushed.
*/
pushToNotificationHistory(notification: NotificationModel) {
this.notifications$.next(notification);
}
private dispatchNotification(message: string, action?: string, config?: number | MatSnackBarConfig, interpolateArgs?: any): MatSnackBarRef<any> { private dispatchNotification(message: string, action?: string, config?: number | MatSnackBarConfig, interpolateArgs?: any): MatSnackBarRef<any> {
const translatedMessage: string = this.translationService.instant(message, interpolateArgs); const translatedMessage: string = this.translationService.instant(message, interpolateArgs);
const translatedAction: string = this.translationService.instant(action, interpolateArgs); const translatedAction: string = this.translationService.instant(action, interpolateArgs);

View File

@ -38,6 +38,7 @@
@import '../../core/search-text/search-text-input.component'; @import '../../core/search-text/search-text-input.component';
@import './snackbar'; @import './snackbar';
@import '../directives/tooltip-card/tooltip-card.component'; @import '../directives/tooltip-card/tooltip-card.component';
@import '../notifications/components/notification-history.component';
@mixin adf-core-theme($theme) { @mixin adf-core-theme($theme) {
@include adf-colors-theme($theme); @include adf-colors-theme($theme);
@ -81,4 +82,5 @@
@include mat-datetimepicker-theme--fix($theme); @include mat-datetimepicker-theme--fix($theme);
@include adf-search-text-input-theme($theme); @include adf-search-text-input-theme($theme);
@include adf-tooltip-card-directive($theme); @include adf-tooltip-card-directive($theme);
@include adf-notification-history-theme($theme);
} }

View File

@ -21,14 +21,14 @@ import { BrowserVisibility } from '../utils/browser-visibility';
export class NotificationHistoryPage { export class NotificationHistoryPage {
notificationList = element(by.css('#adf-notification-history-list')); notificationList = element(by.css('.adf-notification-history-list'));
async clickNotificationButton(): Promise<void> { async clickNotificationButton(): Promise<void> {
await BrowserActions.clickExecuteScript('#adf-notification-history-open-button'); await BrowserActions.clickExecuteScript('#adf-notification-history-open-button');
} }
async clickMarkAsRead(): Promise<void> { async clickMarkAsRead(): Promise<void> {
await BrowserActions.click(element(by.css('#adf-notification-history-mark-as-read'))); await BrowserActions.click(element(by.id('adf-notification-history-mark-as-read')));
} }
async checkNotificationIsPresent(text: string): Promise<void> { async checkNotificationIsPresent(text: string): Promise<void> {