Skip to content

Commit

Permalink
feat(popover): add support for the 'container' option
Browse files Browse the repository at this point in the history
Closes #852
Closes #853
  • Loading branch information
spongessuck authored and pkozlowski-opensource committed Oct 10, 2016
1 parent e86277f commit d1a22c0
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 2 deletions.
@@ -0,0 +1,16 @@
<p>
Set the <code>container</code> property to "body" to have the popover be appended to the body instead of the triggering element's parent. This option is useful if the element triggering the popover is inside an element that clips its contents (i.e. <code>overflow: hidden</code>).
</p>

<div class='row'>
<div class='card'>
<button type="button" class="btn btn-secondary"
ngbPopover="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
Default popover
</button>
<button type="button" class="btn btn-secondary"
ngbPopover="Vivamus sagittis lacus vel augue laoreet rutrum faucibus." container="body">
Popover appended to body
</button>
</div>
</div>
@@ -0,0 +1,9 @@
import {Component} from '@angular/core';

@Component({
selector: 'ngbd-popover-container',
templateUrl: './popover-container.html',
styles: ['.card { padding: 50px 0; text-align: center; overflow:hidden }']
})
export class NgbdPopoverContainer {
}
14 changes: 13 additions & 1 deletion demo/src/app/components/popover/demos/index.ts
Expand Up @@ -2,9 +2,17 @@ import {NgbdPopoverBasic} from './basic/popover-basic';
import {NgbdPopoverTplcontent} from './tplcontent/popover-tplcontent';
import {NgbdPopoverTriggers} from './triggers/popover-triggers';
import {NgbdPopoverVisibility} from './visibility/popover-visibility';
import {NgbdPopoverContainer} from './container/popover-container';
import {NgbdPopoverConfig} from './config/popover-config';

export const DEMO_DIRECTIVES = [NgbdPopoverBasic, NgbdPopoverTplcontent, NgbdPopoverTriggers, NgbdPopoverVisibility, NgbdPopoverConfig];
export const DEMO_DIRECTIVES = [
NgbdPopoverBasic,
NgbdPopoverTplcontent,
NgbdPopoverTriggers,
NgbdPopoverVisibility,
NgbdPopoverContainer,
NgbdPopoverConfig
];

