Skip to content

Commit

Permalink
feat(alert): remove self-closing alert component
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the `NgbSelfClosingAlert` component has been removed.
Check the self-closing alert demo to know how to achieve the same thing with `NgbAlert`.

Closes #758
  • Loading branch information
jnizet authored and pkozlowski-opensource committed Sep 17, 2016
1 parent 21eb610 commit 79e393d
Show file tree
Hide file tree
Showing 11 changed files with 40 additions and 230 deletions.
2 changes: 0 additions & 2 deletions demo/src/app/components/alert/alert.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import {DEMO_SNIPPETS} from './demos';
template: `
<ngbd-content-wrapper component="Alert">
<ngbd-api-docs directive="NgbAlert"></ngbd-api-docs>
<ngbd-api-docs directive="NgbSelfClosingAlert"></ngbd-api-docs>
<ngbd-api-docs-config type="NgbAlertConfig"></ngbd-api-docs-config>
<ngbd-api-docs-config type="NgbSelfClosingAlertConfig"></ngbd-api-docs-config>
<ngbd-example-box demoTitle="Basic Alert" [htmlSnippet]="snippets.basic.markup" [tsSnippet]="snippets.basic.code">
<ngbd-alert-basic></ngbd-alert-basic>
</ngbd-example-box>
Expand Down
6 changes: 0 additions & 6 deletions demo/src/app/components/alert/demos/config/alert-config.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,3 @@
This alert's type is success and it's not dismissible because the config has been customized
</ngb-alert>
</p>
<p *ngFor="let alert of alerts">
<template ngbAlert>{{ alert }}</template>
</p>
<p>
<button class="btn btn-primary" (click)="addAlert()">Add self-closing alert</button>
</p>
15 changes: 4 additions & 11 deletions demo/src/app/components/alert/demos/config/alert-config.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import {Component, Input} from '@angular/core';
import {NgbAlertConfig, NgbSelfClosingAlertConfig} from '@ng-bootstrap/ng-bootstrap';
import {NgbAlertConfig} from '@ng-bootstrap/ng-bootstrap';

@Component({
selector: 'ngbd-alert-config',
templateUrl: './alert-config.html',
// add NgbProgressbarConfig and NgbSelfClosingAlertConfig to the component providers
providers: [NgbAlertConfig, NgbSelfClosingAlertConfig]
// add NgbAlertConfig to the component providers
providers: [NgbAlertConfig]
})
export class NgbdAlertConfig {
@Input() public alerts: Array<string> = [];

constructor(alertConfig: NgbAlertConfig, selfClosingAlertConfig: NgbSelfClosingAlertConfig) {
constructor(alertConfig: NgbAlertConfig) {
// customize default values of alerts used by this component tree
alertConfig.type = 'success';
alertConfig.dismissible = false;
selfClosingAlertConfig.dismissible = true;
selfClosingAlertConfig.dismissOnTimeout = 5000;
selfClosingAlertConfig.type = 'danger';
}

public addAlert() {
this.alerts.push('This alert has type danger and will close automatically after 5 seconds thanks to the custom config');
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
<p>
These alerts will be dismissed in 5 seconds. Click "Add Alert" to add an alert.
Static self-closing alert that disappears after 20 seconds (refresh the page if it has already disappeared)
</p>
<p *ngFor="let alert of alerts">
<template ngbAlert [dismissOnTimeout]="5000">{{ alert }}</template>
<ngb-alert *ngIf="!staticAlertClosed" (close)="staticAlertClosed = true">Check out our awesome new features!</ngb-alert>

<hr/>

<p>
Show a self-closing success message that disappears after 5 seconds.
</p>
<ngb-alert *ngIf="successMessage" type="success" (close)="successMessage = null">{{ successMessage }}</ngb-alert>
<p>
<button class="btn btn-primary" (click)="addAlert()">Add Alert</button>
<button class="btn btn-primary" (click)="changeSuccessMessage()">Change message</button>
</p>
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import {Component, Input} from '@angular/core';
import {Component, Input, OnInit} from '@angular/core';
import {Subject} from 'rxjs/Rx';

@Component({
selector: 'ngbd-alert-selfclosing',
templateUrl: './alert-selfclosing.html'
})
export class NgbdAlertSelfclosing {
@Input()
public alerts: Array<string> = [];
export class NgbdAlertSelfclosing implements OnInit {
private _success = new Subject<string>();

public addAlert() {
this.alerts.push('This alert will close automatically after 5 seconds');
staticAlertClosed = false;
successMessage: string;

ngOnInit(): void {
setTimeout(() => this.staticAlertClosed = true, 20000);

this._success.subscribe((message) => this.successMessage = message);
this._success.debounceTime(5000).subscribe(() => this.successMessage = null);
}

public changeSuccessMessage() {
this._success.next(`${new Date()} - Message successfully changed.`);
}
}
12 changes: 1 addition & 11 deletions src/alert/alert-config.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {NgbAlertConfig, NgbSelfClosingAlertConfig} from './alert-config';
import {NgbAlertConfig} from './alert-config';

describe('ngb-alert-config', () => {
it('should have sensible default values', () => {
Expand All @@ -8,13 +8,3 @@ describe('ngb-alert-config', () => {
expect(config.type).toBe('warning');
});
});

describe('ngb-self-closing-alert-config', () => {
it('should have sensible default values', () => {
const config = new NgbSelfClosingAlertConfig();

expect(config.dismissible).toBe(false);
expect(config.type).toBe('warning');
expect(config.dismissOnTimeout).toBeUndefined();
});
});
12 changes: 0 additions & 12 deletions src/alert/alert-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,3 @@ export class NgbAlertConfig {
dismissible = true;
type = 'warning';
}

/**
* Configuration service for the NgbSelfClosingAlert component.
* You can inject this service, typically in your root component, and customize the values of its properties in
* order to provide default values for all the self-closing alerts used in the application.
*/
@Injectable()
export class NgbSelfClosingAlertConfig {
dismissible = false;
type = 'warning';
dismissOnTimeout: number;
}
6 changes: 3 additions & 3 deletions src/alert/alert.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';

import {NGB_ALERT_DIRECTIVES, NgbAlert} from './alert';
import {NgbAlertConfig, NgbSelfClosingAlertConfig} from './alert-config';
import {NgbAlertConfig} from './alert-config';

export {NgbAlertConfig, NgbSelfClosingAlertConfig} from './alert-config';
export {NgbAlertConfig} from './alert-config';

@NgModule({
declarations: NGB_ALERT_DIRECTIVES,
exports: NGB_ALERT_DIRECTIVES,
imports: [CommonModule],
entryComponents: [NgbAlert],
providers: [NgbAlertConfig, NgbSelfClosingAlertConfig]
providers: [NgbAlertConfig]
})
export class NgbAlertModule {
}
112 changes: 4 additions & 108 deletions src/alert/alert.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {fakeAsync, tick, TestBed, ComponentFixture, inject} from '@angular/core/testing';
import {TestBed, ComponentFixture, inject} from '@angular/core/testing';
import {createGenericTestComponent} from '../test/common';

import {Component, ViewChild} from '@angular/core';
import {Component} from '@angular/core';

import {NgbAlertModule} from './alert.module';
import {NgbAlert, NgbSelfClosingAlert} from './alert';
import {NgbAlertConfig, NgbSelfClosingAlertConfig} from './alert-config';
import {NgbAlert} from './alert';
import {NgbAlertConfig} from './alert-config';

const createTestComponent = (html: string) =>
createGenericTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
Expand Down Expand Up @@ -91,112 +91,8 @@ describe('ngb-alert', () => {
});
});

describe('NgbSelfClosingAlert', () => {

describe('UI logic', () => {
beforeEach(() => { TestBed.configureTestingModule({declarations: [TestComponent], imports: [NgbAlertModule]}); });

it('should open a self-closing alert with default type and no close button', () => {
const defaultConfig = new NgbSelfClosingAlertConfig();
const fixture = createTestComponent(`<template ngbAlert>Hello, {{name}}!</template>`);
const alertEl = getAlertElement(fixture.nativeElement);

expect(alertEl).toHaveCssClass(`alert-${defaultConfig.type}`);
expect(alertEl.getAttribute('role')).toEqual('alert');
expect(getCloseButton(alertEl)).toBeFalsy();
});

it('should open a self-closing alert with a specified type', () => {
const fixture = createTestComponent(`<template ngbAlert type="success">Hello, {{name}}!</template>`);
const alertEl = getAlertElement(fixture.nativeElement);

expect(alertEl).toHaveCssClass('alert-success');
});

it('should dismiss alert and invoke close handler on close button click', () => {
const fixture = createTestComponent(
`<template ngbAlert (close)="closed = true" [dismissible]="true">Hello, {{name}}!</template>`);
const alertEl = getAlertElement(fixture.nativeElement);

getCloseButton(alertEl).click();
fixture.detectChanges();

expect(fixture.componentInstance.closed).toBeTruthy();
expect(getAlertElement(fixture.nativeElement)).toBeNull();
});

it('should auto close on timeout specified', fakeAsync(() => {
const fixture =
createTestComponent(`<template ngbAlert [dismissOnTimeout]="1000">Hello, {{name}}!</template>`);
const alertEl = getAlertElement(fixture.nativeElement);

tick(800);
fixture.detectChanges();
expect(alertEl.getAttribute('role')).toEqual('alert');

tick(1200);
fixture.detectChanges();
expect(getAlertElement(fixture.nativeElement)).toBeNull();
}));
});

describe('Custom config', () => {

let config: NgbSelfClosingAlertConfig;

beforeEach(() => {
TestBed.configureTestingModule({declarations: [TestComponent], imports: [NgbAlertModule]});
TestBed.overrideComponent(TestComponent, {set: {template: '<template ngbAlert>Hello, {{name}}!</template>'}});
});

beforeEach(inject([NgbSelfClosingAlertConfig], (c: NgbSelfClosingAlertConfig) => {
config = c;
config.dismissible = true;
config.dismissOnTimeout = 2000;
config.type = 'success';
}));

it('should initialize inputs with provided config', () => {
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();

const alert = fixture.componentInstance.selfClosingAlert;

expect(alert.type).toBe(config.type);
expect(alert.dismissible).toBe(config.dismissible);
expect(alert.dismissOnTimeout).toBe(config.dismissOnTimeout);
});
});

describe('Custom config as provider', () => {
const config = new NgbSelfClosingAlertConfig();
config.dismissible = true;
config.dismissOnTimeout = 2000;
config.type = 'success';

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
imports: [NgbAlertModule],
providers: [{provide: NgbSelfClosingAlertConfig, useValue: config}]
});
});

it('should initialize inputs with provided config as provider', () => {
const fixture = createTestComponent(`<template ngbAlert>Hello, {{name}}!</template>`);
const alert = fixture.componentInstance.selfClosingAlert;

expect(alert.type).toBe(config.type);
expect(alert.dismissible).toBe(config.dismissible);
expect(alert.dismissOnTimeout).toBe(config.dismissOnTimeout);
});
});
});

@Component({selector: 'test-cmp', template: '', entryComponents: [NgbAlert]})
class TestComponent {
name = 'World';
closed = false;

@ViewChild(NgbSelfClosingAlert) selfClosingAlert: NgbSelfClosingAlert;
}
68 changes: 2 additions & 66 deletions src/alert/alert.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
import {
Component,
Directive,
Input,
Output,
EventEmitter,
OnInit,
ChangeDetectionStrategy,
ViewContainerRef,
Injector,
OnDestroy,
ComponentFactoryResolver,
ComponentRef,
Renderer,
TemplateRef
} from '@angular/core';

import {PopupService} from '../util/popup';
import {NgbAlertConfig, NgbSelfClosingAlertConfig} from './alert-config';
import {NgbAlertConfig} from './alert-config';

/**
* Alerts can be used to provide feedback messages.
Expand Down Expand Up @@ -56,58 +46,4 @@ export class NgbAlert {
closeHandler() { this.close.emit(null); }
}

/**
* Alerts that can be dismissed without any additional code.
*/
@Directive({selector: 'template[ngbAlert]'})
export class NgbSelfClosingAlert implements OnInit, OnDestroy {
private _popupService: PopupService<NgbAlert>;
private _timeout;

/**
* A flag indicating if the alert can be dismissed (closed) by a user. If this flag is set, a close button (in a
* form of an ×) will be displayed.
*/
@Input() dismissible: boolean;
/**
* Alert type (CSS class). Bootstrap 4 recognizes the following types: "success", "info", "warning" and "danger".
*/
@Input() type: string;
/**
* An event emitted when the close button is clicked.
*/
@Output('close') closeEvent = new EventEmitter();
/**
* Time, in milliseconds, before the alert auto closes.
*/
@Input() dismissOnTimeout: number;

constructor(
private _templateRef: TemplateRef<Object>, viewContainerRef: ViewContainerRef, injector: Injector,
componentFactoryResolver: ComponentFactoryResolver, renderer: Renderer, config: NgbSelfClosingAlertConfig) {
this._popupService =
new PopupService<NgbAlert>(NgbAlert, injector, viewContainerRef, renderer, componentFactoryResolver);
this.dismissible = config.dismissible;
this.type = config.type;
this.dismissOnTimeout = config.dismissOnTimeout;
}

close(): void { this._popupService.close(); }

ngOnInit() {
const windowRef = this._popupService.open(this._templateRef);
windowRef.instance.type = this.type;
windowRef.instance.dismissible = this.dismissible;
windowRef.instance.close.subscribe(($event) => {
this.closeEvent.emit($event);
this.close();
});
if (this.dismissOnTimeout) {
this._timeout = setTimeout(() => { this.close(); }, this.dismissOnTimeout);
}
}

ngOnDestroy() { clearTimeout(this._timeout); }
}

export const NGB_ALERT_DIRECTIVES = [NgbAlert, NgbSelfClosingAlert];
export const NGB_ALERT_DIRECTIVES = [NgbAlert];
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {NgbTooltipModule} from './tooltip/tooltip.module';
import {NgbTypeaheadModule, NgbTypeaheadSelectItemEvent} from './typeahead/typeahead.module';

export {NgbAccordionModule, NgbPanelChangeEvent, NgbAccordionConfig} from './accordion/accordion.module';
export {NgbAlertModule, NgbAlertConfig, NgbSelfClosingAlertConfig} from './alert/alert.module';
export {NgbAlertModule, NgbAlertConfig} from './alert/alert.module';
export {NgbButtonsModule} from './buttons/radio.module';
export {NgbCarouselModule, NgbCarouselConfig} from './carousel/carousel.module';
export {NgbCollapseModule} from './collapse/collapse.module';
Expand Down

0 comments on commit 79e393d

Please sign in to comment.