Skip to content

Commit

Permalink
feat(popover): add config service to provide default values
Browse files Browse the repository at this point in the history
refs #518

Closes #686
  • Loading branch information
jnizet authored and pkozlowski-opensource committed Sep 7, 2016
1 parent 5a0226d commit a58588c
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<button type="button" class="btn btn-secondary"
ngbPopover="This popover gets its inputs from the customized configuration" title="Customized popover">
Customized popover
</button>
15 changes: 15 additions & 0 deletions demo/src/app/components/popover/demos/config/popover-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Component} from '@angular/core';
import {NgbPopoverConfig} from '@ng-bootstrap/ng-bootstrap';

@Component({
selector: 'ngbd-popover-config',
templateUrl: './popover-config.html',
providers: [NgbPopoverConfig] // add NgbPopoverConfig to the component providers
})
export class NgbdPopoverConfig {
constructor(config: NgbPopoverConfig) {
// customize default values of popovers used by this component tree
config.placement = 'right';
config.triggers = 'hover';
}
}
16 changes: 12 additions & 4 deletions demo/src/app/components/popover/demos/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import {NgbdPopoverBasic} from './basic/popover-basic';
import {NgbdPopoverTplcontent} from './tplcontent/popover-tplcontent';
import {NgbdPopoverTriggers} from './triggers/popover-triggers';
import {NgbdPopoverConfig} from './config/popover-config';

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

export const DEMO_SNIPPETS = {
basic: {
code: require('!!prismjs?lang=typescript!./basic/popover-basic'),
markup: require('!!prismjs?lang=markup!./basic/popover-basic.html')},
markup: require('!!prismjs?lang=markup!./basic/popover-basic.html')
},
tplcontent: {
code: require('!!prismjs?lang=typescript!./tplcontent/popover-tplcontent'),
markup: require('!!prismjs?lang=markup!./tplcontent/popover-tplcontent.html')},
markup: require('!!prismjs?lang=markup!./tplcontent/popover-tplcontent.html')
},
triggers: {
code: require('!!prismjs?lang=typescript!./triggers/popover-triggers'),
markup: require('!!prismjs?lang=markup!./triggers/popover-triggers.html')}
markup: require('!!prismjs?lang=markup!./triggers/popover-triggers.html')
},
config: {
code: require('!!prismjs?lang=typescript!./config/popover-config'),
markup: require('!!prismjs?lang=markup!./config/popover-config.html')
}
};
5 changes: 5 additions & 0 deletions demo/src/app/components/popover/popover.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {DEMO_SNIPPETS} from './demos';
template: `
<ngbd-content-wrapper component="Popover">
<ngbd-api-docs directive="NgbPopover"></ngbd-api-docs>
<ngbd-api-docs-config type="NgbPopoverConfig"></ngbd-api-docs-config>
<ngbd-example-box demoTitle="Quick and easy popovers" [htmlSnippet]="snippets.basic.markup" [tsSnippet]="snippets.basic.code">
<ngbd-popover-basic></ngbd-popover-basic>
</ngbd-example-box>
Expand All @@ -17,6 +18,10 @@ import {DEMO_SNIPPETS} from './demos';
demoTitle="Custom and manual triggers" [htmlSnippet]="snippets.triggers.markup" [tsSnippet]="snippets.triggers.code">
<ngbd-popover-triggers></ngbd-popover-triggers>
</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>
</ngbd-example-box>
</ngbd-content-wrapper>
`
})
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export {NgbAlertConfig, NgbSelfClosingAlertConfig} from './alert/alert.module';
export {NgbCarouselConfig} from './carousel/carousel.module';
export {NgbDatepickerConfig} from './datepicker/datepicker.module';
export {NgbPaginationConfig} from './pagination/pagination.module';
export {NgbPopoverConfig} from './popover/popover.module';
export {NgbProgressbarConfig} from './progressbar/progressbar.module';
export {NgbRatingConfig} from './rating/rating.module';
export {NgbTimepickerConfig} from './timepicker/timepicker.module';
Expand Down
10 changes: 10 additions & 0 deletions src/popover/popover-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {NgbPopoverConfig} from './popover-config';

describe('ngb-popover-config', () => {
it('should have sensible default values', () => {
const config = new NgbPopoverConfig();

expect(config.placement).toBe('top');
expect(config.triggers).toBe('click');
});
});
12 changes: 12 additions & 0 deletions src/popover/popover-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {Injectable} from '@angular/core';

/**
* Configuration service for the NgbPopover directive.
* You can inject this service, typically in your root component, and customize the values of its properties in
* order to provide default values for all the popovers used in the application.
*/
@Injectable()
export class NgbPopoverConfig {
placement: 'top' | 'bottom' | 'left' | 'right' = 'top';
triggers = 'click';
}
10 changes: 9 additions & 1 deletion src/popover/popover.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import {NgModule} from '@angular/core';

import {NGB_POPOVER_DIRECTIVES, NgbPopoverWindow} from './popover';
import {NgbPopoverConfig} from './popover-config';

@NgModule({declarations: NGB_POPOVER_DIRECTIVES, exports: NGB_POPOVER_DIRECTIVES, entryComponents: [NgbPopoverWindow]})
export {NgbPopoverConfig} from './popover-config';