export const DEMO_SNIPPETS = {
basic: {
Expand All @@ -23,6 +31,10 @@ export const DEMO_SNIPPETS = {
code: require('!!prismjs?lang=typescript!./visibility/popover-visibility'),
markup: require('!!prismjs?lang=markup!./visibility/popover-visibility.html')
},
container: {
code: require('!!prismjs?lang=typescript!./container/popover-container'),
markup: require('!!prismjs?lang=markup!./container/popover-container.html')
},
config: {
code: require('!!prismjs?lang=typescript!./config/popover-config'),
markup: require('!!prismjs?lang=markup!./config/popover-config.html')
Expand Down
4 changes: 4 additions & 0 deletions demo/src/app/components/popover/popover.component.ts
Expand Up @@ -22,6 +22,10 @@ import {DEMO_SNIPPETS} from './demos';
demoTitle="Popover visibility events" [htmlSnippet]="snippets.visibility.markup" [tsSnippet]="snippets.visibility.code">
<ngbd-popover-visibility></ngbd-popover-visibility>
</ngbd-example-box>
<ngbd-example-box
demoTitle="Append popover in the body" [htmlSnippet]="snippets.container.markup" [tsSnippet]="snippets.container.code">
<ngbd-popover-container></ngbd-popover-container>
</ngbd-example-box>
<ngbd-example-box
demoTitle="Global configuration of popovers" [htmlSnippet]="snippets.config.markup" [tsSnippet]="snippets.config.code">
<ngbd-popover-config></ngbd-popover-config>
Expand Down
1 change: 1 addition & 0 deletions src/popover/popover-config.spec.ts
Expand Up @@ -6,5 +6,6 @@ describe('ngb-popover-config', () => {

expect(config.placement).toBe('top');
expect(config.triggers).toBe('click');
expect(config.container).toBeUndefined();
});
});
1 change: 1 addition & 0 deletions src/popover/popover-config.ts
Expand Up @@ -9,4 +9,5 @@ import {Injectable} from '@angular/core';
export class NgbPopoverConfig {
placement: 'top' | 'bottom' | 'left' | 'right' = 'top';
triggers = 'click';
container?: string;
}
23 changes: 23 additions & 0 deletions src/popover/popover.spec.ts
Expand Up @@ -69,6 +69,7 @@ describe('ngb-popover', () => {
expect(windowEl).toHaveCssClass('popover-top');
expect(windowEl.textContent.trim()).toBe('TitleGreat tip!');
expect(windowEl.getAttribute('role')).toBe('tooltip');
expect(windowEl.parentNode).toBe(fixture.nativeElement);

directive.triggerEventHandler('click', {});
fixture.detectChanges();
Expand All @@ -90,6 +91,7 @@ describe('ngb-popover', () => {
expect(windowEl).toHaveCssClass(`popover-${defaultConfig.placement}`);
expect(windowEl.textContent.trim()).toBe('TitleHello, World!');
expect(windowEl.getAttribute('role')).toBe('tooltip');
expect(windowEl.parentNode).toBe(fixture.nativeElement);

directive.triggerEventHandler('click', {});
fixture.detectChanges();
Expand Down Expand Up @@ -191,6 +193,25 @@ describe('ngb-popover', () => {
});
});

describe('container', () => {

it('should be appended to the element matching the selector passed to "container"', () => {
const selector = 'body';
const fixture = createTestComponent(`<div ngbPopover="Great tip!" container="` + selector + `"></div>`);
const directive = fixture.debugElement.query(By.directive(NgbPopover));

directive.triggerEventHandler('click', {});
fixture.detectChanges();
let windowEl = getWindow(fixture);
expect(windowEl).toBeNull();

const container = window.document.querySelector(selector);
windowEl = container.querySelector('ngb-popover-window');
expect(windowEl.parentNode).toBe(container);
});

});

describe('visibility', () => {
it('should emit events when showing and hiding popover', () => {
const fixture = createTestComponent(
Expand Down Expand Up @@ -382,6 +403,7 @@ describe('ngb-popover', () => {
config = c;
config.placement = 'bottom';
config.triggers = 'hover';
config.container = 'body';
}));

it('should initialize inputs with provided config', () => {
Expand All @@ -392,6 +414,7 @@ describe('ngb-popover', () => {

expect(popover.placement).toBe(config.placement);
expect(popover.triggers).toBe(config.triggers);
expect(popover.container).toBe(config.container);
});
});

Expand Down
15 changes: 14 additions & 1 deletion src/popover/popover.ts
Expand Up @@ -57,6 +57,11 @@ export class NgbPopover implements OnInit, OnDestroy {
* Specifies events that should trigger. Supports a space separated list of event names.
*/
@Input() triggers: string;
/**
* A selector specifying the element the popover should be appended to.
* Currently only supports "body".
*/
@Input() container: string;
/**
* Emits an event when the popover is shown
*/
Expand All @@ -77,12 +82,20 @@ export class NgbPopover implements OnInit, OnDestroy {
ngZone: NgZone) {
this.placement = config.placement;
this.triggers = config.triggers;
this.container = config.container;
this._popupService = new PopupService<NgbPopoverWindow>(
NgbPopoverWindow, injector, viewContainerRef, _renderer, componentFactoryResolver);

this._zoneSubscription = ngZone.onStable.subscribe(() => {
if (this._windowRef) {
positionElements(this._elementRef.nativeElement, this._windowRef.location.nativeElement, this.placement);
positionElements(
this._elementRef.nativeElement, this._windowRef.location.nativeElement, this.placement,
this.container === 'body');

if (this.container === 'body') {
let windowEl = this._windowRef.location.nativeElement;
window.document.querySelector(this.container).appendChild(windowEl);
}
}
});
}
Expand Down

0 comments on commit d1a22c0

Please sign in to comment.