Skip to content

Commit

Permalink
Merge pull request #218 from italia/145-add-docs-and-tests-for-compon…
Browse files Browse the repository at this point in the history
…ent-notification

chore(docs): added docs for the notifications component
  • Loading branch information
astagi authored Feb 27, 2023
2 parents eb724a1 + 9a03f43 commit 4f87263
Show file tree
Hide file tree
Showing 26 changed files with 643 additions and 48 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
<div *ngFor="let notification of notifications; let i = index"
<div *ngFor="let notification of notifications"
[id]="notification.id"
class="notification {{notification.position}} {{notification.type}}"
[class.with-icon]="notification.type !== NotificationType.Standard"
[class.dismissable]="notification.dismissable"
[class.with-icon]="!!notification.icon"
[class.dismissable]="notification.dismissible"
role="alert" [attr.aria-labelledby]="notification.id+'-title'">

<h2 [id]="notification.id+'-title'" class="h5">
<it-icon *ngIf="!!notification.icon" [name]="notification.icon"></it-icon>
<ng-container>{{notification.title}}</ng-container>
</h2>
<p *ngIf="notification.message">{{notification.message}}</p>

<button *ngIf="notification.dismissable" type="button" class="btn notification-close"
<button *ngIf="notification.dismissible" type="button" class="btn notification-close"
(click)="hideNotification(notification.id)">
<it-icon name="close"></it-icon>
<span class="visually-hidden">{{'it.core.close-notification'|translate:{title: notification.title} }}</span>
<span class="visually-hidden">{{'it.core.close-notification'|translate:{ title: notification.title } }}</span>
</button>
</div>
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Component, Input, OnDestroy } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { NotificationsService } from '../../../services/notifications/notifications.service';
import { Notification, NotificationPosition, NotificationType } from '../../../interfaces/core';

import { Notification as BSNotification } from 'bootstrap-italia';
import { BooleanInput, isTrueBooleanInput } from '../../../utils/boolean-input';
import { IconName } from '../../../interfaces/icon';