@NgModule({
declarations: NGB_POPOVER_DIRECTIVES,
exports: NGB_POPOVER_DIRECTIVES,
entryComponents: [NgbPopoverWindow],
providers: [NgbPopoverConfig]
})
export class NgbPopoverModule {
}
55 changes: 53 additions & 2 deletions src/popover/popover.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {TestBed, ComponentFixture} from '@angular/core/testing';
import {TestBed, ComponentFixture, inject} from '@angular/core/testing';
import {createGenericTestComponent} from '../test/common';

import {By} from '@angular/platform-browser';
import {Component} from '@angular/core';

import {NgbPopoverModule} from './popover.module';
import {NgbPopoverWindow, NgbPopover} from './popover';
import {NgbPopoverConfig} from './popover-config';

const createTestComponent = (html: string) =>
createGenericTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
Expand Down Expand Up @@ -63,13 +64,14 @@ describe('ngb-popover', () => {
<template #t>Hello, {{name}}!</template>
<div [ngbPopover]="t" title="Title"></div>`);
const directive = fixture.debugElement.query(By.directive(NgbPopover));
const defaultConfig = new NgbPopoverConfig();

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

expect(windowEl).toHaveCssClass('popover');
expect(windowEl).toHaveCssClass('popover-top');
expect(windowEl).toHaveCssClass(`popover-${defaultConfig.placement}`);
expect(windowEl.textContent.trim()).toBe('TitleHello, World!');
expect(windowEl.getAttribute('role')).toBe('tooltip');

Expand Down Expand Up @@ -249,6 +251,55 @@ describe('ngb-popover', () => {
expect(getWindow(fixture)).toBeNull();
});
});

describe('Custom config', () => {
let config: NgbPopoverConfig;

beforeEach(() => {
TestBed.configureTestingModule({imports: [NgbPopoverModule]});
TestBed.overrideComponent(TestComponent, {set: {template: `<div ngbPopover="Great tip!"></div>`}});
});

beforeEach(inject([NgbPopoverConfig], (c: NgbPopoverConfig) => {
config = c;
config.placement = 'bottom';
config.triggers = 'hover';
}));

it('should initialize inputs with provided config', () => {
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const directive = fixture.debugElement.query(By.directive(NgbPopover));

directive.triggerEventHandler('mouseenter', {});
fixture.detectChanges();
const windowEl = getWindow(fixture);

expect(windowEl).toHaveCssClass('popover-bottom');
});
});

describe('Custom config as provider', () => {
let config = new NgbPopoverConfig();
config.placement = 'bottom';
config.triggers = 'hover';

beforeEach(() => {
TestBed.configureTestingModule(
{imports: [NgbPopoverModule], providers: [{provide: NgbPopoverConfig, useValue: config}]});
});

it('should initialize inputs with provided config as provider', () => {
const fixture = createTestComponent(`<div ngbPopover="Great tip!"></div>`);
const directive = fixture.debugElement.query(By.directive(NgbPopover));

directive.triggerEventHandler('mouseenter', {});
fixture.detectChanges();
const windowEl = getWindow(fixture);

expect(windowEl).toHaveCssClass('popover-bottom');
});
});
});

@Component({selector: 'test-cmpt', template: ``})
Expand Down
12 changes: 8 additions & 4 deletions src/popover/popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import {listenToTriggers} from '../util/triggers';
import {Positioning} from '../util/positioning';
import {PopupService} from '../util/popup';
import {NgbPopoverConfig} from './popover-config';

@Component({
selector: 'ngb-popover-window',
Expand All @@ -29,7 +30,7 @@ import {PopupService} from '../util/popup';
`
})
export class NgbPopoverWindow {
@Input() placement = 'top';
@Input() placement: 'top' | 'bottom' | 'left' | 'right' = 'top';
@Input() title: string;
}

Expand All @@ -49,11 +50,11 @@ export class NgbPopover implements OnInit, AfterViewChecked, OnDestroy {
/**
* Placement of a popover. Accepts: "top", "bottom", "left", "right"
*/
@Input() placement = 'top';
@Input() placement: 'top' | 'bottom' | 'left' | 'right';
/**
* Specifies events that should trigger. Supports a space separated list of event names.
*/
@Input() triggers = 'click';
@Input() triggers: string;

private _popupService: PopupService<NgbPopoverWindow>;
private _positioning = new Positioning();
Expand All @@ -62,7 +63,10 @@ export class NgbPopover implements OnInit, AfterViewChecked, OnDestroy {

constructor(
private _elementRef: ElementRef, private _renderer: Renderer, injector: Injector,
componentFactoryResolver: ComponentFactoryResolver, viewContainerRef: ViewContainerRef) {
componentFactoryResolver: ComponentFactoryResolver, viewContainerRef: ViewContainerRef,
config: NgbPopoverConfig) {
this.placement = config.placement;
this.triggers = config.triggers;
this._popupService = new PopupService<NgbPopoverWindow>(
NgbPopoverWindow, injector, viewContainerRef, _renderer, componentFactoryResolver);
}
Expand Down

0 comments on commit a58588c

Please sign in to comment.