Skip to content

Commit

Permalink
feat(alert): added isOpen input and marked as OnPush (#2940)
Browse files Browse the repository at this point in the history
* feat(alert): added isOpen input and marked as OnPush

* fix(alerts-test): fixed alert component test and few style issues

* demo(alert): add dissmissible toggle and dynamic content demo
  • Loading branch information
valorkin authored Nov 3, 2017
1 parent 1b6ed56 commit af7597b
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 30 deletions.
9 changes: 9 additions & 0 deletions demo/src/app/components/+alerts/alerts-section.list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DemoAlertLinkComponent } from './demos/link/link';
import { DemoAlertContentComponent } from './demos/content/content';
import { DemoAlertDismissComponent } from './demos/dismiss/dismiss';
import { DemoAlertDynamicHtmlComponent } from './demos/dynamic-html/dynamic-html';
import { DemoAlertDynamicContentComponent } from './demos/dynamic-content/dynamic-content';
import { DemoAlertTimeoutComponent } from './demos/dismiss-on-timeout/dismiss-on-timeout';
import { DemoAlertStylingGlobalComponent } from './demos/styling-global/styling-global';
import { DemoAlertStylingLocalComponent } from './demos/styling-local/styling-local';
Expand Down Expand Up @@ -80,6 +81,14 @@ export const demoComponentContent: ContentSection[] = [
html: require('!!raw-loader?lang=markup!./demos/dynamic-html/dynamic-html.html'),
outlet: DemoAlertDynamicHtmlComponent
},
{
title: 'Dynamic content',
anchor: 'dynamic-content',
description: `<p>Alerts fully support bindings.</p>`,
component: require('!!raw-loader?lang=typescript!./demos/dynamic-content/dynamic-content.ts'),
html: require('!!raw-loader?lang=markup!./demos/dynamic-content/dynamic-content.html'),
outlet: DemoAlertDynamicContentComponent
},
{
title: 'Dismiss on timeout',
anchor: 'dismiss-on-timeout',
Expand Down
3 changes: 2 additions & 1 deletion demo/src/app/components/+alerts/demos/dismiss/dismiss.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<div *ngFor="let alert of alerts">
<alert [type]="alert.type" [dismissible]="true">{{ alert.msg }}</alert>
<alert [type]="alert.type" [dismissible]="dismissible">{{ alert.msg }}</alert>
</div>
<button type="button" class="btn btn-primary" (click)="dismissible = !dismissible">Toggle dismissible</button>
<button type="button" class="btn btn-primary" (click)="reset()">Reset</button>
1 change: 1 addition & 0 deletions demo/src/app/components/+alerts/demos/dismiss/dismiss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Component } from '@angular/core';
templateUrl: './dismiss.html'
})
export class DemoAlertDismissComponent {
dismissible = true;
alerts: any = [
{
type: 'success',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<alert type="success">{{messages[index]}}</alert>

<div *ngIf="index !== messages.length -1; else elseBlock">
<button class="btn btn-primary" (click)="changeText()">Change text</button>
</div>
<ng-template #elseBlock>
<button class="btn btn-primary" (click)="index = 0">Reset</button>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component } from '@angular/core';

@Component({
selector: 'demo-alert-content-html',
templateUrl: './dynamic-content.html'
})
export class DemoAlertDynamicContentComponent {
index = 0;
messages = [
'You successfully read this important alert message.',
'Now this text is different from what it was before. Go ahead and click the button one more time',
'Well done! Click reset button and you\'ll see the first message'
];

changeText() {
if (this.messages.length - 1 !== this.index) {
this.index++;
}
}
}
2 changes: 2 additions & 0 deletions demo/src/app/components/+alerts/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { DemoAlertTimeoutComponent } from './dismiss-on-timeout/dismiss-on-timeo
import { DemoAlertStylingGlobalComponent } from './styling-global/styling-global';
import { DemoAlertStylingLocalComponent } from './styling-local/styling-local';
import { DemoAlertConfigComponent } from './config/config';
import { DemoAlertDynamicContentComponent } from './dynamic-content/dynamic-content';

export const DEMO_COMPONENTS = [
DemoAlertBasicComponent,
DemoAlertLinkComponent,
DemoAlertContentComponent,
DemoAlertDismissComponent,
DemoAlertDynamicHtmlComponent,
DemoAlertDynamicContentComponent,
DemoAlertTimeoutComponent,
DemoAlertStylingGlobalComponent,
DemoAlertStylingLocalComponent,
Expand Down
6 changes: 4 additions & 2 deletions demo/src/app/components/+pagination/demos/pager/pager.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Component } from '@angular/core';
import { Component, ViewEncapsulation } from '@angular/core';

@Component({
selector: 'demo-pagination-pager',
templateUrl: './pager.html'
templateUrl: './pager.html',
styles: ['.pager li.btn:active { box-shadow: none; }'],
encapsulation: ViewEncapsulation.None
})
export class DemoPaginationPagerComponent {
totalItems: number = 64;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { Component } from '@angular/core';
/* tslint:disable no-unused-css*/
styles: [
`
:host >>> .tooltip-inner {
background-color: #009688;
color: #fff;
}
:host >>> .tooltip .tooltip-arrow {
border-bottom-color: #009688;
}
`
:host >>> .tooltip-inner {
background-color: #009688;
color: #fff;
}
:host >>> .tooltip.top .tooltip-arrow:before,
:host >>> .tooltip.top .tooltip-arrow {
border-top-color: #009688;
}
`
]
})
export class DemoTooltipStylingLocalComponent {}
4 changes: 4 additions & 0 deletions demo/src/assets/css/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ a:hover {
.main {
display: flex;
flex-wrap: wrap;
position: relative;
z-index: 1;
}
/* entry */
.entry {
Expand Down Expand Up @@ -452,6 +454,8 @@ a:hover {

.nav-item {
&.customClass {
float: right;

&:first-child {
margin-left: auto;
}
Expand Down
2 changes: 1 addition & 1 deletion src/alert/alert.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<ng-template [ngIf]="!isClosed">
<ng-template [ngIf]="isOpen">
<div [class]="'alert alert-' + type" role="alert" [ngClass]="classes">
<ng-template [ngIf]="dismissible">
<button type="button" class="close" aria-label="Close" (click)="close()">
Expand Down
25 changes: 16 additions & 9 deletions src/alert/alert.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit,
Output
} from '@angular/core';
import { AlertConfig } from './alert.config';
import { OnChange } from '../utils/decorators';

@Component({
selector: 'alert,bs-alert',
templateUrl: './alert.component.html'
templateUrl: './alert.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AlertComponent implements OnInit {
/** Alert type.
Expand All @@ -13,27 +17,29 @@ export class AlertComponent implements OnInit {
*/
@Input() type = 'warning';
/** If set, displays an inline "Close" button */
@OnChange()
@Input()
dismissible = false;
@OnChange() @Input() dismissible = false;
/** Number in milliseconds, after which alert will be closed */
@Input() dismissOnTimeout: number | string;

/** Is alert visible */
@Input() isOpen = true;

/** This event fires immediately after close instance method is called,
* $event is an instance of Alert component.
*/
@Output() onClose = new EventEmitter<AlertComponent>();
/** This event fires when alert closed, $event is an instance of Alert component */
@Output() onClosed = new EventEmitter<AlertComponent>();

isClosed = false;

classes = '';
dismissibleChange: EventEmitter<boolean> = new EventEmitter<boolean>();

constructor(_config: AlertConfig) {
constructor(_config: AlertConfig, private changeDetection: ChangeDetectorRef) {
Object.assign(this, _config);
this.dismissibleChange.subscribe((dismissible: boolean) => {
this.classes = this.dismissible ? 'alert-dismissible' : '';
this.changeDetection.markForCheck();
});
}

Expand All @@ -53,12 +59,13 @@ export class AlertComponent implements OnInit {
* Closes an alert by removing it from the DOM.
*/
close(): void {
if (this.isClosed) {
if (!this.isOpen) {
return;
}

this.onClose.emit(this);
this.isClosed = true;
this.isOpen = false;
this.changeDetection.markForCheck();
this.onClosed.emit(this);
}
}
12 changes: 6 additions & 6 deletions src/spec/alert.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { ChangeDetectorRef, Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { AlertComponent } from '../alert/alert.component';
Expand All @@ -7,8 +7,8 @@ import { AlertModule } from '../alert/alert.module';

@Component({selector: 'alert-test', template: ''})
class TestAlertComponent extends AlertComponent {
constructor(config: AlertConfig) {
super(config);
constructor(config: AlertConfig, changeDetection: ChangeDetectorRef) {
super(config, changeDetection);
}
}

Expand Down Expand Up @@ -51,16 +51,16 @@ describe('Component: Alert', () => {
it('should be dismissed by timeout', (done: () => void) => {
context.dismissOnTimeout = 1000;
context.onClosed.subscribe(() => {
expect(context.isClosed).toBeTruthy();
expect(context.isOpen).toBeFalsy();
done();
});
context.ngOnInit();
});

it('should be closed by public method onClose', () => {
context.ngOnInit();
expect(context.isClosed).toBeFalsy();
expect(context.isOpen).toBeTruthy();
context.close();
expect(context.isClosed).toBeTruthy();
expect(context.isOpen).toBeFalsy();
});
});
10 changes: 7 additions & 3 deletions src/spec/ng-bootstrap/alert.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
import { TestBed, inject } from '@angular/core/testing';
import { createGenericTestComponent } from './test/common';
import { Component } from '@angular/core';
import { ChangeDetectorRef, Component } from '@angular/core';
import { AlertModule, AlertComponent, AlertConfig } from '../../alert';

@Component({
Expand Down Expand Up @@ -32,13 +32,17 @@ describe('ngb-alert', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
imports: [AlertModule.forRoot()]
imports: [AlertModule.forRoot()],
providers: [
AlertComponent, ChangeDetectorRef
]
});
});

it('should initialize inputs with default values', () => {
const defaultConfig = new AlertConfig();
const alertCmp = new AlertComponent(new AlertConfig());
/*const alertCmp = new AlertComponent(new AlertConfig());*/
const alertCmp = TestBed.get(AlertComponent);

expect(alertCmp.dismissible).toBe(defaultConfig.dismissible);
expect(alertCmp.type).toBe(defaultConfig.type);
Expand Down

0 comments on commit af7597b

Please sign in to comment.