diff --git a/src/framework/theme/components/context-menu/context-menu.component.ts b/src/framework/theme/components/context-menu/context-menu.component.ts index c3d9f603ac..5c04559257 100644 --- a/src/framework/theme/components/context-menu/context-menu.component.ts +++ b/src/framework/theme/components/context-menu/context-menu.component.ts @@ -7,7 +7,7 @@ import { Component, Input } from '@angular/core'; import { NbMenuItem } from '../../components/menu/menu.service'; -import { NbPositionedContainer } from '../cdk'; +import { NbPositionedContainer, NbRenderableContainer } from '../cdk'; /** * Context menu component used as content within NbContextMenuDirective. @@ -23,10 +23,21 @@ import { NbPositionedContainer } from '../cdk'; styleUrls: ['./context-menu.component.scss'], template: ` - + `, }) -export class NbContextMenuComponent extends NbPositionedContainer { +export class NbContextMenuComponent extends NbPositionedContainer implements NbRenderableContainer { + @Input() items: NbMenuItem[] = []; @Input() tag: string; + + @Input() + context: { items: NbMenuItem[], tag?: string } = { items: [] }; + + + /** + * The method is empty since we don't need to do anything additionally + * render is handled by change detection + */ + renderContent() {} } diff --git a/src/framework/theme/components/context-menu/context-menu.directive.ts b/src/framework/theme/components/context-menu/context-menu.directive.ts index 762727db4f..0c617760b9 100644 --- a/src/framework/theme/components/context-menu/context-menu.directive.ts +++ b/src/framework/theme/components/context-menu/context-menu.directive.ts @@ -6,32 +6,28 @@ import { AfterViewInit, - ComponentFactoryResolver, ComponentRef, Directive, ElementRef, - Inject, Input, + OnChanges, OnDestroy, + OnInit, } from '@angular/core'; import { filter, takeWhile } from 'rxjs/operators'; import { - createContainer, NbAdjustableConnectedPositionStrategy, NbAdjustment, + NbDynamicOverlay, + NbDynamicOverlayController, + NbDynamicOverlayHandler, NbOverlayRef, - NbOverlayService, NbPosition, - NbPositionBuilderService, NbTrigger, - NbTriggerStrategy, - NbTriggerStrategyBuilderService, - patch, } from '../cdk'; import { NbContextMenuComponent } from './context-menu.component'; import { NbMenuItem, NbMenuService } from '../menu/menu.service'; -import { NB_DOCUMENT } from '../../theme.options'; /** * Full featured context menu directive. @@ -106,8 +102,11 @@ import { NB_DOCUMENT } from '../../theme.options'; * * @stacked-example(Manual Control, context-menu/context-menu-noop.component) * */ -@Directive({ selector: '[nbContextMenu]' }) -export class NbContextMenuDirective implements AfterViewInit, OnDestroy { +@Directive({ + selector: '[nbContextMenu]', + providers: [NbDynamicOverlayHandler, NbDynamicOverlay], +}) +export class NbContextMenuDirective implements NbDynamicOverlayController, OnChanges, AfterViewInit, OnDestroy, OnInit { /** * Position will be calculated relatively host element based on the position. @@ -134,9 +133,9 @@ export class NbContextMenuDirective implements AfterViewInit, OnDestroy { * Basic menu items, will be passed to the internal NbMenuComponent. * */ @Input('nbContextMenu') - set setItems(items: NbMenuItem[]) { + set items(items: NbMenuItem[]) { this.validateItems(items); - this.items = items; + this._items = items; }; /** @@ -150,96 +149,62 @@ export class NbContextMenuDirective implements AfterViewInit, OnDestroy { protected container: ComponentRef; protected positionStrategy: NbAdjustableConnectedPositionStrategy; protected alive: boolean = true; - private items: NbMenuItem[] = []; + private _items: NbMenuItem[] = []; - constructor(@Inject(NB_DOCUMENT) protected document, + private dynamicOverlay: NbDynamicOverlay; + + constructor(private hostRef: ElementRef, private menuService: NbMenuService, - private hostRef: ElementRef, - private positionBuilder: NbPositionBuilderService, - private triggerStrategyBuilder: NbTriggerStrategyBuilderService, - private overlay: NbOverlayService, - private componentFactoryResolver: ComponentFactoryResolver) { + private dynamicOverlayHandler: NbDynamicOverlayHandler) { + } + + ngOnInit() { + this.dynamicOverlayHandler + .host(this.hostRef) + .componentType(NbContextMenuComponent); + } + + ngOnChanges() { + this.rebuild(); } ngAfterViewInit() { - this.subscribeOnTriggers(); + this.dynamicOverlay = this.configureDynamicOverlay() + .build(); this.subscribeOnItemClick(); - this.subscribeOnPositionChange(); } - ngOnDestroy() { - this.alive = false; - this.hide(); - if (this.ref) { - this.ref.dispose(); - } + rebuild() { + this.dynamicOverlay = this.configureDynamicOverlay() + .rebuild(); } show() { - if (!this.ref) { - this.createOverlay(); - } - - this.openContextMenu(); + this.dynamicOverlay.show(); } hide() { - if (this.ref) { - this.ref.detach(); - } - - this.container = null; + this.dynamicOverlay.hide(); } toggle() { - if (this.ref && this.ref.hasAttached()) { - this.hide(); - } else { - this.show(); - } - } - - protected createOverlay() { - this.ref = this.overlay.create({ - positionStrategy: this.positionStrategy, - scrollStrategy: this.overlay.scrollStrategies.reposition(), - }); + this.dynamicOverlay.toggle(); } - protected openContextMenu() { - this.container = createContainer(this.ref, NbContextMenuComponent, { - position: this.position, - items: this.items, - tag: this.tag, - }, this.componentFactoryResolver); + ngOnDestroy() { + this.dynamicOverlayHandler.destroy(); } - protected createPositionStrategy(): NbAdjustableConnectedPositionStrategy { - return this.positionBuilder - .connectedTo(this.hostRef) + protected configureDynamicOverlay() { + return this.dynamicOverlayHandler .position(this.position) - .adjustment(this.adjustment); - } - - protected createTriggerStrategy(): NbTriggerStrategy { - return this.triggerStrategyBuilder .trigger(this.trigger) - .host(this.hostRef.nativeElement) - .container(() => this.container) - .build(); - } - - protected subscribeOnPositionChange() { - this.positionStrategy = this.createPositionStrategy(); - this.positionStrategy.positionChange - .pipe(takeWhile(() => this.alive)) - .subscribe((position: NbPosition) => patch(this.container, { position })); - } - - protected subscribeOnTriggers() { - const triggerStrategy = this.createTriggerStrategy(); - triggerStrategy.show$.pipe(takeWhile(() => this.alive)).subscribe(() => this.show()); - triggerStrategy.hide$.pipe(takeWhile(() => this.alive)).subscribe(() => this.hide()); + .adjustment(this.adjustment) + .context({ + position: this.position, + items: this._items, + tag: this.tag, + }); } /* diff --git a/src/framework/theme/components/context-menu/context-menu.spec.ts b/src/framework/theme/components/context-menu/context-menu.spec.ts index e7f9a9b4b3..243c32c9ba 100644 --- a/src/framework/theme/components/context-menu/context-menu.spec.ts +++ b/src/framework/theme/components/context-menu/context-menu.spec.ts @@ -1,69 +1,111 @@ -import { Component, ComponentRef, ElementRef, Inject, Injectable, Input, NgModule, ViewChild } from '@angular/core'; -import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; +import { Component, ElementRef, Input, NgModule, Type, ViewChild } from '@angular/core'; +import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; - -import { of as observableOf, Subject } from 'rxjs'; +import { RouterTestingModule } from '@angular/router/testing'; import { NbThemeModule } from '../../theme.module'; import { NbLayoutModule } from '../layout/layout.module'; import { NbAdjustment, + NbDynamicOverlayHandler, + NbOverlayContent, NbPosition, - NbPositionBuilderService, + NbRenderableContainer, NbTrigger, - NbTriggerStrategy, - NbTriggerStrategyBuilderService, } from '../cdk'; -import { NB_DOCUMENT } from '../../theme.options'; -import { NbContextMenuDirective } from './context-menu.directive'; import { NbMenuModule } from '../menu/menu.module'; +import { NbContextMenuDirective } from './context-menu.directive'; +import { NbContextMenuComponent } from './context-menu.component'; import { NbContextMenuModule } from './context-menu.module'; - @Component({ - selector: 'nb-context-menu-test', + selector: 'nb-context-menu-default-test', template: ` - + `, }) -export class NbContextMenuTestComponent { - @Input() trigger: NbTrigger = NbTrigger.CLICK; +export class NbContextMenuDefaultTestComponent { @ViewChild('button') button: ElementRef; @ViewChild(NbContextMenuDirective) contextMenu: NbContextMenuDirective; items = [{ title: 'User' }, { title: 'Log Out' }]; } -@NgModule({ - imports: [ - RouterTestingModule.withRoutes([]), - NoopAnimationsModule, - NbThemeModule.forRoot(), - NbLayoutModule, - NbMenuModule.forRoot(), - NbContextMenuModule, - ], - declarations: [ - NbContextMenuTestComponent, - ], +@Component({ + selector: 'nb-context-menu-bindings-test', + template: ` + + + + + + `, +}) +export class NbContextMenuBindingsTestComponent { + @ViewChild(NbContextMenuDirective) contextMenu: NbContextMenuDirective; + @ViewChild('button') button: ElementRef; + @Input() trigger = NbTrigger.CLICK; + @Input() position = NbPosition.TOP; + @Input() adjustment = NbAdjustment.CLOCKWISE; + @Input() tag = ''; + @Input() items = [{ title: 'User' }, { title: 'Log Out' }]; +} + +@Component({ + selector: 'nb-context-menu-instance-test', + template: ` + + + + + + + Some Template + `, }) -export class ContextMenuTestModule { +export class NbContextMenuInstanceTestComponent { + @ViewChild(NbContextMenuDirective) contextMenu: NbContextMenuDirective; + @ViewChild('button') button: ElementRef; + + items = [{ title: 'User' }, { title: 'Log Out' }]; } -export class MockPositionBuilder { - positionChange = new Subject(); - _connectedTo: ElementRef; - _position: NbPosition; - _adjustment: NbAdjustment; +const dynamicOverlay = { + show() {}, + hide() {}, + toggle() {}, + destroy() {}, +}; + +export class NbDynamicOverlayHandlerMock { + _componentType: Type; + _host: ElementRef; + _context: Object = {}; + _content: NbOverlayContent; + _trigger: NbTrigger = NbTrigger.NOOP; + _position: NbPosition = NbPosition.TOP; + _adjustment: NbAdjustment = NbAdjustment.NOOP; + _offset: number; + + constructor() { + } + + host(host: ElementRef) { + this._host = host; + return this; + } - connectedTo(connectedTo: ElementRef) { - this._connectedTo = connectedTo; + trigger(trigger: NbTrigger) { + this._trigger = trigger; return this; } @@ -77,158 +119,330 @@ export class MockPositionBuilder { return this; } - offset() { + componentType(componentType: Type) { + this._componentType = componentType; return this; - }; - - attach() { - }; - - apply() { - }; - - detach() { - }; + } - dispose() { - }; -} + content(content: NbOverlayContent) { + this._content = content; + return this; + } -@Injectable() -export class MockTriggerStrategyBuilder { + context(context: {}) { + this._context = context; + return this; + } - _host: HTMLElement; - _container: () => ComponentRef; - _trigger: NbTrigger; + offset(offset: number) { + this._offset = offset; + return this; + } - constructor(@Inject(NB_DOCUMENT) public _document: Document) { + build() { + return dynamicOverlay; } - trigger(trigger: NbTrigger): this { - this._trigger = trigger; - return this; + rebuild() { + return dynamicOverlay; } - host(host: HTMLElement): this { - this._host = host; - return this; + connect() { } - container(container: () => ComponentRef): this { - this._container = container; - return this; + disconnect() { } - build(): NbTriggerStrategy { - return { - show$: observableOf(null), - hide$: observableOf(null), - } as NbTriggerStrategy; + destroy() { } } -describe('Directive: NbContextMenuDirective', () => { - beforeEach(() => { - TestBed.configureTestingModule({ imports: [ContextMenuTestModule] }); - }); +const TEST_COMPONENTS = [ + NbContextMenuDefaultTestComponent, + NbContextMenuBindingsTestComponent, + NbContextMenuInstanceTestComponent, +]; - let fixture: ComponentFixture; +@NgModule({ + imports: [NbLayoutModule, NbContextMenuModule], + exports: [...TEST_COMPONENTS], + declarations: [...TEST_COMPONENTS], +}) +class ContextMenuTestModule { } - beforeEach(() => { - fixture = TestBed.createComponent(NbContextMenuTestComponent); - fixture.detectChanges(); - }); +describe('Directive: NbContextMenuDirective', () => { - afterEach(() => { - fixture.destroy(); - }); + const overlayHandler = new NbDynamicOverlayHandlerMock(); + + beforeEach(async(() => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [ + NoopAnimationsModule, + RouterTestingModule.withRoutes([]), + NbThemeModule.forRoot(), + NbMenuModule.forRoot(), + ContextMenuTestModule, + ], + }); + })); - it('should render context menu', () => { - fixture.componentInstance.contextMenu.show(); - fixture.detectChanges(); + describe('smoke ', () => { - const menu = fixture.nativeElement.querySelector('nb-context-menu nb-menu'); - expect(menu).toBeTruthy(); - }); + let fixture: ComponentFixture; - it('should hide', fakeAsync(() => { - let menu; - fixture.componentInstance.contextMenu.show(); - fixture.detectChanges(); + afterEach(() => { + fixture.destroy(); + }); - menu = fixture.nativeElement.querySelector('nb-context-menu nb-menu'); - expect(menu).toBeTruthy(); - fixture.componentInstance.contextMenu.hide(); - fixture.detectChanges(); + it('should render string', () => { + fixture = TestBed.createComponent(NbContextMenuDefaultTestComponent); + fixture.detectChanges(); + fixture.componentInstance.contextMenu.show(); + fixture.detectChanges(); - tick(); // we need this tick for animations - menu = fixture.nativeElement.querySelector('nb-context-menu nb-menu'); - expect(menu).toBeFalsy(); - })); + const textContainer = fixture.nativeElement.querySelector('nb-menu'); + expect(textContainer.textContent).toContain('Log Out'); + }); - it('should toogle', fakeAsync(() => { - let menu; + it('should hide', () => fakeAsync(() => { + fixture = TestBed.createComponent(NbContextMenuDefaultTestComponent); + fixture.detectChanges(); + fixture.componentInstance.contextMenu.show(); + fixture.detectChanges(); - fixture.componentInstance.contextMenu.show(); - fixture.detectChanges(); - menu = fixture.nativeElement.querySelector('nb-context-menu nb-menu'); - expect(menu).toBeTruthy(); - fixture.componentInstance.contextMenu.toggle(); - fixture.detectChanges(); + const textContainer = fixture.nativeElement.querySelector('nb-menu'); + expect(textContainer.textContent).toContain('Logout'); + fixture.componentInstance.contextMenu.hide(); + fixture.detectChanges(); - tick(); // we need this tick for animations - const tooltip = fixture.nativeElement.querySelector('nb-context-menu'); - expect(tooltip).toBeNull(); + tick(); // we need this tick for animations + const contextMenu = fixture.nativeElement.querySelector('nb-menu'); + expect(contextMenu).toBeNull(); + })); - fixture.componentInstance.contextMenu.toggle(); - fixture.detectChanges(); - tick(); + it('should render different items', () => { + fixture = TestBed.createComponent(NbContextMenuDefaultTestComponent); + fixture.detectChanges(); - menu = fixture.nativeElement.querySelector('nb-context-menu nb-menu'); - expect(menu).toBeTruthy(); - })); + fixture.componentInstance.contextMenu.show(); + fixture.detectChanges(); - it('should build position strategy', () => { - const mockPositionBuilder = new MockPositionBuilder(); - TestBed.resetTestingModule(); - TestBed.configureTestingModule({ - imports: [ContextMenuTestModule], - providers: [{ provide: NbPositionBuilderService, useValue: mockPositionBuilder }], + fixture.componentInstance.items = [ { title: 'Hello' } ]; + fixture.detectChanges(); + const textContainer = fixture.nativeElement.querySelector('nb-menu'); + expect(textContainer.textContent).toContain('Hello'); }); - fixture = TestBed.createComponent(NbContextMenuTestComponent); - fixture.detectChanges(); - expect(mockPositionBuilder._connectedTo.nativeElement).toBe(fixture.componentInstance.button.nativeElement); - expect(mockPositionBuilder._position).toBe(NbPosition.BOTTOM); - expect(mockPositionBuilder._adjustment).toBe(NbAdjustment.CLOCKWISE); }); - it('should build with default trigger strategy', () => { - TestBed.resetTestingModule(); - const bed = TestBed.configureTestingModule({ - imports: [ContextMenuTestModule], - providers: [{ provide: NbTriggerStrategyBuilderService, useClass: MockTriggerStrategyBuilder }], + describe('mocked services', () => { + + beforeEach(async(() => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [ + RouterTestingModule.withRoutes([]), + NbThemeModule.forRoot(), + NbMenuModule.forRoot(), + ContextMenuTestModule, + ], + }) + .overrideComponent(NbContextMenuDirective, { + set: { + providers: [ + { provide: NbDynamicOverlayHandler, useValue: overlayHandler }, + ], + }, + }); + })); + describe('default context-menu', () => { + + let fixture: ComponentFixture; + + afterEach(() => { + fixture.destroy(); + }); + + it('should build', () => { + const componentSpy = spyOn(overlayHandler, 'componentType').and.callThrough(); + const hostSpy = spyOn(overlayHandler, 'host').and.callThrough(); + const positionSpy = spyOn(overlayHandler, 'position').and.callThrough(); + const triggerSpy = spyOn(overlayHandler, 'trigger').and.callThrough(); + const adjustmentSpy = spyOn(overlayHandler, 'adjustment').and.callThrough(); + const contentSpy = spyOn(overlayHandler, 'content').and.callThrough(); + const contextSpy = spyOn(overlayHandler, 'context').and.callThrough(); + const offsetSpy = spyOn(overlayHandler, 'offset').and.callThrough(); + const buildSpy = spyOn(overlayHandler, 'build').and.callThrough(); + const rebuildSpy = spyOn(overlayHandler, 'rebuild').and.callThrough(); + + fixture = TestBed.createComponent(NbContextMenuDefaultTestComponent); + fixture.detectChanges(); + + expect(componentSpy).toHaveBeenCalledTimes(1); + expect(componentSpy).toHaveBeenCalledWith(NbContextMenuComponent); + expect(hostSpy).toHaveBeenCalledWith(fixture.componentInstance.button); + expect(hostSpy).toHaveBeenCalledTimes(1); + expect(offsetSpy).toHaveBeenCalledTimes(0); + expect(positionSpy).toHaveBeenCalledTimes(2); + expect(positionSpy).toHaveBeenCalledWith(NbPosition.BOTTOM); + expect(triggerSpy).toHaveBeenCalledTimes(2); + expect(triggerSpy).toHaveBeenCalledWith(NbTrigger.CLICK); + expect(adjustmentSpy).toHaveBeenCalledTimes(2); + expect(adjustmentSpy).toHaveBeenCalledWith(NbAdjustment.CLOCKWISE); + expect(contentSpy).toHaveBeenCalledTimes(0); + expect(contextSpy).toHaveBeenCalledTimes(2); + expect(contextSpy).toHaveBeenCalledWith({ + position: NbPosition.BOTTOM, + items: [{ title: 'User' }, { title: 'Log Out' }], + tag: undefined, + }); + expect(buildSpy).toHaveBeenCalledTimes(1); + expect(rebuildSpy).toHaveBeenCalledTimes(1); + }); + + it('should show/hide/toggle', () => { + fixture = TestBed.createComponent(NbContextMenuDefaultTestComponent); + fixture.detectChanges(); + const showSpy = spyOn(dynamicOverlay, 'show').and.callThrough(); + const hideSpy = spyOn(dynamicOverlay, 'hide').and.callThrough(); + const toggleSpy = spyOn(dynamicOverlay, 'toggle').and.callThrough(); + + fixture.componentInstance.contextMenu.show(); + fixture.detectChanges(); + + expect(showSpy).toHaveBeenCalledTimes(1); + expect(hideSpy).toHaveBeenCalledTimes(0); + expect(toggleSpy).toHaveBeenCalledTimes(0); + + fixture.componentInstance.contextMenu.hide(); + fixture.detectChanges(); + + expect(showSpy).toHaveBeenCalledTimes(1); + expect(hideSpy).toHaveBeenCalledTimes(1); + expect(toggleSpy).toHaveBeenCalledTimes(0); + + + fixture.componentInstance.contextMenu.toggle(); + fixture.detectChanges(); + + expect(showSpy).toHaveBeenCalledTimes(1); + expect(hideSpy).toHaveBeenCalledTimes(1); + expect(toggleSpy).toHaveBeenCalledTimes(1); + }); }); - const mockTriggerStrategy = bed.get(NbTriggerStrategyBuilderService); - fixture = TestBed.createComponent(NbContextMenuTestComponent); - fixture.detectChanges(); - expect(mockTriggerStrategy._trigger).toBe(NbTrigger.CLICK); - }); - - it('should build with custom trigger strategy', () => { - TestBed.resetTestingModule(); - const bed = TestBed.configureTestingModule({ - imports: [ContextMenuTestModule], - providers: [{ provide: NbTriggerStrategyBuilderService, useClass: MockTriggerStrategyBuilder }], + describe('binding contextMenu', () => { + + let fixture: ComponentFixture; + + afterEach(() => { + fixture.destroy(); + }); + + it('should rebuild', () => { + + const componentSpy = spyOn(overlayHandler, 'componentType').and.callThrough(); + const hostSpy = spyOn(overlayHandler, 'host').and.callThrough(); + const positionSpy = spyOn(overlayHandler, 'position').and.callThrough(); + const triggerSpy = spyOn(overlayHandler, 'trigger').and.callThrough(); + const adjustmentSpy = spyOn(overlayHandler, 'adjustment').and.callThrough(); + const contentSpy = spyOn(overlayHandler, 'content').and.callThrough(); + const contextSpy = spyOn(overlayHandler, 'context').and.callThrough(); + const offsetSpy = spyOn(overlayHandler, 'offset').and.callThrough(); + const buildSpy = spyOn(overlayHandler, 'build').and.callThrough(); + const rebuildSpy = spyOn(overlayHandler, 'rebuild').and.callThrough(); + + fixture = TestBed.createComponent(NbContextMenuBindingsTestComponent); + fixture.detectChanges(); + + fixture.componentInstance.adjustment = NbAdjustment.HORIZONTAL; + fixture.componentInstance.trigger = NbTrigger.HOVER; + fixture.componentInstance.items = [{ title: 'New' }]; + fixture.componentInstance.tag = 'new'; + fixture.componentInstance.position = NbPosition.LEFT; + + fixture.detectChanges(); + + expect(componentSpy).toHaveBeenCalledTimes(1); + expect(componentSpy).toHaveBeenCalledWith(NbContextMenuComponent); + expect(hostSpy).toHaveBeenCalledWith(fixture.componentInstance.button); + expect(hostSpy).toHaveBeenCalledTimes(1); + expect(offsetSpy).toHaveBeenCalledTimes(0); + expect(positionSpy).toHaveBeenCalledTimes(3); + expect(positionSpy).toHaveBeenCalledWith(NbPosition.LEFT); + expect(triggerSpy).toHaveBeenCalledTimes(3); + expect(triggerSpy).toHaveBeenCalledWith(NbTrigger.HOVER); + expect(adjustmentSpy).toHaveBeenCalledTimes(3); + expect(adjustmentSpy).toHaveBeenCalledWith(NbAdjustment.HORIZONTAL); + expect(contentSpy).toHaveBeenCalledTimes(0); + expect(contextSpy).toHaveBeenCalledTimes(3); + expect(contextSpy).toHaveBeenCalledWith({ + position: NbPosition.LEFT, + items: [{ title: 'New' }], + tag: 'new', + }); + expect(buildSpy).toHaveBeenCalledTimes(1); + expect(rebuildSpy).toHaveBeenCalledTimes(2); + }); }); - const mockTriggerStrategy = bed.get(NbTriggerStrategyBuilderService); - fixture = TestBed.createComponent(NbContextMenuTestComponent); - fixture.componentInstance.trigger = NbTrigger.HOVER; - fixture.detectChanges(); - expect(mockTriggerStrategy._trigger).toBe(NbTrigger.HOVER); + describe('instance context-menu', () => { + + let fixture: ComponentFixture; + + afterEach(() => { + fixture.destroy(); + }); + + it('should rebuild', () => { + + const componentSpy = spyOn(overlayHandler, 'componentType').and.callThrough(); + const hostSpy = spyOn(overlayHandler, 'host').and.callThrough(); + const positionSpy = spyOn(overlayHandler, 'position').and.callThrough(); + const triggerSpy = spyOn(overlayHandler, 'trigger').and.callThrough(); + const adjustmentSpy = spyOn(overlayHandler, 'adjustment').and.callThrough(); + const contentSpy = spyOn(overlayHandler, 'content').and.callThrough(); + const contextSpy = spyOn(overlayHandler, 'context').and.callThrough(); + const offsetSpy = spyOn(overlayHandler, 'offset').and.callThrough(); + const buildSpy = spyOn(overlayHandler, 'build').and.callThrough(); + const rebuildSpy = spyOn(overlayHandler, 'rebuild').and.callThrough(); + + fixture = TestBed.createComponent(NbContextMenuInstanceTestComponent); + fixture.detectChanges(); + + fixture.componentInstance.contextMenu.adjustment = NbAdjustment.HORIZONTAL; + fixture.componentInstance.contextMenu.trigger = NbTrigger.HOVER; + fixture.componentInstance.contextMenu.items = [{ title: 'New' }]; + fixture.componentInstance.contextMenu.tag = 'new'; + fixture.componentInstance.contextMenu.position = NbPosition.LEFT; + + fixture.componentInstance.contextMenu.rebuild(); + + expect(componentSpy).toHaveBeenCalledTimes(1); + expect(componentSpy).toHaveBeenCalledWith(NbContextMenuComponent); + expect(hostSpy).toHaveBeenCalledWith(fixture.componentInstance.button); + expect(hostSpy).toHaveBeenCalledTimes(1); + expect(positionSpy).toHaveBeenCalledTimes(3); + expect(positionSpy).toHaveBeenCalledWith(NbPosition.LEFT); + expect(triggerSpy).toHaveBeenCalledTimes(3); + expect(triggerSpy).toHaveBeenCalledWith(NbTrigger.HOVER); + expect(offsetSpy).toHaveBeenCalledTimes(0); + expect(adjustmentSpy).toHaveBeenCalledTimes(3); + expect(adjustmentSpy).toHaveBeenCalledWith(NbAdjustment.HORIZONTAL); + expect(contentSpy).toHaveBeenCalledTimes(0); + expect(contextSpy).toHaveBeenCalledTimes(3); + expect(contextSpy).toHaveBeenCalledWith({ + position: NbPosition.LEFT, + items: [{ title: 'New' }], + tag: 'new', + }); + expect(buildSpy).toHaveBeenCalledTimes(1); + expect(rebuildSpy).toHaveBeenCalledTimes(2); + }); + + }); }); }); diff --git a/src/framework/theme/index.ts b/src/framework/theme/index.ts index 4635962b4b..e9eaaa52b8 100644 --- a/src/framework/theme/index.ts +++ b/src/framework/theme/index.ts @@ -39,6 +39,7 @@ export * from './components/badge/badge.module'; export * from './components/popover/popover.directive'; export * from './components/popover/popover.module'; export * from './components/context-menu/context-menu.directive'; +export * from './components/context-menu/context-menu.component'; export * from './components/context-menu/context-menu.module'; export * from './components/progress-bar/progress-bar.component'; export * from './components/progress-bar/progress-bar.module';