Skip to content

Commit

Permalink
feat(tooltip): ability to add custom CSS classes to tooltip window
Browse files Browse the repository at this point in the history
Closes #2625
Closes #1349
  • Loading branch information
divdavem authored and Benoit Charbonnier committed Aug 28, 2018
1 parent 07e4e84 commit 709d6b6
Show file tree
Hide file tree
Showing 7 changed files with 86 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>tooltipClass</code>
</p>

<button type="button" class="btn btn-outline-secondary" ngbTooltip="Nice class!"
tooltipClass="my-custom-class">
Tooltip 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-tooltip-customclass',
templateUrl: './tooltip-customclass.html',
encapsulation: ViewEncapsulation.None,
styles: [`
.my-custom-class .tooltip-inner {
background-color: darkgreen;
font-size: 125%;
}
.my-custom-class .arrow::before {
border-top-color: darkgreen;
}
`]
})
export class NgbdTooltipCustomclass {
}
8 changes: 8 additions & 0 deletions demo/src/app/components/tooltip/tooltip.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import { NgbdTooltipAutoclose } from './demos/autoclose/tooltip-autoclose';
import { NgbdTooltipBasic } from './demos/basic/tooltip-basic';
import { NgbdTooltipConfig } from './demos/config/tooltip-config';
import { NgbdTooltipContainer } from './demos/container/tooltip-container';
import { NgbdTooltipCustomclass } from './demos/customclass/tooltip-customclass';
import { NgbdTooltipTplcontent } from './demos/tplcontent/tooltip-tplcontent';
import { NgbdTooltipTplwithcontext } from './demos/tplwithcontext/tooltip-tplwithcontext';
import { NgbdTooltipTriggers } from './demos/triggers/tooltip-triggers';

const DEMO_DIRECTIVES = [
NgbdTooltipBasic,
NgbdTooltipContainer,
NgbdTooltipCustomclass,
NgbdTooltipTplcontent,
NgbdTooltipTriggers,
NgbdTooltipAutoclose,
Expand Down Expand Up @@ -60,6 +62,12 @@ const DEMOS = {
code: require('!!raw-loader!./demos/container/tooltip-container'),
markup: require('!!raw-loader!./demos/container/tooltip-container.html')
},
'customclass': {
title: 'Tooltip with custom class',
type: NgbdTooltipCustomclass,
code: require('!!raw-loader!./demos/customclass/tooltip-customclass'),
markup: require('!!raw-loader!./demos/customclass/tooltip-customclass.html')
},
config: {
title: 'Global configuration of tooltips',
type: NgbdTooltipConfig,
Expand Down
1 change: 1 addition & 0 deletions src/tooltip/tooltip-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ describe('ngb-tooltip-config', () => {
expect(config.triggers).toBe('hover');
expect(config.container).toBeUndefined();
expect(config.disableTooltip).toBe(false);
expect(config.tooltipClass).toBeUndefined();
});
});
1 change: 1 addition & 0 deletions src/tooltip/tooltip-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export class NgbTooltipConfig {
triggers = 'hover';
container: string;
disableTooltip = false;
tooltipClass: string;
}
41 changes: 41 additions & 0 deletions src/tooltip/tooltip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ describe('ngb-tooltip-window', () => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveCssClass('bs-tooltip-left');
});

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

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

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

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

});

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

it('should open and close a tooltip - default settings and custom class', () => {
const fixture = createTestComponent(`
<div ngbTooltip="Great tip!" tooltipClass="my-custom-class"></div>`);
const directive = fixture.debugElement.query(By.directive(NgbTooltip));

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

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

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

it('should not open a tooltip if content is falsy', () => {
const fixture = createTestComponent(`<div [ngbTooltip]="notExisting"></div>`);
const directive = fixture.debugElement.query(By.directive(NgbTooltip));
Expand Down Expand Up @@ -700,6 +737,7 @@ describe('ngb-tooltip', () => {
config.placement = 'bottom';
config.triggers = 'click';
config.container = 'body';
config.tooltipClass = 'my-custom-class';
}));

it('should initialize inputs with provided config', () => {
Expand All @@ -710,6 +748,7 @@ describe('ngb-tooltip', () => {
expect(tooltip.placement).toBe(config.placement);
expect(tooltip.triggers).toBe(config.triggers);
expect(tooltip.container).toBe(config.container);
expect(tooltip.tooltipClass).toBe(config.tooltipClass);
});
});

Expand All @@ -718,6 +757,7 @@ describe('ngb-tooltip', () => {
config.placement = 'bottom';
config.triggers = 'click';
config.container = 'body';
config.tooltipClass = 'my-custom-class';

beforeEach(() => {
TestBed.configureTestingModule(
Expand All @@ -731,6 +771,7 @@ describe('ngb-tooltip', () => {
expect(tooltip.placement).toBe(config.placement);
expect(tooltip.triggers).toBe(config.triggers);
expect(tooltip.container).toBe(config.container);
expect(tooltip.tooltipClass).toBe(config.tooltipClass);
});
});

Expand Down
10 changes: 9 additions & 1 deletion src/tooltip/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ let nextId = 0;
selector: 'ngb-tooltip-window',
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class]': '"tooltip show bs-tooltip-" + placement.split("-")[0]+" bs-tooltip-" + placement',
'[class]':
'"tooltip show bs-tooltip-" + placement.split("-")[0]+" bs-tooltip-" + placement + (tooltipClass ? " " + tooltipClass : "")',
'role': 'tooltip',
'[id]': 'id'
},
Expand Down Expand Up @@ -71,6 +72,7 @@ let nextId = 0;
export class NgbTooltipWindow {
@Input() placement: Placement = 'top';
@Input() id: string;
@Input() tooltipClass: string;

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

Expand Down Expand Up @@ -135,6 +137,10 @@ export class NgbTooltip implements OnInit, OnDestroy {
* @since 1.1.0
*/
@Input() disableTooltip: boolean;
/**
* An optional class applied to ngb-tooltip-window
*/
@Input() tooltipClass: string;
/**
* Emits an event when the tooltip is shown
*/
Expand All @@ -160,6 +166,7 @@ export class NgbTooltip implements OnInit, OnDestroy {
this.triggers = config.triggers;
this.container = config.container;
this.disableTooltip = config.disableTooltip;
this.tooltipClass = config.tooltipClass;
this._popupService = new PopupService<NgbTooltipWindow>(
NgbTooltipWindow, injector, viewContainerRef, _renderer, componentFactoryResolver);

Expand Down Expand Up @@ -193,6 +200,7 @@ export class NgbTooltip implements OnInit, OnDestroy {
open(context?: any) {
if (!this._windowRef && this._ngbTooltip && !this.disableTooltip) {
this._windowRef = this._popupService.open(this._ngbTooltip, context);
this._windowRef.instance.tooltipClass = this.tooltipClass;
this._windowRef.instance.id = this._ngbTooltipWindowId;

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

0 comments on commit 709d6b6

Please sign in to comment.