Skip to content

Commit

Permalink
Merge pull request #219 from Stabzs/feat/trustedHtml_pipe
Browse files Browse the repository at this point in the history
feat(trustedHtml-pipe)
  • Loading branch information
Stabzs authored Dec 1, 2020
2 parents 530dc32 + 2b5a878 commit d666c60
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 62 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ to `toaster-config.defaultToastType`.
correctly changed to `info` instead of `toast-info` for the fallback case.
* **toaster.css:** The close button positioning has been corrected.
Closes [#192](https://github.com/Stabzs/Angular2-Toaster/issues/192).
* **toast.component:** The `BodyOutputType.TrustedHtml` body content did not properly update on change. A pipe has been
added at the suggestion of @rmeshksar to force re-rendering of the content if it changes. Replaces
[#185]](https://github.com/Stabzs/Angular2-Toaster/pull/185).


# 10.0.0
Expand Down
57 changes: 52 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ largely based off of [AngularJS-Toaster](https://github.com/jirikavi/AngularJS-T
[![Build Status](https://travis-ci.org/Stabzs/Angular2-Toaster.svg?branch=master)](https://travis-ci.org/Stabzs/Angular2-Toaster)
[![Coverage Status](https://coveralls.io/repos/github/Stabzs/Angular2-Toaster/badge.svg?branch=master&b=8.0.0)](https://coveralls.io/github/Stabzs/Angular2-Toaster?branch=master)

Version ^11.0.0 has a number of new features, type definitions, and break changesing. Please review the
[CHANGELOG](CHANGELOG.md/#11.0.0) for a list of features and breaking changes before upgrading.

Version ^5.0.0 requires either `.forRoot()` or `.forChild()` `ToasterModule` inclusion. Please
read the 5.x.x release notes and the `Getting Started` section before upgraded.
read the 5.x.x release notes and the `Getting Started` section before upgrading.

Version ^4.0.0 now supports @angular/animations, which is a breaking change.
Please read both the Getting Started and Animations sections before upgrading.
Expand Down Expand Up @@ -257,16 +259,47 @@ If you need to target a non-supported browser, a [polyfill](https://github.com/w
# Configurable Options
### Toast Types
By default, five toast types are defined: 'error, 'info', 'wait', 'success', and 'warning'.
By default, five toast types are defined via the `ToastType` type: 'error, 'info', 'wait', 'success', and 'warning'.
The default options can be overridden by passing a mapping object to the config, where the key corresponds to the toast type and the value corresponds to a custom style:
The existing toast type configurations can be overridden by passing a mapping object that uses the
same type names but overrides the style class:
```typescript
template:
`<toaster-container [toasterconfig]="config"></toaster-container>`

public config: ToasterConfig =
new ToasterConfig({typeClasses: {'partial-success': '.toaster-partial-success' }});
new ToasterConfig({typeClasses: {
error: 'custom-toast-error',
info: 'custom-toast-info',
wait: 'custom-toast-wait',
success: 'custom-toast-success',
warning: 'custom-toast-warning'
}});
```
In addition, the default options can be overridden, replaced, or expanded, by extending the toast type with a
custom type and passing a mapping object to the config, where the key corresponds to the toast type and the
value corresponds to a custom class:
**NOTE: When providing a custom type, both the typeClasses and iconClasses objects must be updated.
In the case where either are not provided, the toast type will fall back to the `defaultToastType` which
defaults to `info`.**
```typescript
import {DefaultTypeClasses, DefaultIconClasses} from 'angular2-toaster';
type ExtendedToastType = ('partial-success') & ToastType;

template:
`<toaster-container [toasterconfig]="config"></toaster-container>`

extendedTypeClasses = { ...DefaultTypeClasses, ...{ 'partial-success': 'toast-partial-success' }};
extendedIconClasses = { ...DefaultIconClasses, ...{ 'partial-success': 'icon-partial-success' }};

public config: ToasterConfig =
new ToasterConfig({
typeClasses: <ExtendedToastType>this.extendedTypeClasses,
iconClasses: <ExtendedToastType>this.extendedIconClasses
});
```
### Animations
Expand Down Expand Up @@ -548,6 +581,20 @@ var toast: Toast = {
this.toasterService.pop(toast);
```
### On Click Callback
An onClick callback function can be attached to each toast instance. The callback will be invoked upon the toast being
clicked, even if the click is the close button. The callback will be invoked before the toast is removed.
```typescript
var toast: Toast = {
type: 'success',
title: 'parent',
onClickCallback: (toast) => this.toasterService.pop('success', 'invoked from ' + toast.title + ' onClick callback')
};

this.toasterService.pop(toast);
```
# Building the Source
In order to build Angular2-Toaster for development, you will need to have Git and Node.js installed.
Expand All @@ -570,7 +617,7 @@ npm run build
Run Karma test instance with coverage report:
```bash
npm run test
ng test angular2-toaster --code-coverage
```
# Frequently Asked Questions and Issues
Expand Down
6 changes: 4 additions & 2 deletions src/angular2-toaster/src/lib/toast.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ describe('ToastComponent', () => {
component['removeToastTick'] = new Date().getTime() + 75;

component['updateProgressBar']();
expect(component.progressBarWidth).toBe(75);
expect(component.progressBarWidth).toBeGreaterThan(70);
expect(component.progressBarWidth).toBeLessThan(80);
});

it('should invert progressBarWidth if progressBarDirection is increasing', () => {
Expand All @@ -299,7 +300,8 @@ describe('ToastComponent', () => {
component['removeToastTick'] = new Date().getTime() + 75;

component['updateProgressBar']();
expect(component.progressBarWidth).toBe(25);
expect(component.progressBarWidth).toBeGreaterThan(20);
expect(component.progressBarWidth).toBeLessThan(30);
});

it('should set progressBarWidth to 0 if offset is less than 0', () => {
Expand Down
15 changes: 2 additions & 13 deletions src/angular2-toaster/src/lib/toast.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
ElementRef,
Renderer2
} from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { Toast } from './toast';
import { BodyOutputType } from './bodyOutputType';
import { ToasterConfig } from './toaster-config';
Expand All @@ -27,12 +26,12 @@ import { ToasterConfig } from './toaster-config';
<div [ngClass]="titleClass">{{toast.title}}</div>
<div [ngClass]="messageClass" [ngSwitch]="toast.bodyOutputType">
<div *ngSwitchCase="bodyOutputType.Component" #componentBody></div>
<div *ngSwitchCase="bodyOutputType.TrustedHtml" [innerHTML]="safeBodyHtml"></div>
<div *ngSwitchCase="bodyOutputType.TrustedHtml" [innerHTML]="toast.body | trustHtml"></div>
<div *ngSwitchCase="bodyOutputType.Default">{{toast.body}}</div>
</div>
</div>
<button class="toast-close-button" *ngIf="toast.showCloseButton" (click)="click($event, toast)"
[innerHTML]="safeCloseHtml">
[innerHTML]="toast.closeHtml | trustHtml">
</button>
<div *ngIf="toast.progressBar">
<div class="toast-progress-bar" [style.width]="progressBarWidth + '%'"></div>
Expand All @@ -45,8 +44,6 @@ export class ToastComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() messageClass: string;
@ViewChild('componentBody', { read: ViewContainerRef, static: false }) componentBody: ViewContainerRef;

public safeCloseHtml: SafeHtml;
public safeBodyHtml: SafeHtml;
public progressBarWidth: number = -1;
public bodyOutputType = BodyOutputType;

Expand All @@ -63,7 +60,6 @@ export class ToastComponent implements OnInit, AfterViewInit, OnDestroy {
private removeMouseOverListener: () => void;

constructor(
private sanitizer: DomSanitizer,
private componentFactoryResolver: ComponentFactoryResolver,
private changeDetectorRef: ChangeDetectorRef,
private ngZone: NgZone,
Expand All @@ -72,13 +68,6 @@ export class ToastComponent implements OnInit, AfterViewInit, OnDestroy {
) {}

ngOnInit() {
if (this.toast.closeHtml) {
this.safeCloseHtml = this.sanitizer.bypassSecurityTrustHtml(this.toast.closeHtml);
}
if (this.toast.bodyOutputType === BodyOutputType.TrustedHtml) {
this.safeBodyHtml = this.sanitizer.bypassSecurityTrustHtml(this.toast.body);
}

if (this.toast.progressBar) {
this.toast.progressBarDirection = this.toast.progressBarDirection || 'decreasing';
}
Expand Down
32 changes: 18 additions & 14 deletions src/angular2-toaster/src/lib/toaster-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ import { Injectable } from '@angular/core';
import { BodyOutputType } from './bodyOutputType';
import { ToastType } from './toast';

export const DefaultTypeClasses : { [key in ToastType] } = {
error: 'toast-error',
info: 'toast-info',
wait: 'toast-wait',
success: 'toast-success',
warning: 'toast-warning'
};

export const DefaultIconClasses : { [key in ToastType] } = {
error: 'icon-error',
info: 'icon-info',
wait: 'icon-wait',
success: 'icon-success',
warning: 'icon-warning'
};

export interface IToasterConfig {
limit?: number|null;
tapToDismiss?: boolean;
Expand Down Expand Up @@ -60,20 +76,8 @@ export class ToasterConfig implements IToasterConfig {
this.closeHtml = configOverrides.closeHtml || '<span>&times;</span>';
this.newestOnTop = configOverrides.newestOnTop != null ? configOverrides.newestOnTop : true;
this.timeout = configOverrides.timeout != null ? configOverrides.timeout : 5000;
this.typeClasses = configOverrides.typeClasses || {
error: 'toast-error',
info: 'toast-info',
wait: 'toast-wait',
success: 'toast-success',
warning: 'toast-warning'
};
this.iconClasses = configOverrides.iconClasses || {
error: 'icon-error',
info: 'icon-info',
wait: 'icon-wait',
success: 'icon-success',
warning: 'icon-warning'
};
this.typeClasses = configOverrides.typeClasses || DefaultTypeClasses;
this.iconClasses = configOverrides.iconClasses || DefaultIconClasses;
this.bodyOutputType = configOverrides.bodyOutputType || BodyOutputType.Default;
this.bodyTemplate = configOverrides.bodyTemplate || 'toasterBodyTmpl.html';
this.defaultToastType = configOverrides.defaultToastType || 'info';
Expand Down
6 changes: 2 additions & 4 deletions src/angular2-toaster/src/lib/toaster-container.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,10 @@ export class ToasterContainerComponent implements OnInit, OnDestroy {
}

private clearToasts(clearWrapper: IClearWrapper) {
const toastId = clearWrapper.toastId;
const toastId = clearWrapper.toastId ;
const toastContainerId = clearWrapper.toastContainerId;

if (this.isNullOrUndefined(toastContainerId)) {
this.clearToastsAction(toastId);
} else if (toastContainerId === this.toasterconfig.toastContainerId) {
if (this.isNullOrUndefined(toastContainerId) || (toastContainerId === this.toasterconfig.toastContainerId)) {
this.clearToastsAction(toastId);
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/angular2-toaster/src/lib/toaster.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { CommonModule } from '@angular/common';
import { ToastComponent } from './toast.component';
import { ToasterContainerComponent } from './toaster-container.component';
import { ToasterService } from './toaster.service';
import { TrustHtmlPipe } from './trust-html.pipe';

@NgModule({
imports: [CommonModule],
declarations: [
ToastComponent,
ToasterContainerComponent
ToasterContainerComponent,
TrustHtmlPipe
],
exports: [
ToasterContainerComponent,
Expand Down
15 changes: 15 additions & 0 deletions src/angular2-toaster/src/lib/trust-html.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

@Pipe({
name: 'trustHtml',
pure: true
})
export class TrustHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {
}

transform(content: any): SafeHtml {
return this.sanitizer.bypassSecurityTrustHtml(content);
}
}
8 changes: 2 additions & 6 deletions src/demo/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -365,12 +365,8 @@ <h2>Resources</h2>
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
</a>

<a class="card" target="_blank" rel="noopener" href="https://blog.angular.io/">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"/></svg>

<span>Angular Blog</span>

<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
<a class="card">
<button (click)="clear()">clear toasts</button>
</a>

</div>
Expand Down
27 changes: 10 additions & 17 deletions src/demo/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import {
ToasterConfig, IToasterConfig, ToasterService, Toast, ToastType
ToasterConfig, IToasterConfig, ToasterService, Toast, ToastType, DefaultTypeClasses, DefaultIconClasses
} from '../../../angular2-toaster/src/public-api';

@Component({
Expand All @@ -11,25 +11,14 @@ import {
export class AppComponent {
title = 'demo';

extendedTypeClasses = { ...DefaultTypeClasses, ...{ customtype: 'toast-success' }};
extendedIconClasses = { ...DefaultIconClasses, ...{ customtype: 'icon-error' }};

appConfig: IToasterConfig = new ToasterConfig({
animation: 'fade', newestOnTop: true, positionClass: 'toast-bottom-right',
toastContainerId: 1, timeout: 0, showCloseButton: true, // mouseoverTimerStop: true
typeClasses: <ExtendedToastType>{
customtype: 'toast-success',
error: 'toast-error',
info: 'toast-info',
wait: 'toast-wait',
success: 'toast-success',
warning: 'toast-warning'
},
iconClasses: <ExtendedToastType>{
customtype: 'icon-error',
error: 'icon-error',
info: 'icon-info',
wait: 'icon-wait',
success: 'icon-success',
warning: 'icon-warning'
}
typeClasses: <ExtendedToastType>this.extendedTypeClasses,
iconClasses: <ExtendedToastType>this.extendedIconClasses
});

testConfig: IToasterConfig = new ToasterConfig({
Expand Down Expand Up @@ -97,6 +86,10 @@ export class AppComponent {
};
this.toasterService.pop(toast);
}

clear() {
this.toasterService.clear();
}
}

type ExtendedToastType = ('customtype' | 'bad value') & ToastType;

0 comments on commit d666c60

Please sign in to comment.