Skip to content

Commit

Permalink
feat(module:root): make nz-root optional (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
trotyl authored and vthinkxie committed Aug 24, 2017
1 parent 143c080 commit 9de3de1
Show file tree
Hide file tree
Showing 12 changed files with 352 additions and 139 deletions.
26 changes: 26 additions & 0 deletions src/components/ng-zorro-antd.module.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { async, TestBed } from '@angular/core/testing';
import { NgZorroAntdModule, NZ_ROOT_CONFIG, NzRootConfig } from './ng-zorro-antd.module';

describe('NgZorroAntdModule with Angular integration', () => {
it('should not provide root config with empty forRoot', async(() => {
TestBed.configureTestingModule({
imports: [
NgZorroAntdModule.forRoot(),
],
}).compileComponents();

expect(TestBed.get(NZ_ROOT_CONFIG)).not.toBeDefined();
}));

it('should provide root config with params in forRoot', async(() => {
const options: NzRootConfig = { extraFontName: '', extraFontUrl: '' };

TestBed.configureTestingModule({
imports: [
NgZorroAntdModule.forRoot(options),
],
}).compileComponents();

expect(TestBed.get(NZ_ROOT_CONFIG)).toBeDefined();
}));
});
9 changes: 7 additions & 2 deletions src/components/ng-zorro-antd.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ import { NzMessageService } from './message/nz-message.service';
import { NzModalService } from './modal/nz-modal.service';
import { NzModalSubject } from './modal/nz-modal-subject.service';

// Tokens (eg. global services' config)
import { NZ_ROOT_CONFIG, NzRootConfig } from './root/nz-root-config'

// ---------------------------------------------------------
// | Exports
// ---------------------------------------------------------
Expand Down Expand Up @@ -108,6 +111,7 @@ export { NzModalSubject } from './modal/nz-modal-subject.service';
// Tokens (eg. global services' config)
export { NZ_MESSAGE_CONFIG } from './message/nz-message-config';
export { NZ_NOTIFICATION_CONFIG } from './notification/nz-notification-config';
export { NZ_ROOT_CONFIG, NzRootConfig } from './root/nz-root-config';

// ---------------------------------------------------------
// | Root module
Expand Down Expand Up @@ -159,13 +163,14 @@ export { NZ_NOTIFICATION_CONFIG } from './notification/nz-notification-config';
})
export class NgZorroAntdModule {

static forRoot(): ModuleWithProviders {
static forRoot(options?: NzRootConfig): ModuleWithProviders {
return {
ngModule: NgZorroAntdModule,
providers: [
// Services
NzNotificationService,
NzMessageService
NzMessageService,
{ provide: NZ_ROOT_CONFIG, useValue: options },
]
};
}
Expand Down
32 changes: 32 additions & 0 deletions src/components/root/nz-root-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { createNzRootInitializer, NzRootConfig } from './nz-root-config';

describe('NzRootConfig', () => {
let mockDocument: Document;
let mockConfig: NzRootConfig;
let mockElement: HTMLDivElement;

beforeEach(() => {
mockDocument = { head: { appendChild: () => null }, createElement: () => null } as any;
mockConfig = { extraFontName: '', extraFontUrl: '' } as any;
mockElement = {} as any;

spyOn(mockDocument, 'createElement').and.returnValue(mockElement);
spyOn(mockDocument.head, 'appendChild');
});

it('should apply extra font style when option provided', () => {
const iniliatizer = createNzRootInitializer(mockDocument, mockConfig);
iniliatizer();

expect(mockDocument.createElement).toHaveBeenCalledWith('style');
expect(mockDocument.head.appendChild).toHaveBeenCalledWith(mockElement);
});

it('should not apply extra font style when option not provided', () => {
const iniliatizer = createNzRootInitializer(mockDocument);
iniliatizer();

expect(mockDocument.createElement).not.toHaveBeenCalled();
expect(mockDocument.head.appendChild).not.toHaveBeenCalled();
});
});
32 changes: 32 additions & 0 deletions src/components/root/nz-root-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { InjectionToken } from '@angular/core';

export interface NzRootConfig {
extraFontName: string;
extraFontUrl: string;
}

export const NZ_ROOT_CONFIG = new InjectionToken<NzRootConfig>('NzRootConfig');

export function createNzRootInitializer(document: Document, options?: NzRootConfig) {
return function nzRootInitializer() {
if (options) {
const style = document.createElement('style');
style.innerHTML = `
@font-face {
font-family: '${options.extraFontName}';
src: url('${options.extraFontUrl}.eot'); /* IE9*/
src:
/* IE6-IE8 */
url('${options.extraFontUrl}.eot?#iefix') format('embedded-opentype'),
/* chrome、firefox */
url('${options.extraFontUrl}.woff') format('woff'),
/* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
url('${options.extraFontUrl}.ttf') format('truetype'),
/* iOS 4.1- */
url('${options.extraFontUrl}.svg#iconfont') format('svg');
}
`;
document.head.appendChild(style);
}
}
}
11 changes: 11 additions & 0 deletions src/components/root/nz-root-style.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component, ViewEncapsulation } from '@angular/core';

@Component({
template : ``,
styleUrls : [
'../style/index.less',
'./style/index.less',
],
encapsulation : ViewEncapsulation.None,
})
export class NzRootStyleComponent { }
52 changes: 52 additions & 0 deletions src/components/root/nz-root.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ComponentFactoryResolver, Injector, ComponentRef, ComponentFactory, APP_INITIALIZER } from '@angular/core';
import { async, inject, TestBed } from '@angular/core/testing';
import { DOCUMENT } from '@angular/common';
import { NzRootComponent } from './nz-root.component';
import { NZ_ROOT_CONFIG, NzRootConfig } from './nz-root-config';

