Skip to content

Commit

Permalink
feat(popover): ability to add custom CSS classes to pover window
Browse files Browse the repository at this point in the history
* provide class applied to `NgbPopoverWindow` via input to
  `NgbPopover` or via `NgbPopoverConfig`
* add example usage to demo app

Closes #1441

Closes #2310
  • Loading branch information
swseverance authored and pkozlowski-opensource committed Jun 29, 2018
1 parent dd00758 commit 483bd05
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<p>
You can optionally pass in a custom class via <code>popoverClass</code>
</p>

<button type="button" class="btn btn-outline-secondary" ngbPopover="Nice class!"
popoverClass="my-custom-class">
Popover with custom class
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {Component, ViewEncapsulation} from '@angular/core';

@Component({
selector: 'ngbd-popover-custom-class',
templateUrl: './popover-custom-class.html',
encapsulation: ViewEncapsulation.None,
styles: [`
.my-custom-class {
background: aliceblue;
font-size: 125%;
}
.my-custom-class .arrow::after {
border-top-color: aliceblue;
}
`]
})
export class NgbdPopoverCustomClass {
}
6 changes: 6 additions & 0 deletions demo/src/app/components/popover/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {NgbdPopoverTplwithcontext} from './tplwithcontext/popover-tplwithcontext
import {NgbdPopoverTriggers} from './triggers/popover-triggers';
import {NgbdPopoverVisibility} from './visibility/popover-visibility';
import {NgbdPopoverContainer} from './container/popover-container';
import {NgbdPopoverCustomClass} from './custom-class/popover-custom-class';
import {NgbdPopoverConfig} from './config/popover-config';

export const DEMO_DIRECTIVES = [
Expand All @@ -13,6 +14,7 @@ export const DEMO_DIRECTIVES = [
NgbdPopoverTriggers,
NgbdPopoverVisibility,
NgbdPopoverContainer,
NgbdPopoverCustomClass,
NgbdPopoverConfig
];

Expand Down Expand Up @@ -41,6 +43,10 @@ export const DEMO_SNIPPETS = {
'code': require('!!raw-loader!./container/popover-container'),
'markup': require('!!raw-loader!./container/popover-container.html')
},
'custom-class': {
'code': require('!!raw-loader!./custom-class/popover-custom-class'),
'markup': require('!!raw-loader!./custom-class/popover-custom-class.html')
},
'config': {
'code': require('!!raw-loader!./config/popover-config'),
'markup': require('!!raw-loader!./config/popover-config.html')
Expand Down
4 changes: 4 additions & 0 deletions demo/src/app/components/popover/popover.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ import {DEMO_SNIPPETS} from './demos';
demoTitle="Append popover in the body" [snippets]="snippets" component="popover" demo="container">
<ngbd-popover-container></ngbd-popover-container>
</ngbd-example-box>
<ngbd-example-box
demoTitle="Popover with custom class" [snippets]="snippets" component="popover" demo="custom-class">
<ngbd-popover-custom-class></ngbd-popover-custom-class>
</ngbd-example-box>
<ngbd-example-box
demoTitle="Global configuration of popovers" [snippets]="snippets" component="popover" demo="config">
<ngbd-popover-config></ngbd-popover-config>
Expand Down
1 change: 1 addition & 0 deletions src/popover/popover-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ describe('ngb-popover-config', () => {
expect(config.triggers).toBe('click');
expect(config.container).toBeUndefined();
expect(config.disablePopover).toBe(false);
expect(config.popoverClass).toBeUndefined();
});
});
1 change: 1 addition & 0 deletions src/popover/popover-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export class NgbPopoverConfig {
triggers = 'click';
container: string;
disablePopover = false;
popoverClass: string;
}
40 changes: 40 additions & 0 deletions src/popover/popover.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ describe('ngb-popover-window', () => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveCssClass('bs-popover-left');
});

it('should optionally have a custom class', () => {
const fixture = TestBed.createComponent(NgbPopoverWindow);
fixture.detectChanges();

expect(fixture.nativeElement).not.toHaveCssClass('my-custom-class');

fixture.componentInstance.popoverClass = 'my-custom-class';
fixture.detectChanges();

expect(fixture.nativeElement).toHaveCssClass('my-custom-class');
});
});