@Component({
selector: 'it-notifications',
templateUrl: './notifications.component.html',
styleUrls: ['./notifications.component.scss']
styleUrls: ['./notifications.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NotificationsComponent implements OnDestroy {

/**
* Default notifications duration
* Default notifications duration (milliseconds)
* @default 8000
*/
@Input() duration: number = 8000;
Expand All @@ -23,10 +25,18 @@ export class NotificationsComponent implements OnDestroy {
*/
@Input() position?: NotificationPosition;

/**
* Default notifications is dismissible
* @default true
*/
@Input() dismissible?: BooleanInput = true;

private subscription: Subscription;
notifications: Array<Notification & { id: string }> = [];
private notificationCount: number = 0;
protected notifications: Array<Notification & { id: string }> = [];

constructor(
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _notificationService: NotificationsService
) {
this.subscription = this._notificationService.onNotification().subscribe(notification => {
Expand All @@ -36,12 +46,19 @@ export class NotificationsComponent implements OnDestroy {
if (!notification.position && this.position) {
notification.position = this.position; // Add position if not is set
}
if (notification.dismissible === undefined && isTrueBooleanInput(this.dismissible)) {
notification.dismissible = true; // Add dismissible if not is set
}
if (!notification.icon) {
notification.icon = this.getNotificationIcon(notification);
}

const newNotification = {
...notification,
id: `${notification.type}-${this.notifications.length}-notification`
id: `${notification.type}-${this.notificationCount++}-notification`
};
const index = this.notifications.push(newNotification);
this.notifications.push(newNotification);
this._changeDetectorRef.detectChanges();

setTimeout(() => {
// Show the notification
Expand All @@ -51,7 +68,14 @@ export class NotificationsComponent implements OnDestroy {

// Clear notification after the duration
setTimeout(() => {
this.notifications = this.notifications.splice(index, 1);
const index = this.notifications.findIndex(n => n.id === newNotification.id);
if (index > -1) {
this.notifications.splice(index, 1);
if (!this.notifications.length) {
this.notificationCount = 0;
}
this._changeDetectorRef.detectChanges();
}
}, notification.duration);
}, 200);
});
Expand All @@ -61,15 +85,36 @@ export class NotificationsComponent implements OnDestroy {
this.subscription.unsubscribe();
}

get NotificationType(): typeof NotificationType {
protected get NotificationType(): typeof NotificationType {
return NotificationType;
}

/**
* Hide the notification
* @param id
*/
hideNotification(id: string): void {
protected hideNotification(id: string): void {
BSNotification.getInstance(document.getElementById(id)!)?.hide();
}

/**
* Retrieve the icon name by notification type
* @param notification the notification
* @protected
*/
private getNotificationIcon(notification: Notification): IconName | undefined {
switch (notification.type) {
case NotificationType.Success:
return 'check-circle';
case NotificationType.Error:
return 'close-circle';
case NotificationType.Warning:
return 'error';
case NotificationType.Info:
return 'info-circle';
case NotificationType.Standard:
default:
return undefined;
}
}
}
33 changes: 32 additions & 1 deletion projects/design-angular-kit/src/lib/interfaces/core.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { IconName } from './icon';

export type AlertColor = 'info' | 'success' | 'warning' | 'danger';

export type ButtonColor =
Expand Down Expand Up @@ -56,12 +58,41 @@ export type DropdownDirection = 'dropup' | 'dropend' | 'dropstart';
export type CarouselType = 'default' | 'three-cols' | 'three-cols-arrow-visible';

export interface Notification {

/**
* Notification type
*/
type: NotificationType;

/**
* Notification title
*/
title: string;

/**
* Notification message / text
*/
message?: string;

/**
* Custom duration of notification
*/
duration?: number;
dismissable?: boolean;

/**
* The close notification button appears
*/
dismissible?: boolean;

/**
* Custom position of notification
*/
position?: NotificationPosition;

/**
* Custom icon of notification
*/
icon?: IconName;
}

export enum NotificationType {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import {Injectable} from '@angular/core';
import {filter, Observable, Subject} from "rxjs";
import {Notification, NotificationPosition, NotificationType} from "../../interfaces/core";
import { Injectable } from '@angular/core';
import { filter, Observable, Subject } from 'rxjs';
import { Notification, NotificationPosition, NotificationType } from '../../interfaces/core';

@Injectable({
providedIn: 'root'
})
@Injectable({ providedIn: 'root' })
export class NotificationsService {
private subject = new Subject<Notification>();

/**
* Listen on notification arrived
* @param filterType filter type of notification
*/
onNotification(filterType?: NotificationType): Observable<Notification> {
public onNotification(filterType?: NotificationType): Observable<Notification> {
return this.subject.asObservable().pipe(
filter(n => n && (!filterType || (n.type === filterType)))
);
Expand All @@ -22,25 +20,25 @@ export class NotificationsService {
* Show new notification
* @param notification notification
*/
addNotification(notification: Notification): void {
public addNotification(notification: Notification): void {
this.subject.next(notification);
}

/**
* Create new Standard notification
* @param title notification title
* @param message notification message
* @param dismissable notification dismissable
* @param duration notification duration (millis)
* @param dismissible notification dismissible
* @param duration notification duration (milliseconds)
* @param position notification position
*/
standard(title: string, message?: string, dismissable = true, duration?: number, position?: NotificationPosition): void {
public standard(title: string, message?: string, dismissible?:boolean, duration?: number, position?: NotificationPosition): void {
this.addNotification({
type: NotificationType.Standard,
message,
title,
duration,
dismissable,
dismissible,
position
});
}
Expand All @@ -49,17 +47,17 @@ export class NotificationsService {
* Create new Success notification
* @param title notification title
* @param message notification message
* @param dismissable notification dismissable
* @param duration notification duration (millis)
* @param dismissible notification dismissible
* @param duration notification duration (milliseconds)
* @param position notification position
*/
success(title: string, message?: string, dismissable = true, duration?: number, position?: NotificationPosition): void {
public success(title: string, message?: string, dismissible?:boolean, duration?: number, position?: NotificationPosition): void {
this.addNotification({
type: NotificationType.Success,
message,
title,
duration,
dismissable,
dismissible,
position
});
}
Expand All @@ -68,17 +66,17 @@ export class NotificationsService {
* Create new Error notification
* @param title notification title
* @param message notification message
* @param dismissable notification dismissable
* @param duration notification duration (millis)
* @param dismissible notification dismissible
* @param duration notification duration (milliseconds)
* @param position notification position
*/
error(title: string, message?: string, dismissable = true, duration?: number, position?: NotificationPosition): void {
public error(title: string, message?: string, dismissible?:boolean, duration?: number, position?: NotificationPosition): void {
this.addNotification({
type: NotificationType.Error,
message,
title,
duration,
dismissable,
dismissible,
position
});
}
Expand All @@ -87,17 +85,17 @@ export class NotificationsService {
* Create new Warning notification
* @param title notification title
* @param message notification message
* @param dismissable notification dismissable
* @param duration notification duration (millis)
* @param dismissible notification dismissible
* @param duration notification duration (milliseconds)
* @param position notification position
*/
warning(title: string, message?: string, dismissable = true, duration?: number, position?: NotificationPosition): void {
public warning(title: string, message?: string, dismissible?:boolean, duration?: number, position?: NotificationPosition): void {
this.addNotification({
type: NotificationType.Warning,
message,
title,
duration,
dismissable,
dismissible,
position
});
}
Expand All @@ -106,17 +104,17 @@ export class NotificationsService {
* Create new Info notification
* @param title notification title
* @param message notification message
* @param dismissable notification dismissable
* @param duration notification duration (millis)
* @param dismissible notification dismissible
* @param duration notification duration (milliseconds)
* @param position notification position
*/
info(title: string, message?: string, dismissable = true, duration?: number, position?: NotificationPosition): void {
public info(title: string, message?: string, dismissible?:boolean, duration?: number, position?: NotificationPosition): void {
this.addNotification({
type: NotificationType.Info,
message,
title,
duration,
dismissable,
dismissible,
position
});
}
Expand Down
1 change: 1 addition & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const routes: Routes = [
{ path: 'callout', loadChildren: () => import('src/app/callout/callout.module').then(m => m.CalloutModule) },
{ path: 'upload', loadChildren: () => import('src/app/upload/upload.module').then(m => m.UploadModule) },
{ path: 'steppers', loadChildren: () => import('src/app/steppers/steppers.module').then(m => m.SteppersModule) },
{ path: 'notifications', loadChildren: () => import('src/app/notifications/notifications.module').then(m => m.NotificationsModule) },
]}
];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<h3>Tipi di notifica</h3>

<div class="bd-example">
<div class="d-flex mb-2">
<button class="btn-me" itButton size="sm" (click)="standardNotification()">
Notifica Standard
</button>
<button class="btn-me" itButton="success" size="sm" (click)="successNotification()">
Notifica Successo
</button>
<button class="btn-me" itButton="danger" size="sm" (click)="errorNotification()">
Notifica Errore
</button>
<button class="btn-me" itButton="warning" size="sm" (click)="warningNotification()">
Notifica Precauzione
</button>
<button class="btn-me" itButton="info" size="sm" (click)="infoNotification()">
Notifica Info
</button>
</div>
</div>

Loading

1 comment on commit 4f87263

@vercel
Copy link

@vercel vercel bot commented on 4f87263 Feb 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.