From d62c8f448a3c9e6a3581a53863a585bde1ddd8de Mon Sep 17 00:00:00 2001 From: Wendell Hu Date: Wed, 22 Jan 2020 11:00:46 +0800 Subject: [PATCH 1/3] feat(module:notification): support opening at different places close #4338 chore(module:message,notification): refactor --- components/core/config/config.ts | 2 +- ...essage-base.service.ts => base.service.ts} | 20 +-- ...nent.ts => message-container.component.ts} | 37 +++-- components/message/message.component.ts | 130 ++++++++++++++++++ ...nz-message.module.ts => message.module.ts} | 8 +- ...ce.module.ts => message.service.module.ts} | 0 ...-message.service.ts => message.service.ts} | 8 +- .../{nz-message.spec.ts => message.spec.ts} | 52 +++---- .../nz-message-container.component.html | 7 - components/message/nz-message.component.html | 21 --- components/message/nz-message.component.ts | 111 --------------- components/message/public-api.ts | 14 +- .../{nz-message.definitions.ts => typings.ts} | 0 components/notification/demo/module | 3 +- components/notification/demo/placement.md | 2 +- components/notification/demo/placement.ts | 44 +++--- ...ts => notification-container.component.ts} | 44 ++++-- .../notification/notification.component.ts | 109 +++++++++++++++ ...ation.module.ts => notification.module.ts} | 6 +- ...dule.ts => notification.service.module.ts} | 0 ...ion.service.ts => notification.service.ts} | 6 +- ...ification.spec.ts => notification.spec.ts} | 54 ++++---- .../notification/nz-notification-config.ts | 37 ----- .../nz-notification-container.component.html | 20 --- .../nz-notification.component.html | 64 --------- .../notification/nz-notification.component.ts | 47 ------- components/notification/public-api.ts | 12 +- ...notification.definitions.ts => typings.ts} | 11 +- 28 files changed, 428 insertions(+), 441 deletions(-) rename components/message/{nz-message-base.service.ts => base.service.ts} (85%) rename components/message/{nz-message-container.component.ts => message-container.component.ts} (70%) create mode 100644 components/message/message.component.ts rename components/message/{nz-message.module.ts => message.module.ts} (72%) rename components/message/{nz-message.service.module.ts => message.service.module.ts} (100%) rename components/message/{nz-message.service.ts => message.service.ts} (89%) rename components/message/{nz-message.spec.ts => message.spec.ts} (83%) delete mode 100644 components/message/nz-message-container.component.html delete mode 100644 components/message/nz-message.component.html delete mode 100644 components/message/nz-message.component.ts rename components/message/{nz-message.definitions.ts => typings.ts} (100%) rename components/notification/{nz-notification-container.component.ts => notification-container.component.ts} (61%) create mode 100644 components/notification/notification.component.ts rename components/notification/{nz-notification.module.ts => notification.module.ts} (76%) rename components/notification/{nz-notification.service.module.ts => notification.service.module.ts} (100%) rename components/notification/{nz-notification.service.ts => notification.service.ts} (92%) rename components/notification/{nz-notification.spec.ts => notification.spec.ts} (84%) delete mode 100644 components/notification/nz-notification-config.ts delete mode 100644 components/notification/nz-notification-container.component.html delete mode 100644 components/notification/nz-notification.component.html delete mode 100644 components/notification/nz-notification.component.ts rename components/notification/{nz-notification.definitions.ts => typings.ts} (80%) diff --git a/components/core/config/config.ts b/components/core/config/config.ts index e4dc17e15ad..a91a7c0cb84 100644 --- a/components/core/config/config.ts +++ b/components/core/config/config.ts @@ -164,7 +164,7 @@ export interface ModalConfig { export interface NotificationConfig extends MessageConfig { nzTop?: string | number; nzBottom?: string | number; - nzPlacement?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | string; + nzPlacement?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'; } export interface PageHeaderConfig { diff --git a/components/message/nz-message-base.service.ts b/components/message/base.service.ts similarity index 85% rename from components/message/nz-message-base.service.ts rename to components/message/base.service.ts index 17a9351b6eb..2799683d466 100644 --- a/components/message/nz-message-base.service.ts +++ b/components/message/base.service.ts @@ -10,13 +10,13 @@ import { Overlay } from '@angular/cdk/overlay'; import { ApplicationRef, ComponentFactoryResolver, EmbeddedViewRef, Injector, Type } from '@angular/core'; import { NzSingletonService } from 'ng-zorro-antd/core'; -import { NzMessageContainerComponent } from './nz-message-container.component'; -import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions'; +import { NzMessageContainerComponent } from './message-container.component'; +import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './typings'; let globalCounter = 0; export class NzMessageBaseService { - protected _container: ContainerClass; + protected container: ContainerClass; constructor( private nzSingletonService: NzSingletonService, @@ -27,15 +27,15 @@ export class NzMessageBaseService = { @@ -27,9 +28,14 @@ const NZ_MESSAGE_DEFAULT_CONFIG: Required = { selector: 'nz-message-container', exportAs: 'nzMessageContainer', preserveWhitespaces: false, - templateUrl: './nz-message-container.component.html' + template: ` +
+ +
+ ` }) -export class NzMessageContainerComponent implements OnInit { +export class NzMessageContainerComponent implements OnInit, OnDestroy { + destroy$ = new Subject(); messages: NzMessageDataFilled[] = []; config: Required; top: string | null; @@ -42,18 +48,27 @@ export class NzMessageContainerComponent implements OnInit { this.subscribeConfigChange(); } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + /** * Create a new message. * @param message Parsed message configuration. */ createMessage(message: NzMessageDataFilled): void { if (this.messages.length >= this.config.nzMaxStack) { - this.messages.splice(0, 1); + this.messages = this.messages.slice(1); } - message.options = this._mergeMessageOptions(message.options); + message.options = this.mergeMessageOptions(message.options); message.onClose = new Subject(); - this.messages.push(message); + this.messages = [...this.messages, message]; + console.log('debug messages length', this.messages); + + // Bug should happen here. messages get updated but change detection failed. this.cdr.detectChanges(); + this.cdr.markForCheck(); } /** @@ -65,6 +80,7 @@ export class NzMessageContainerComponent implements OnInit { this.messages.some((message, index) => { if (message.messageId === messageId) { this.messages.splice(index, 1); + this.messages = [...this.messages]; this.cdr.detectChanges(); message.onClose!.next(userAction); message.onClose!.complete(); @@ -89,7 +105,10 @@ export class NzMessageContainerComponent implements OnInit { } protected subscribeConfigChange(): void { - this.nzConfigService.getConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME).subscribe(() => this.updateConfig()); + this.nzConfigService + .getConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME) + .pipe(takeUntil(this.destroy$)) + .subscribe(() => this.updateConfig()); } protected updateConfigFromConfigService(): Required { @@ -104,7 +123,7 @@ export class NzMessageContainerComponent implements OnInit { * Merge default options and custom message options * @param options */ - protected _mergeMessageOptions(options?: NzMessageDataOptions): NzMessageDataOptions { + protected mergeMessageOptions(options?: NzMessageDataOptions): NzMessageDataOptions { const defaultOptions: NzMessageDataOptions = { nzDuration: this.config.nzDuration, nzAnimate: this.config.nzAnimate, diff --git a/components/message/message.component.ts b/components/message/message.component.ts new file mode 100644 index 00000000000..bef006de590 --- /dev/null +++ b/components/message/message.component.ts @@ -0,0 +1,130 @@ +/** + * @license + * Copyright Alibaba.com All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; + +import { moveUpMotion } from 'ng-zorro-antd/core'; + +import { NzMessageContainerComponent } from './message-container.component'; +import { NzMessageDataFilled, NzMessageDataOptions } from './typings'; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + selector: 'nz-message', + exportAs: 'nzMessage', + preserveWhitespaces: false, + animations: [moveUpMotion], + template: ` +
+
+
+ + + + + + + + + + +
+
+
+ ` +}) +export class NzMessageComponent implements OnInit, OnDestroy { + @Input() nzMessage: NzMessageDataFilled; + @Input() nzIndex: number; + + protected options: Required; + + // Whether to set a timeout to destroy itself. + private autoClose: boolean; + + private eraseTimer: number | null = null; + private eraseTimingStart: number; + private eraseTTL: number; // Time to live. + + constructor(private messageContainerComponent: NzMessageContainerComponent, protected cdr: ChangeDetectorRef) {} + + ngOnInit(): void { + // `NzMessageContainer` does its job so all properties cannot be undefined. + this.options = this.nzMessage.options as Required; + + if (this.options.nzAnimate) { + this.nzMessage.state = 'enter'; + } + + this.autoClose = this.options.nzDuration > 0; + + if (this.autoClose) { + this.initErase(); + this.startEraseTimeout(); + } + } + + ngOnDestroy(): void { + if (this.autoClose) { + this.clearEraseTimeout(); + } + } + + onEnter(): void { + if (this.autoClose && this.options.nzPauseOnHover) { + this.clearEraseTimeout(); + this.updateTTL(); + } + } + + onLeave(): void { + if (this.autoClose && this.options.nzPauseOnHover) { + this.startEraseTimeout(); + } + } + + // Remove self + protected destroy(userAction: boolean = false): void { + if (this.options.nzAnimate) { + this.nzMessage.state = 'leave'; + this.cdr.detectChanges(); + setTimeout(() => this.messageContainerComponent.removeMessage(this.nzMessage.messageId, userAction), 200); + } else { + this.messageContainerComponent.removeMessage(this.nzMessage.messageId, userAction); + } + } + + private initErase(): void { + this.eraseTTL = this.options.nzDuration; + this.eraseTimingStart = Date.now(); + } + + private updateTTL(): void { + if (this.autoClose) { + this.eraseTTL -= Date.now() - this.eraseTimingStart; + } + } + + private startEraseTimeout(): void { + if (this.eraseTTL > 0) { + this.clearEraseTimeout(); + this.eraseTimer = setTimeout(() => this.destroy(), this.eraseTTL); + this.eraseTimingStart = Date.now(); + } else { + this.destroy(); + } + } + + private clearEraseTimeout(): void { + if (this.eraseTimer !== null) { + clearTimeout(this.eraseTimer); + this.eraseTimer = null; + } + } +} diff --git a/components/message/nz-message.module.ts b/components/message/message.module.ts similarity index 72% rename from components/message/nz-message.module.ts rename to components/message/message.module.ts index 9f5787aa91e..2a75b40f11b 100644 --- a/components/message/nz-message.module.ts +++ b/components/message/message.module.ts @@ -12,13 +12,13 @@ import { NgModule } from '@angular/core'; import { NzOutletModule } from 'ng-zorro-antd/core'; import { NzIconModule } from 'ng-zorro-antd/icon'; -import { NzMessageContainerComponent } from './nz-message-container.component'; -import { NzMessageComponent } from './nz-message.component'; -import { NzMessageServiceModule } from './nz-message.service.module'; +import { NzMessageContainerComponent } from './message-container.component'; +import { NzMessageComponent } from './message.component'; +import { NzMessageServiceModule } from './message.service.module'; @NgModule({ imports: [CommonModule, OverlayModule, NzIconModule, NzOutletModule, NzMessageServiceModule], declarations: [NzMessageContainerComponent, NzMessageComponent], - entryComponents: [NzMessageContainerComponent] + entryComponents: [NzMessageContainerComponent, NzMessageComponent] }) export class NzMessageModule {} diff --git a/components/message/nz-message.service.module.ts b/components/message/message.service.module.ts similarity index 100% rename from components/message/nz-message.service.module.ts rename to components/message/message.service.module.ts diff --git a/components/message/nz-message.service.ts b/components/message/message.service.ts similarity index 89% rename from components/message/nz-message.service.ts rename to components/message/message.service.ts index e5820ffe5be..899feaa8b18 100644 --- a/components/message/nz-message.service.ts +++ b/components/message/message.service.ts @@ -10,10 +10,10 @@ import { Overlay } from '@angular/cdk/overlay'; import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector, TemplateRef } from '@angular/core'; import { NzSingletonService } from 'ng-zorro-antd/core'; -import { NzMessageBaseService } from './nz-message-base.service'; -import { NzMessageContainerComponent } from './nz-message-container.component'; -import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions'; -import { NzMessageServiceModule } from './nz-message.service.module'; +import { NzMessageBaseService } from './base.service'; +import { NzMessageContainerComponent } from './message-container.component'; +import { NzMessageServiceModule } from './message.service.module'; +import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './typings'; @Injectable({ providedIn: NzMessageServiceModule diff --git a/components/message/nz-message.spec.ts b/components/message/message.spec.ts similarity index 83% rename from components/message/nz-message.spec.ts rename to components/message/message.spec.ts index 1fae1feb253..1b1231d49f3 100644 --- a/components/message/nz-message.spec.ts +++ b/components/message/message.spec.ts @@ -1,39 +1,46 @@ import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, TemplateRef, ViewChild } from '@angular/core'; -import { ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, inject, tick } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { dispatchMouseEvent, NZ_CONFIG, NzConfig, NzConfigService } from 'ng-zorro-antd/core'; +import { dispatchMouseEvent, NZ_CONFIG, NzConfigService } from 'ng-zorro-antd/core'; +import { ComponentBed, createComponentBed } from 'ng-zorro-antd/core/testing/componet-bed'; -import { NzMessageModule } from './nz-message.module'; -import { NzMessageService } from './nz-message.service'; +import { NzMessageModule } from './message.module'; +import { NzMessageService } from './message.service'; -describe('NzMessage', () => { +describe('message', () => { + let testBed: ComponentBed; let messageService: NzMessageService; let overlayContainerElement: HTMLElement; - let fixture: ComponentFixture; - let testComponent: NzTestMessageBasicComponent; + let fixture: ComponentFixture; + let testComponent: NzTestMessageComponent; let nzConfigService: NzConfigService; beforeEach(fakeAsync(() => { - const MESSAGE_CONFIG: NzConfig['message'] = { - nzMaxStack: 2, - nzTop: 24 - }; - - TestBed.configureTestingModule({ + testBed = createComponentBed(NzTestMessageComponent, { imports: [NzMessageModule, NoopAnimationsModule], - declarations: [NzTestMessageBasicComponent], - providers: [{ provide: NZ_CONFIG, useValue: { message: MESSAGE_CONFIG } }] + providers: [ + { + provide: NZ_CONFIG, + useValue: { + message: { + nzMaxStack: 2, + nzTop: 24 + } + } + } + ] }); - TestBed.compileComponents(); + fixture = testBed.fixture; + testComponent = testBed.component; })); beforeEach(inject([NzMessageService, OverlayContainer], (m: NzMessageService, oc: OverlayContainer) => { messageService = m; // @ts-ignore - nzConfigService = messageService._container.nzConfigService; + nzConfigService = messageService.container.nzConfigService; if (!overlayContainerElement) { overlayContainerElement = oc.getContainerElement(); } @@ -43,11 +50,6 @@ describe('NzMessage', () => { messageService.remove(); }); - beforeEach(() => { - fixture = TestBed.createComponent(NzTestMessageBasicComponent); - testComponent = fixture.debugElement.componentInstance; - }); - it('should open a message box with success', () => { messageService.success('SUCCESS'); fixture.detectChanges(); @@ -146,14 +148,14 @@ describe('NzMessage', () => { expect(overlayContainerElement.textContent).toContain(content); if (id === 3) { expect(overlayContainerElement.textContent).not.toContain('SUCCESS-1'); - expect((messageService as any)._container.messages.length).toBe(2); // tslint:disable-line:no-any + expect((messageService as any).container.messages.length).toBe(2); // tslint:disable-line:no-any } }); messageService.remove(); fixture.detectChanges(); expect(overlayContainerElement.textContent).not.toContain('SUCCESS-3'); - expect((messageService as any)._container.messages.length).toBe(0); // tslint:disable-line:no-any + expect((messageService as any).container.messages.length).toBe(0); // tslint:disable-line:no-any })); it('should destroy without animation', fakeAsync(() => { @@ -201,6 +203,6 @@ describe('NzMessage', () => { ` }) -export class NzTestMessageBasicComponent { +export class NzTestMessageComponent { @ViewChild('contentTemplate', { static: true }) template: TemplateRef; } diff --git a/components/message/nz-message-container.component.html b/components/message/nz-message-container.component.html deleted file mode 100644 index 8de9b55b965..00000000000 --- a/components/message/nz-message-container.component.html +++ /dev/null @@ -1,7 +0,0 @@ -
- -
diff --git a/components/message/nz-message.component.html b/components/message/nz-message.component.html deleted file mode 100644 index b4f2b18261b..00000000000 --- a/components/message/nz-message.component.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
-
- - - - - - - - - - -
-
-
diff --git a/components/message/nz-message.component.ts b/components/message/nz-message.component.ts deleted file mode 100644 index d94c7d8d7ad..00000000000 --- a/components/message/nz-message.component.ts +++ /dev/null @@ -1,111 +0,0 @@ -/** - * @license - * Copyright Alibaba.com All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE - */ - -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; - -import { moveUpMotion } from 'ng-zorro-antd/core'; - -import { NzMessageContainerComponent } from './nz-message-container.component'; -import { NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions'; - -@Component({ - changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None, - selector: 'nz-message', - exportAs: 'nzMessage', - preserveWhitespaces: false, - animations: [moveUpMotion], - templateUrl: './nz-message.component.html' -}) -export class NzMessageComponent implements OnInit, OnDestroy { - @Input() nzMessage: NzMessageDataFilled; - @Input() nzIndex: number; - - protected _options: Required; - - private _autoErase: boolean; // Whether to set a timeout to destroy itself. - private _eraseTimer: number | null = null; - private _eraseTimingStart: number; - private _eraseTTL: number; // Time to live. - - constructor(private _messageContainer: NzMessageContainerComponent, protected cdr: ChangeDetectorRef) {} - - ngOnInit(): void { - // `NzMessageContainer` does its job so all properties cannot be undefined. - this._options = this.nzMessage.options as Required; - - if (this._options.nzAnimate) { - this.nzMessage.state = 'enter'; - } - - this._autoErase = this._options.nzDuration > 0; - - if (this._autoErase) { - this._initErase(); - this._startEraseTimeout(); - } - } - - ngOnDestroy(): void { - if (this._autoErase) { - this._clearEraseTimeout(); - } - } - - onEnter(): void { - if (this._autoErase && this._options.nzPauseOnHover) { - this._clearEraseTimeout(); - this._updateTTL(); - } - } - - onLeave(): void { - if (this._autoErase && this._options.nzPauseOnHover) { - this._startEraseTimeout(); - } - } - - // Remove self - protected _destroy(userAction: boolean = false): void { - if (this._options.nzAnimate) { - this.nzMessage.state = 'leave'; - this.cdr.detectChanges(); - setTimeout(() => this._messageContainer.removeMessage(this.nzMessage.messageId, userAction), 200); - } else { - this._messageContainer.removeMessage(this.nzMessage.messageId, userAction); - } - } - - private _initErase(): void { - this._eraseTTL = this._options.nzDuration; - this._eraseTimingStart = Date.now(); - } - - private _updateTTL(): void { - if (this._autoErase) { - this._eraseTTL -= Date.now() - this._eraseTimingStart; - } - } - - private _startEraseTimeout(): void { - if (this._eraseTTL > 0) { - this._clearEraseTimeout(); - this._eraseTimer = setTimeout(() => this._destroy(), this._eraseTTL); - this._eraseTimingStart = Date.now(); - } else { - this._destroy(); - } - } - - private _clearEraseTimeout(): void { - if (this._eraseTimer !== null) { - clearTimeout(this._eraseTimer); - this._eraseTimer = null; - } - } -} diff --git a/components/message/public-api.ts b/components/message/public-api.ts index ae41208d993..ceef5f1c73c 100644 --- a/components/message/public-api.ts +++ b/components/message/public-api.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -export * from './nz-message-base.service'; -export * from './nz-message.service'; -export * from './nz-message.service.module'; -export * from './nz-message.module'; -export * from './nz-message.component'; -export * from './nz-message.definitions'; -export * from './nz-message-container.component'; +export * from './base.service'; +export * from './message.service'; +export * from './message.service.module'; +export * from './message.module'; +export * from './message.component'; +export * from './typings'; +export * from './message-container.component'; diff --git a/components/message/nz-message.definitions.ts b/components/message/typings.ts similarity index 100% rename from components/message/nz-message.definitions.ts rename to components/message/typings.ts diff --git a/components/notification/demo/module b/components/notification/demo/module index 5ad886eb8bf..34966685b6b 100644 --- a/components/notification/demo/module +++ b/components/notification/demo/module @@ -3,5 +3,6 @@ import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzSelectModule } from 'ng-zorro-antd/select'; import { NzTagModule } from 'ng-zorro-antd/tag'; +import { NzDividerModule } from 'ng-zorro-antd/divider' -export const moduleList = [ NzNotificationModule, NzButtonModule, NzIconModule, NzSelectModule, NzTagModule ]; +export const moduleList = [ NzNotificationModule, NzButtonModule, NzIconModule, NzSelectModule, NzTagModule, NzDividerModule]; diff --git a/components/notification/demo/placement.md b/components/notification/demo/placement.md index 638ceac0e60..37005f1dbb4 100755 --- a/components/notification/demo/placement.md +++ b/components/notification/demo/placement.md @@ -7,7 +7,7 @@ title: ## zh-CN -可以设置通知从右上角、右下角、左下角、左上角弹出。 +通知从右上角、右下角、左下角、左上角弹出。 ## en-US diff --git a/components/notification/demo/placement.ts b/components/notification/demo/placement.ts index 3a7a8be91f5..3ce9f2a03b6 100644 --- a/components/notification/demo/placement.ts +++ b/components/notification/demo/placement.ts @@ -1,35 +1,39 @@ import { Component } from '@angular/core'; -import { NzConfigService } from 'ng-zorro-antd/core'; -import { NzNotificationService } from 'ng-zorro-antd/notification'; +import { NzNotificationPosition, NzNotificationService } from 'ng-zorro-antd/notification'; @Component({ selector: 'nz-demo-notification-placement', template: ` - - - - - - - - ` + + + + + + `, + styles: [ + ` + button { + margin-right: 1em; + } + ` + ] }) export class NzDemoNotificationPlacementComponent { placement = 'topRight'; - clearBeforeNotifications(): void { - this.notification.remove(); - } - - createBasicNotification(): void { - this.configService.set('notification', { - nzPlacement: this.placement - }); + createBasicNotification(position: NzNotificationPosition): void { this.notification.blank( 'Notification Title', - 'This is the content of the notification. This is the content of the notification. This is the content of the notification.' + 'This is the content of the notification. This is the content of the notification. This is the content of the notification.', + { nzPosition: position } ); } - constructor(private notification: NzNotificationService, private configService: NzConfigService) {} + constructor(private notification: NzNotificationService) {} } diff --git a/components/notification/nz-notification-container.component.ts b/components/notification/notification-container.component.ts similarity index 61% rename from components/notification/nz-notification-container.component.ts rename to components/notification/notification-container.component.ts index fc896543e47..86d8348cf0c 100644 --- a/components/notification/nz-notification-container.component.ts +++ b/components/notification/notification-container.component.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs'; import { NotificationConfig, NzConfigService, toCssPixel } from 'ng-zorro-antd/core'; import { NzMessageContainerComponent } from 'ng-zorro-antd/message'; -import { NzNotificationDataFilled, NzNotificationDataOptions } from './nz-notification.definitions'; +import { NzNotificationDataFilled, NzNotificationDataOptions } from './typings'; const NZ_CONFIG_COMPONENT_NAME = 'notification'; @@ -32,7 +32,20 @@ const NZ_NOTIFICATION_DEFAULT_CONFIG: Required = { selector: 'nz-notification-container', exportAs: 'nzNotificationContainer', preserveWhitespaces: false, - templateUrl: './nz-notification-container.component.html' + template: ` +
+ +
+
+ +
+
+ +
+
+ +
+ ` }) export class NzNotificationContainerComponent extends NzMessageContainerComponent { config: Required; @@ -47,6 +60,22 @@ export class NzNotificationContainerComponent extends NzMessageContainerComponen super(cdr, nzConfigService); } + get topLeftMessages(): Array> { + return this.messages.filter(m => m.options.nzPosition === 'topLeft'); + } + + get topRightMessages(): Array> { + return this.messages.filter(m => m.options.nzPosition === 'topRight' || !m.options.nzPosition); + } + + get bottomLeftMessages(): Array> { + return this.messages.filter(m => m.options.nzPosition === 'bottomLeft'); + } + + get bottomRightMessages(): Array> { + return this.messages.filter(m => m.options.nzPosition === 'bottomRight'); + } + /** * Create a new notification. * If there's a notification whose `nzKey` is same with `nzKey` in `NzNotificationDataFilled`, @@ -55,7 +84,7 @@ export class NzNotificationContainerComponent extends NzMessageContainerComponen * @param notification */ createMessage(notification: NzNotificationDataFilled): void { - notification.options = this._mergeMessageOptions(notification.options); + notification.options = this.mergeMessageOptions(notification.options); notification.onClose = new Subject(); const key = notification.options.nzKey; const notificationWithSameKey = this.messages.find( @@ -82,10 +111,9 @@ export class NzNotificationContainerComponent extends NzMessageContainerComponen ...this.config, ...this.nzConfigService.getConfigForComponent(NZ_CONFIG_COMPONENT_NAME) }); - const placement = this.config.nzPlacement; - this.top = placement === 'topLeft' || placement === 'topRight' ? toCssPixel(newConfig.nzTop) : null; - this.bottom = placement === 'bottomLeft' || placement === 'bottomRight' ? toCssPixel(newConfig.nzBottom) : null; + this.top = toCssPixel(newConfig.nzTop); + this.bottom = toCssPixel(newConfig.nzBottom); this.cdr.markForCheck(); } @@ -94,9 +122,7 @@ export class NzNotificationContainerComponent extends NzMessageContainerComponen * @override */ protected subscribeConfigChange(): void { - this.nzConfigService - .getConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME) - .subscribe(() => this.updateConfig()); + this.nzConfigService.getConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME).subscribe(() => this.updateConfig()); } private replaceNotification(old: NzNotificationDataFilled, _new: NzNotificationDataFilled): void { diff --git a/components/notification/notification.component.ts b/components/notification/notification.component.ts new file mode 100644 index 00000000000..d4b86631e56 --- /dev/null +++ b/components/notification/notification.component.ts @@ -0,0 +1,109 @@ +/** + * @license + * Copyright Alibaba.com All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +import { ChangeDetectorRef, Component, Input, ViewEncapsulation } from '@angular/core'; + +import { notificationMotion } from 'ng-zorro-antd/core'; +import { NzMessageComponent } from 'ng-zorro-antd/message'; + +import { NzNotificationContainerComponent } from './notification-container.component'; +import { NzNotificationDataFilled } from './typings'; + +@Component({ + encapsulation: ViewEncapsulation.None, + selector: 'nz-notification', + exportAs: 'nzNotification', + preserveWhitespaces: false, + animations: [notificationMotion], + template: ` +
+
+
+
+ + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ ` +}) +export class NzNotificationComponent extends NzMessageComponent { + @Input() nzMessage: NzNotificationDataFilled; + + constructor(private container: NzNotificationContainerComponent, protected cdr: ChangeDetectorRef) { + super(container, cdr); + } + + close(): void { + this.destroy(true); + } + + get state(): string | undefined { + if (this.nzMessage.state === 'enter') { + if (this.container.config.nzPlacement === 'topLeft' || this.container.config.nzPlacement === 'bottomLeft') { + return 'enterLeft'; + } else { + return 'enterRight'; + } + } else { + return this.nzMessage.state; + } + } +} diff --git a/components/notification/nz-notification.module.ts b/components/notification/notification.module.ts similarity index 76% rename from components/notification/nz-notification.module.ts rename to components/notification/notification.module.ts index 832e24ebccd..d93639c8466 100644 --- a/components/notification/nz-notification.module.ts +++ b/components/notification/notification.module.ts @@ -12,9 +12,9 @@ import { NgModule } from '@angular/core'; import { NzOutletModule } from 'ng-zorro-antd/core'; import { NzIconModule } from 'ng-zorro-antd/icon'; -import { NzNotificationContainerComponent } from './nz-notification-container.component'; -import { NzNotificationComponent } from './nz-notification.component'; -import { NzNotificationServiceModule } from './nz-notification.service.module'; +import { NzNotificationContainerComponent } from './notification-container.component'; +import { NzNotificationComponent } from './notification.component'; +import { NzNotificationServiceModule } from './notification.service.module'; @NgModule({ imports: [CommonModule, OverlayModule, NzIconModule, NzNotificationServiceModule, NzOutletModule], diff --git a/components/notification/nz-notification.service.module.ts b/components/notification/notification.service.module.ts similarity index 100% rename from components/notification/nz-notification.service.module.ts rename to components/notification/notification.service.module.ts diff --git a/components/notification/nz-notification.service.ts b/components/notification/notification.service.ts similarity index 92% rename from components/notification/nz-notification.service.ts rename to components/notification/notification.service.ts index 3c1581f740e..701e2e886d4 100644 --- a/components/notification/nz-notification.service.ts +++ b/components/notification/notification.service.ts @@ -12,9 +12,9 @@ import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector, Templat import { NzSingletonService } from 'ng-zorro-antd/core'; import { NzMessageBaseService } from 'ng-zorro-antd/message'; -import { NzNotificationContainerComponent } from './nz-notification-container.component'; -import { NzNotificationData, NzNotificationDataFilled, NzNotificationDataOptions } from './nz-notification.definitions'; -import { NzNotificationServiceModule } from './nz-notification.service.module'; +import { NzNotificationContainerComponent } from './notification-container.component'; +import { NzNotificationServiceModule } from './notification.service.module'; +import { NzNotificationData, NzNotificationDataFilled, NzNotificationDataOptions } from './typings'; @Injectable({ providedIn: NzNotificationServiceModule diff --git a/components/notification/nz-notification.spec.ts b/components/notification/notification.spec.ts similarity index 84% rename from components/notification/nz-notification.spec.ts rename to components/notification/notification.spec.ts index ba297c4ee81..24800d3824a 100644 --- a/components/notification/nz-notification.spec.ts +++ b/components/notification/notification.spec.ts @@ -1,28 +1,30 @@ import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, TemplateRef, ViewChild } from '@angular/core'; -import { ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, inject, tick } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { HomeOutline } from '@ant-design/icons-angular/icons'; -import { dispatchMouseEvent, NZ_CONFIG, NzConfig, NzConfigService } from 'ng-zorro-antd/core'; +import { dispatchMouseEvent, NZ_CONFIG, NzConfigService } from 'ng-zorro-antd/core'; +import { ComponentBed, createComponentBed } from 'ng-zorro-antd/core/testing/componet-bed'; import { NZ_ICONS } from 'ng-zorro-antd/icon'; -import { NzNotificationModule } from './nz-notification.module'; -import { NzNotificationService } from './nz-notification.service'; +import { NzNotificationModule } from './notification.module'; +import { NzNotificationService } from './notification.service'; @Component({ template: ` {{ 'test template content' }}{{ data }} ` }) -export class DemoAppComponent { +export class NzTestNotificationComponent { @ViewChild(TemplateRef, { static: true }) demoTemplateRef: TemplateRef<{}>; } describe('NzNotification', () => { + let testBed: ComponentBed; let notificationService: NzNotificationService; let overlayContainerElement: HTMLElement; - let fixture: ComponentFixture; + let fixture: ComponentFixture; let nzConfigService: NzConfigService; function waitForNotificationToggling(): void { @@ -32,38 +34,36 @@ describe('NzNotification', () => { } beforeEach(fakeAsync(() => { - const NOTIFICATION_CONFIG: NzConfig['notification'] = { - nzMaxStack: 2 - }; - - TestBed.configureTestingModule({ + testBed = createComponentBed(NzTestNotificationComponent, { imports: [NzNotificationModule, NoopAnimationsModule], - declarations: [DemoAppComponent], providers: [ { provide: NZ_ICONS, useValue: [HomeOutline] }, - { provide: NZ_CONFIG, useValue: { notification: NOTIFICATION_CONFIG } } - ] // Override default config + { + provide: NZ_CONFIG, + useValue: { + notification: { + nzMaxStack: 2 + } + } + } + ] }); - TestBed.compileComponents(); + fixture = testBed.fixture; })); beforeEach(inject([NzNotificationService, OverlayContainer], (n: NzNotificationService, oc: OverlayContainer) => { notificationService = n; // @ts-ignore - nzConfigService = notificationService._container.nzConfigService; + nzConfigService = notificationService.container.nzConfigService; if (!overlayContainerElement) { overlayContainerElement = oc.getContainerElement(); } })); - beforeEach(() => { - fixture = TestBed.createComponent(DemoAppComponent); - }); - afterEach(() => { notificationService.remove(); }); @@ -155,14 +155,14 @@ describe('NzNotification', () => { expect(overlayContainerElement.textContent).toContain(content); if (id === 3) { expect(overlayContainerElement.textContent).not.toContain('SUCCESS-1'); - expect((notificationService as any)._container.messages.length).toBe(2); // tslint:disable-line:no-any + expect((notificationService as any).container.messages.length).toBe(2); // tslint:disable-line:no-any } }); notificationService.remove(); fixture.detectChanges(); expect(overlayContainerElement.textContent).not.toContain('SUCCESS-3'); - expect((notificationService as any)._container.messages.length).toBe(0); // tslint:disable-line:no-any + expect((notificationService as any).container.messages.length).toBe(0); // tslint:disable-line:no-any })); it('should destroy without animation', fakeAsync(() => { @@ -226,15 +226,15 @@ describe('NzNotification', () => { nzConfigService.set('notification', { nzTop: 48 }); notificationService.create('', '', 'TEST TOP', { nzDuration: 3000 }); waitForNotificationToggling(); - const notificationContainer = overlayContainerElement.querySelector('.ant-notification') as HTMLElement; - expect(notificationContainer.style.top).toBe('48px'); - expect(notificationContainer.style.bottom).toBeFalsy(); + const notificationContainers = overlayContainerElement.querySelectorAll('.ant-notification') as NodeListOf; + expect(notificationContainers[0].style.top).toBe('48px'); + expect(notificationContainers[0].style.bottom).toBeFalsy(); nzConfigService.set('notification', { nzPlacement: 'bottomLeft', nzBottom: '48px' }); notificationService.create('', '', 'TEST BOTTOM'); waitForNotificationToggling(); - expect(notificationContainer.style.top).toBeFalsy(); - expect(notificationContainer.style.bottom).toBe('48px'); + expect(notificationContainers[3].style.top).toBeFalsy(); + expect(notificationContainers[3].style.bottom).toBe('48px'); waitForNotificationToggling(); })); diff --git a/components/notification/nz-notification-config.ts b/components/notification/nz-notification-config.ts deleted file mode 100644 index c00e71c58c3..00000000000 --- a/components/notification/nz-notification-config.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @license - * Copyright Alibaba.com All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE - */ - -import { InjectionToken } from '@angular/core'; - -import { NzMessageConfigLegacy } from 'ng-zorro-antd/message'; - -/** - * @deprecated This interface would has been moved to `ng-zorro-antd/core`. Please migrate to that. - */ -export interface NzNotificationConfigLegacy extends NzMessageConfigLegacy { - nzTop?: string | number; - nzBottom?: string | number; - nzPlacement?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | string; -} - -export const NZ_NOTIFICATION_DEFAULT_CONFIG = new InjectionToken('NZ_NOTIFICATION_DEFAULT_CONFIG'); - -export const NZ_NOTIFICATION_CONFIG = new InjectionToken('NZ_NOTIFICATION_CONFIG'); - -export const NZ_NOTIFICATION_DEFAULT_CONFIG_PROVIDER = { - provide: NZ_NOTIFICATION_DEFAULT_CONFIG, - useValue: { - nzTop: '24px', - nzBottom: '24px', - nzPlacement: 'topRight', - nzDuration: 4500, - nzMaxStack: 7, - nzPauseOnHover: true, - nzAnimate: true - } -}; diff --git a/components/notification/nz-notification-container.component.html b/components/notification/nz-notification-container.component.html deleted file mode 100644 index 5c22676e2ab..00000000000 --- a/components/notification/nz-notification-container.component.html +++ /dev/null @@ -1,20 +0,0 @@ -
- - -
diff --git a/components/notification/nz-notification.component.html b/components/notification/nz-notification.component.html deleted file mode 100644 index 83b53de863b..00000000000 --- a/components/notification/nz-notification.component.html +++ /dev/null @@ -1,64 +0,0 @@ -
-
-
-
- - - - - - -
-
-
-
-
- - - - - - - - - - - - - - -
diff --git a/components/notification/nz-notification.component.ts b/components/notification/nz-notification.component.ts deleted file mode 100644 index c61638ea7e7..00000000000 --- a/components/notification/nz-notification.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @license - * Copyright Alibaba.com All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE - */ - -import { ChangeDetectorRef, Component, Input, ViewEncapsulation } from '@angular/core'; - -import { notificationMotion } from 'ng-zorro-antd/core'; -import { NzMessageComponent } from 'ng-zorro-antd/message'; - -import { NzNotificationContainerComponent } from './nz-notification-container.component'; -import { NzNotificationDataFilled } from './nz-notification.definitions'; - -@Component({ - encapsulation: ViewEncapsulation.None, - selector: 'nz-notification', - exportAs: 'nzNotification', - preserveWhitespaces: false, - animations: [notificationMotion], - templateUrl: './nz-notification.component.html' -}) -export class NzNotificationComponent extends NzMessageComponent { - @Input() nzMessage: NzNotificationDataFilled; - - constructor(private container: NzNotificationContainerComponent, protected cdr: ChangeDetectorRef) { - super(container, cdr); - } - - close(): void { - this._destroy(true); - } - - get state(): string | undefined { - if (this.nzMessage.state === 'enter') { - if (this.container.config.nzPlacement === 'topLeft' || this.container.config.nzPlacement === 'bottomLeft') { - return 'enterLeft'; - } else { - return 'enterRight'; - } - } else { - return this.nzMessage.state; - } - } -} diff --git a/components/notification/public-api.ts b/components/notification/public-api.ts index c00399e2766..301a08cb4f0 100644 --- a/components/notification/public-api.ts +++ b/components/notification/public-api.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -export * from './nz-notification.component'; -export * from './nz-notification.module'; -export * from './nz-notification.definitions'; -export * from './nz-notification.service'; -export * from './nz-notification.service.module'; -export * from './nz-notification-container.component'; +export * from './notification.component'; +export * from './notification.module'; +export * from './typings'; +export * from './notification.service'; +export * from './notification.service.module'; +export * from './notification-container.component'; diff --git a/components/notification/nz-notification.definitions.ts b/components/notification/typings.ts similarity index 80% rename from components/notification/nz-notification.definitions.ts rename to components/notification/typings.ts index 2704cd33026..7442a07b4fd 100644 --- a/components/notification/nz-notification.definitions.ts +++ b/components/notification/typings.ts @@ -9,6 +9,7 @@ import { TemplateRef } from '@angular/core'; import { Subject } from 'rxjs'; +import { NgClassInterface, NgStyleInterface } from 'ng-zorro-antd/core'; import { NzMessageData, NzMessageDataOptions } from 'ng-zorro-antd/message'; export interface NzNotificationData extends NzMessageData { @@ -18,15 +19,17 @@ export interface NzNotificationData extends NzMessageData { title?: string; } +export type NzNotificationPosition = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'; + export interface NzNotificationDataOptions extends NzMessageDataOptions { nzKey?: string; - nzStyle?: any; // tslint:disable-line:no-any - nzClass?: any; // tslint:disable-line:no-any + nzStyle?: NgStyleInterface | string; + nzClass?: NgClassInterface | string; + nzCloseIcon?: TemplateRef | string; + nzPosition?: NzNotificationPosition; /** Anything user wants renderer into a template. */ nzData?: T; - - nzCloseIcon?: TemplateRef | string; } // Filled version of NzMessageData (includes more private properties) From 886055a1edbcd4f501f68608e8d0919a5af21aa2 Mon Sep 17 00:00:00 2001 From: Wendell Hu Date: Wed, 26 Feb 2020 11:39:41 +0800 Subject: [PATCH 2/3] chore: test with cdr --- angular.json | 5 ++++- components/message/message-container.component.ts | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/angular.json b/angular.json index 1c19366a512..4ca8aec245d 100644 --- a/angular.json +++ b/angular.json @@ -231,5 +231,8 @@ "@schematics/angular:directive": { "prefix": "app" } + }, + "cli": { + "analytics": false } -} +} \ No newline at end of file diff --git a/components/message/message-container.component.ts b/components/message/message-container.component.ts index 8fc9469d686..da5d6cf48a8 100644 --- a/components/message/message-container.component.ts +++ b/components/message/message-container.component.ts @@ -64,11 +64,14 @@ export class NzMessageContainerComponent implements OnInit, OnDestroy { message.options = this.mergeMessageOptions(message.options); message.onClose = new Subject(); this.messages = [...this.messages, message]; - console.log('debug messages length', this.messages); + + const cdr = this.cdr; + + console.log('debug messages length', this.messages, cdr); // Bug should happen here. messages get updated but change detection failed. - this.cdr.detectChanges(); - this.cdr.markForCheck(); + cdr.detectChanges(); + cdr.markForCheck(); } /** From a2c6b800e77be08683101fb2a27dedf5c6555daf Mon Sep 17 00:00:00 2001 From: Wendell Hu Date: Sun, 15 Mar 2020 14:42:12 +0800 Subject: [PATCH 3/3] chore: remove console --- components/message/message-container.component.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/components/message/message-container.component.ts b/components/message/message-container.component.ts index da5d6cf48a8..354634d38c0 100644 --- a/components/message/message-container.component.ts +++ b/components/message/message-container.component.ts @@ -64,14 +64,7 @@ export class NzMessageContainerComponent implements OnInit, OnDestroy { message.options = this.mergeMessageOptions(message.options); message.onClose = new Subject(); this.messages = [...this.messages, message]; - - const cdr = this.cdr; - - console.log('debug messages length', this.messages, cdr); - - // Bug should happen here. messages get updated but change detection failed. - cdr.detectChanges(); - cdr.markForCheck(); + this.cdr.detectChanges(); } /**