describe('ngb-popover', () => {
Expand Down Expand Up @@ -129,6 +141,30 @@ describe('ngb-popover', () => {
expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
});

it('should open and close a popover - default settings and custom class', () => {
const fixture = createTestComponent(`
<div ngbPopover="Great tip!" popoverTitle="Title" popoverClass="my-custom-class"></div>`);
const directive = fixture.debugElement.query(By.directive(NgbPopover));

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

expect(windowEl).toHaveCssClass('popover');
expect(windowEl).toHaveCssClass('bs-popover-top');
expect(windowEl).toHaveCssClass('my-custom-class');
expect(windowEl.textContent.trim()).toBe('TitleGreat tip!');
expect(windowEl.getAttribute('role')).toBe('tooltip');
expect(windowEl.getAttribute('id')).toBe('ngb-popover-3');
expect(windowEl.parentNode).toBe(fixture.nativeElement);
expect(directive.nativeElement.getAttribute('aria-describedby')).toBe('ngb-popover-3');

directive.triggerEventHandler('click', {});
fixture.detectChanges();
expect(getWindow(fixture.nativeElement)).toBeNull();
expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
});

it('should properly destroy TemplateRef content', () => {
const fixture = createTestComponent(`
<ng-template #t><destroyable-cmpt></destroyable-cmpt></ng-template>
Expand Down Expand Up @@ -549,6 +585,7 @@ describe('ngb-popover', () => {
config.placement = 'bottom';
config.triggers = 'hover';
config.container = 'body';
config.popoverClass = 'my-custom-class';
}));

it('should initialize inputs with provided config', () => {
Expand All @@ -560,13 +597,15 @@ describe('ngb-popover', () => {
expect(popover.placement).toBe(config.placement);
expect(popover.triggers).toBe(config.triggers);
expect(popover.container).toBe(config.container);
expect(popover.popoverClass).toBe(config.popoverClass);
});
});

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

beforeEach(() => {
TestBed.configureTestingModule(
Expand All @@ -579,6 +618,7 @@ describe('ngb-popover', () => {

expect(popover.placement).toBe(config.placement);
expect(popover.triggers).toBe(config.triggers);
expect(popover.popoverClass).toBe(config.popoverClass);
});
});
});
Expand Down
12 changes: 11 additions & 1 deletion src/popover/popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ let nextId = 0;
selector: 'ngb-popover-window',
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class]': '"popover bs-popover-" + placement.split("-")[0]+" bs-popover-" + placement',
'[class]':
'"popover bs-popover-" + placement.split("-")[0]+" bs-popover-" + placement + (popoverClass ? " " + popoverClass : "")',
'role': 'tooltip',
'[id]': 'id'
},
Expand Down Expand Up @@ -71,6 +72,7 @@ export class NgbPopoverWindow {
@Input() placement: Placement = 'top';
@Input() title: string;
@Input() id: string;
@Input() popoverClass: string;

constructor(private _element: ElementRef<HTMLElement>, private _renderer: Renderer2) {}

Expand Down Expand Up @@ -123,6 +125,12 @@ export class NgbPopover implements OnInit, OnDestroy, OnChanges {
* @since 1.1.0
*/
@Input() disablePopover: boolean;
/**
* An optional class applied to ngb-popover-window
*
* @since 2.2.0
*/
@Input() popoverClass: string;
/**
* Emits an event when the popover is shown
*/
Expand Down Expand Up @@ -155,6 +163,7 @@ export class NgbPopover implements OnInit, OnDestroy, OnChanges {
this.triggers = config.triggers;
this.container = config.container;
this.disablePopover = config.disablePopover;
this.popoverClass = config.popoverClass;
this._popupService = new PopupService<NgbPopoverWindow>(
NgbPopoverWindow, injector, viewContainerRef, _renderer, componentFactoryResolver);

Expand All @@ -176,6 +185,7 @@ export class NgbPopover implements OnInit, OnDestroy, OnChanges {
if (!this._windowRef && !this._isDisabled()) {
this._windowRef = this._popupService.open(this.ngbPopover, context);
this._windowRef.instance.title = this.popoverTitle;
this._windowRef.instance.popoverClass = this.popoverClass;
this._windowRef.instance.id = this._ngbPopoverWindowId;

this._renderer.setAttribute(this._elementRef.nativeElement, 'aria-describedby', this._ngbPopoverWindowId);
Expand Down

0 comments on commit 483bd05

Please sign in to comment.