Skip to content

Commit

Permalink
refactor(popover): add a capability to pass content context
Browse files Browse the repository at this point in the history
  • Loading branch information
tibing-old-email authored and nnixaa committed Feb 20, 2018
1 parent 515636c commit c044e5d
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 16 deletions.
19 changes: 16 additions & 3 deletions e2e/popover.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { browser, by, element } from 'protractor';

const contentTemplate = by.css('nb-card:nth-child(1) button:nth-child(1)');
const contentComponent = by.css('nb-card:nth-child(1) button:nth-child(2)');
const contentString = by.css('nb-card:nth-child(1) button:nth-child(3)');
const contentComponentWithContext = by.css('nb-card:nth-child(1) button:nth-child(2)');
const contentTemplateWithContext = by.css('nb-card:nth-child(1) button:nth-child(3)');
const contentString = by.css('nb-card:nth-child(1) button:nth-child(4)');
const placementRight = by.css('nb-card:nth-child(2) button:nth-child(1)');
const placementBottom = by.css('nb-card:nth-child(2) button:nth-child(2)');
const placementTop = by.css('nb-card:nth-child(2) button:nth-child(3)');
Expand All @@ -25,7 +26,7 @@ describe('nb-popover', () => {
});

it('render component ref', () => {
element(contentComponent).click();
element(contentComponentWithContext).click();
const containerContent = element(popover).element(by.css('nb-dynamic-to-add'));
expect(containerContent.isPresent()).toBeTruthy();
});
Expand Down Expand Up @@ -151,4 +152,16 @@ describe('nb-popover', () => {
const container = element(popover);
expect(container.isPresent()).toBeTruthy();
});

it('have to render component with context', () => {
element(contentComponentWithContext).click();
const text = element(popover).element(by.css('nb-dynamic-to-add > div > strong')).getText();
expect(text).toEqual('hello from dynamically inserted component: Example context');
});

it('have to render template with context', () => {
element(contentTemplateWithContext).click();
const text = element(popover).element(by.css('nb-dynamic-to-add > div > strong')).getText();
expect(text).toEqual('hello from dynamically inserted component: Example context');
});
});
10 changes: 7 additions & 3 deletions src/app/layout-test/theme-dynamic-test.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

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

import { NbThemeService } from '@nebular/theme';

@Component({
selector: 'nb-dynamic-to-add',
template: `
<div>
<strong>hello from dynamically inserted component</strong>
<strong>hello from dynamically inserted component: {{text}}</strong>
</div>
`,
})
export class NbDynamicToAddComponent {}
export class NbDynamicToAddComponent {

@Input()
text: string = '';
}