describe('NzRootComponent', () => {
let component: NzRootComponent;
let mockDocument: Document;
let mockConfig: NzRootConfig;
let mockElement: HTMLDivElement;

beforeEach(() => {
mockDocument = { head: { appendChild: () => null }, createElement: () => null } as any;
mockConfig = { extraFontName: '', extraFontUrl: '' } as any;
mockElement = {} as any;

spyOn(mockDocument, 'createElement').and.returnValue(mockElement);
spyOn(mockDocument.head, 'appendChild');
});

it('should apply extra font style when input being set & option not provided', () => {
component = new NzRootComponent(mockDocument, undefined);
component.nzExtraFontName = 'some-name';
component.nzExtraFontUrl = 'some-url';

component.ngOnInit();

expect(mockDocument.createElement).toHaveBeenCalledWith('style');
expect(mockDocument.head.appendChild).toHaveBeenCalledWith(mockElement);
});

it('should not apply extra font style when option being provided', () => {
component = new NzRootComponent(mockDocument, mockConfig);
component.nzExtraFontName = 'some-name';
component.nzExtraFontUrl = 'some-url';

component.ngOnInit();

expect(mockDocument.createElement).not.toHaveBeenCalled();
expect(mockDocument.head.appendChild).not.toHaveBeenCalled();
});

it('should not apply extra font style when option being provided', () => {
component = new NzRootComponent(mockDocument, undefined);

component.ngOnInit();

expect(mockDocument.createElement).not.toHaveBeenCalled();
expect(mockDocument.head.appendChild).not.toHaveBeenCalled();
});
});
43 changes: 15 additions & 28 deletions src/components/root/nz-root.component.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,37 @@
import {
Component,
Input,
ViewEncapsulation,
OnInit,
Inject
Inject,
Optional,
} from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { NZ_ROOT_CONFIG, NzRootConfig, createNzRootInitializer} from './nz-root-config';