@Component({
selector: 'nb-dynamic-test',
Expand Down
22 changes: 17 additions & 5 deletions src/app/popover-test/popover-test.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,35 @@ import { NbDynamicToAddComponent } from '../layout-test/theme-dynamic-test.compo
<nb-card>
<nb-card-header>Content Type</nb-card-header>
<nb-card-body>
<button class="btn btn-info" [nbPopover]="popoverTemplate" nbPopoverPlacement="right">Template Ref Test
<button class="btn btn-info" [nbPopover]="popoverTemplate"
[nbPopoverContext]="{text: 'Example context'}"
nbPopoverPlacement="right">
Template Ref Test
</button>
<ng-template #popoverTemplate>
<ng-template #popoverTemplate let-text="text">
<nb-card [style.margin.px]="0" [style.boxShadow]="'none'">
<nb-card-body>
<label class="form-control-label" for="success-form-control">Success Form Control</label>
<label class="form-control-label" for="success-form-control">{{text}}</label>
<input class="form-control form-control-success" id="success-form-control"
placeholder="Success Form Control">
<span class="form-control-feedback">Help text</span>
</nb-card-body>
</nb-card>
</ng-template>
<button class="btn btn-warning" [nbPopover]="customPopoverComponent">
Component Test
<button class="btn btn-warning" [nbPopover]="customPopoverComponent"
[nbPopoverContext]="{text: 'Example context'}">
Component with context test
</button>
<button class="btn btn-warning" [nbPopover]="popoverTemplateWithContext"
[nbPopoverContext]="{text: 'Example context'}">
Template with context test
</button>
<ng-template #popoverTemplateWithContext let-text="text">
<nb-dynamic-to-add [text]="text"></nb-dynamic-to-add>
</ng-template>
<button class="btn btn-primary" nbPopover="Hi, I'm popover!" nbPopoverPlacement="bottom">String Test
</button>
</nb-card-body>
Expand Down
34 changes: 32 additions & 2 deletions src/framework/theme/components/popover/popover.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

import { Component, HostBinding, Input, TemplateRef, Type } from '@angular/core';
import { ChangeDetectorRef, Component, HostBinding, Input, TemplateRef, Type, ViewChild } from '@angular/core';
import { NbPopoverPlacement } from './helpers/model';
import { NgComponentOutlet } from '@angular/common';

/**
* Popover can be one of the following types:
Expand All @@ -31,7 +32,9 @@ export type NbPopoverContent = string | TemplateRef<any> | Type<any>;
template: `
<span class="arrow"></span>
<ng-container *ngIf="isTemplate" [ngTemplateOutlet]="content"></ng-container>
<ng-container *ngIf="isTemplate">
<ng-container *ngTemplateOutlet="content; context: context"></ng-container>
</ng-container>
<ng-container *ngIf="isComponent" [ngComponentOutlet]="content"></ng-container>
<ng-container *ngIf="isPrimitive">
<div class="primitive-popover">{{content}}</div>
Expand All @@ -46,6 +49,12 @@ export class NbPopoverComponent {
@Input()
content: NbPopoverContent;

/**
* Context which will be passed to rendered component instance.
* */
@Input()
context: Object;

/**
* Popover placement relatively host element.
* */
Expand All @@ -61,6 +70,24 @@ export class NbPopoverComponent {
@HostBinding('style.left.px')
positionLeft: number;

/**
* If content type is TemplateRef we're passing context as template outlet param.
* But if we have custom component content we're just assigning passed context to the component instance.
* */
@ViewChild(NgComponentOutlet)
set componentOutlet(el) {
if (this.isComponent) {
Object.assign(el._componentRef.instance, this.context);
/**
* Change detection have to performed here, because another way applied context
* will be rendered on the next change detection loop and
* we'll have incorrect positioning. Because rendered component may change its size
* based on the context.
* */
this.changeDetectorRef.detectChanges();
}
}

/**
* Check that content is a TemplateRef.
*
Expand All @@ -85,4 +112,7 @@ export class NbPopoverComponent {
get isPrimitive(): boolean {
return !this.isTemplate && !this.isComponent;
}

constructor(private changeDetectorRef: ChangeDetectorRef) {
}
}
13 changes: 10 additions & 3 deletions src/framework/theme/components/popover/popover.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ export class NbPopoverDirective implements OnInit, OnDestroy {
@Input('nbPopover')
content: NbPopoverContent;

/**
* Container content context. Will be applied to the rendered component.
* */
@Input('nbPopoverContext')
context: Object;

/**
* Position will be calculated relatively host element based on the placement.
* Can be top, right, bottom and left.
Expand Down Expand Up @@ -222,7 +228,7 @@ export class NbPopoverDirective implements OnInit, OnDestroy {
.pipe(takeWhile(() => this.alive))
.subscribe((containerRef: ComponentRef<NbPopoverComponent>) => {
this.containerRef = containerRef;
this.patchPopoverContent(this.content);
this.patchPopover(this.content, this.context);
/*
* Have to call detectChanges because on this phase {@link NbPopoverComponent} isn't inserted in the DOM
* and haven't got calculated size.
Expand Down Expand Up @@ -258,10 +264,11 @@ export class NbPopoverDirective implements OnInit, OnDestroy {
}

/*
* Set container content.
* Set container content and context.
* */
private patchPopoverContent(content: NbPopoverContent) {
private patchPopover(content: NbPopoverContent, context: Object) {
this.container.content = content;
this.container.context = context;
}

/*
Expand Down

0 comments on commit c044e5d

Please sign in to comment.