@Component({
selector : '[nz-root],nz-root',
encapsulation: ViewEncapsulation.None,
template : `
selector : '[nz-root],nz-root',
template : `
<ng-content></ng-content>
`,
styleUrls : [
'../style/index.less',
'./style/index.less',
]
})
export class NzRootComponent implements OnInit {

// Extra font definition
@Input() nzExtraFontName: string;
@Input() nzExtraFontUrl: string;

constructor(@Inject(DOCUMENT) private _document: Document) { }
constructor(
@Inject(DOCUMENT) private _document: Document,
// Cannot use type annotation here due to https://github.com/angular/angular-cli/issues/2034
// Should be revisited after AOT being made the only option
@Inject(NZ_ROOT_CONFIG) @Optional() private options: any | undefined,
) { }

ngOnInit() {
if (this.nzExtraFontName && this.nzExtraFontUrl) {
const style = this._document.createElement('style');
style.innerHTML = `
@font-face {
font-family: '${this.nzExtraFontName}';
src: url('${this.nzExtraFontUrl}.eot'); /* IE9*/
src:
/* IE6-IE8 */
url('${this.nzExtraFontUrl}.eot?#iefix') format('embedded-opentype'),
/* chrome、firefox */
url('${this.nzExtraFontUrl}.woff') format('woff'),
/* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
url('${this.nzExtraFontUrl}.ttf') format('truetype'),
/* iOS 4.1- */
url('${this.nzExtraFontUrl}.svg#iconfont') format('svg');
}
`;
this._document.head.appendChild(style);
if (this.nzExtraFontName && this.nzExtraFontUrl && !this.options) {
const options: NzRootConfig = { extraFontName: this.nzExtraFontName, extraFontUrl: this.nzExtraFontUrl };
const initializer = createNzRootInitializer(this._document, options);
initializer();
}
}
}
58 changes: 58 additions & 0 deletions src/components/root/nz-root.module.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ComponentFactoryResolver, Injector, ComponentRef, ComponentFactory, APP_INITIALIZER } from '@angular/core';
import { async, inject, TestBed } from '@angular/core/testing';
import { NzRootModule } from './nz-root.module';
import { NzRootStyleComponent } from './nz-root-style.component';

describe('NzRootModule', () => {
let ngModule: NzRootModule;
let mockDocument: Document;
let mockInjector: Injector;
let mockFactoryResolver: ComponentFactoryResolver;
let mockElement: HTMLDivElement;
let mockComponentFactory: ComponentFactory<NzRootStyleComponent>;
let mockComponentRef: ComponentRef<NzRootStyleComponent>;

beforeEach(() => {
mockDocument = { createElement: () => null } as any;
mockInjector = {} as any;
mockFactoryResolver = { resolveComponentFactory: () => null } as any;
mockElement = {} as any;
mockComponentFactory = { create: () => null } as any;
mockComponentRef = { destroy: () => null } as any;

spyOn(mockDocument, 'createElement').and.returnValue(mockElement);
spyOn(mockComponentRef, 'destroy');
spyOn(mockComponentFactory, 'create').and.returnValue(mockComponentRef);
spyOn(mockFactoryResolver, 'resolveComponentFactory').and.returnValue(mockComponentFactory);
});

beforeEach(() => {
ngModule = new NzRootModule(mockDocument, mockInjector, mockFactoryResolver);
});

it('should create style component when start', () => {
expect(mockDocument.createElement).toHaveBeenCalledWith('div');
expect(mockFactoryResolver.resolveComponentFactory).toHaveBeenCalledWith(NzRootStyleComponent);
expect(mockComponentFactory.create).toHaveBeenCalledWith(mockInjector, null, mockElement);
});

it('should destroy style component when terminate', () => {
ngModule.ngOnDestroy();

expect(mockComponentRef.destroy).toHaveBeenCalled();
})
});

describe('NzRootModule with Angular integration', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [NzRootModule],
declarations: [],
providers: [],
}).compileComponents();
}));

it('should provide APP_INITIALIZER', inject([APP_INITIALIZER], (initializers: Function[]) => {
expect(initializers.some(x => x.name === 'nzRootInitializer')).toBe(true);
}));
});
28 changes: 22 additions & 6 deletions src/components/root/nz-root.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import { NgModule } from '@angular/core';
import { NgModule, OnDestroy, ComponentRef, ComponentFactoryResolver, Inject, Optional, Injector, APP_INITIALIZER } from '@angular/core';
import { CommonModule, DOCUMENT } from '@angular/common';
import { NzRootComponent } from './nz-root.component';
import { CommonModule } from '@angular/common';
import { NzRootStyleComponent } from './nz-root-style.component';
import { NZ_ROOT_CONFIG, createNzRootInitializer } from './nz-root-config';

@NgModule({
exports : [ NzRootComponent ],
declarations: [ NzRootComponent ],
imports : [ CommonModule ]
exports : [ NzRootComponent ],
declarations : [ NzRootComponent, NzRootStyleComponent ],
imports : [ CommonModule ],
entryComponents : [ NzRootStyleComponent ],
providers : [
{ provide: APP_INITIALIZER, multi: true, useFactory: createNzRootInitializer, deps: [DOCUMENT, [new Optional(), NZ_ROOT_CONFIG]] },
],
})
export class NzRootModule implements OnDestroy {
private styleHostComponent: ComponentRef<NzRootStyleComponent>;

export class NzRootModule {
constructor(@Inject(DOCUMENT) _document: Document, injector: Injector, resolver: ComponentFactoryResolver) {
const componentFactory = resolver.resolveComponentFactory(NzRootStyleComponent);
const div = _document.createElement('div');
this.styleHostComponent = componentFactory.create(injector, null, div);
}

ngOnDestroy() {
this.styleHostComponent.destroy();
}
}
Loading

0 comments on commit 9de3de1

Please sign in to comment.