Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 45ed280

Browse files
author
Josh David Miller
committed
feat(tooltip): added tooltip-html-unsafe directive
The directive displays the unsanitized HTML in the tooltip instead of the escaped text. The $tooltip service has been modified to allow a little more flexibility in terms of the prefix used on the $observe'd attributes. For example, the `tooltip-html-unsafe` directive needs to be called as written, but it would be nonsensical to require all other attributes (like animation or placement) to also use that verbose prefix as opposed to the simpler and more familiar `tooltip-` prefix. The service now allows independent specification of the name and its prefix. Lastly, the docs for the tooltip and popover have been updated to show their available optional attributes. Closes #246
1 parent 78da771 commit 45ed280

File tree

8 files changed

+111
-10
lines changed

8 files changed

+111
-10
lines changed

src/popover/docs/readme.md

+11
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,14 @@ directive supports multiple placements, optional transition animation, and more.
33

44
Like the Twitter Bootstrap jQuery plugin, the popover **requires** the tooltip
55
module.
6+
7+
The popover directives provides several optional attributes to control how it
8+
will display:
9+
10+
- `popover-title`: A string to display as a fancy title.
11+
- `popover-placement`: Where to place it? Defaults to "top", but also accepts
12+
"bottom", "left", or "right".
13+
- `popover-animation`: Should it fade in and out? Defaults to "true".
14+
- `popover-popup-delay`: For how long should the user have to have the mouse
15+
over the element before the popover shows (in milliseconds)? Defaults to 0.
16+

src/popover/popover.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
1313
};
1414
})
1515
.directive( 'popover', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) {
16-
return $tooltip( 'popover', 'click' );
16+
return $tooltip( 'popover', 'popover', 'click' );
1717
}]);
1818

src/tooltip/docs/demo.html

+4
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@
1616
at elementum eu, facilisis sed odio morbi quis commodo odio. In cursus
1717
<a><span tooltip-popup-delay='1000' tooltip='appears with delay'>delayed</span></a> turpis massa tincidunt dui ut.
1818
</p>
19+
20+
<p>
21+
I can even contain HTML. <a><span tooltip-html-unsafe="{{htmlTooltip}}">Check me out!</span></a>
22+
</p>
1923
</div>
2024
</div>

src/tooltip/docs/demo.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var TooltipDemoCtrl = function ($scope) {
22
$scope.dynamicTooltip = "Hello, World!";
33
$scope.dynamicTooltipText = "dynamic";
4+
$scope.htmlTooltip = "I've been made <b>bold</b>!";
45
};

src/tooltip/docs/readme.md

+16
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,18 @@
11
A lightweight, extensible directive for fancy tooltip creation. The tooltip
22
directive supports multiple placements, optional transition animation, and more.
3+
4+
There are two versions of the tooltip: `tooltip` and `tooltip-html-unsafe`. The
5+
former takes text only and will escape any HTML provided. The latter takes
6+
whatever HTML is provided and displays it in a tooltip; it called "unsafe"
7+
because the HTML is not sanitized. *The user is responsible for ensuring the
8+
content is safe to put into the DOM!*
9+
10+
The tooltip directives provide several optional attributes to control how they
11+
will display:
12+
13+
- `tooltip-placement`: Where to place it? Defaults to "top", but also accepts
14+
"bottom", "left", or "right".
15+
- `tooltip-animation`: Should it fade in and out? Defaults to "true".
16+
- `tooltip-popup-delay`: For how long should the user have to have the mouse
17+
over the element before the tooltip shows (in milliseconds)? Defaults to 0.
18+

src/tooltip/test/tooltip.spec.js

+38
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,44 @@ describe('tooltip', function() {
163163

164164
});
165165

166+
describe( 'tooltipHtmlUnsafe', function() {
167+
var elm, elmBody, scope;
168+
169+
// load the tooltip code
170+
beforeEach(module('ui.bootstrap.tooltip', function ( $tooltipProvider ) {
171+
$tooltipProvider.options({ animation: false });
172+
}));
173+
174+
// load the template
175+
beforeEach(module('template/tooltip/tooltip-html-unsafe-popup.html'));
176+
177+
beforeEach(inject(function($rootScope, $compile) {
178+
scope = $rootScope;
179+
scope.html = 'I say: <strong class="hello">Hello!</strong>';
180+
181+
elmBody = $compile( angular.element(
182+
'<div><span tooltip-html-unsafe="{{html}}">Selector Text</span></div>'
183+
))( scope );
184+
scope.$digest();
185+
elm = elmBody.find('span');
186+
elmScope = elm.scope();
187+
}));
188+
189+
it( 'should show on mouseenter and hide on mouseleave', inject( function () {
190+
expect( elmScope.tt_isOpen ).toBe( false );
191+
192+
elm.trigger( 'mouseenter' );
193+
expect( elmScope.tt_isOpen ).toBe( true );
194+
expect( elmBody.children().length ).toBe( 2 );
195+
196+
expect( elmScope.tt_content ).toEqual( scope.html );
197+
198+
elm.trigger( 'mouseleave' );
199+
expect( elmScope.tt_isOpen ).toBe( false );
200+
expect( elmBody.children().length ).toBe( 1 );
201+
}));
202+
});
203+
166204
describe( '$tooltipProvider', function() {
167205

168206
describe( 'popupDelay', function() {

src/tooltip/tooltip.js

+36-9
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,35 @@ angular.module( 'ui.bootstrap.tooltip', [] )
3333
angular.extend( globalOptions, value );
3434
};
3535

36+
/**
37+
* This is a helper function for translating camel-case to snake-case.
38+
*/
39+
function snake_case(name){
40+
var regexp = /[A-Z]/g;
41+
var separator = '-';
42+
return name.replace(regexp, function(letter, pos) {
43+
return (pos ? separator : '') + letter.toLowerCase();
44+
});
45+
}
46+
3647
/**
3748
* Returns the actual instance of the $tooltip service.
3849
* TODO support multiple triggers
3950
*/
4051
this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', function ( $window, $compile, $timeout, $parse, $document ) {
41-
return function $tooltip ( type, defaultTriggerShow, defaultTriggerHide ) {
52+
return function $tooltip ( type, prefix, defaultTriggerShow, defaultTriggerHide ) {
4253
var options = angular.extend( {}, defaultOptions, globalOptions );
54+
var directiveName = snake_case( type );
4355

4456
var template =
45-
'<'+ type +'-popup '+
57+
'<'+ directiveName +'-popup '+
4658
'title="{{tt_title}}" '+
4759
'content="{{tt_content}}" '+
4860
'placement="{{tt_placement}}" '+
4961
'animation="tt_animation()" '+
5062
'is-open="tt_isOpen"'+
5163
'>'+
52-
'</'+ type +'-popup>';
64+
'</'+ directiveName +'-popup>';
5365

5466
// Calculate the current position and size of the directive element.
5567
function getPosition( element ) {
@@ -75,19 +87,19 @@ angular.module( 'ui.bootstrap.tooltip', [] )
7587
scope.tt_content = val;
7688
});
7789

78-
attrs.$observe( type+'Title', function ( val ) {
90+
attrs.$observe( prefix+'Title', function ( val ) {
7991
scope.tt_title = val;
8092
});
8193

82-
attrs.$observe( type+'Placement', function ( val ) {
94+
attrs.$observe( prefix+'Placement', function ( val ) {
8395
scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
8496
});
8597

86-
attrs.$observe( type+'Animation', function ( val ) {
98+
attrs.$observe( prefix+'Animation', function ( val ) {
8799
scope.tt_animation = angular.isDefined( val ) ? $parse( val ) : function(){ return options.animation; };
88100
});
89101

90-
attrs.$observe( type+'PopupDelay', function ( val ) {
102+
attrs.$observe( prefix+'PopupDelay', function ( val ) {
91103
var delay = parseInt( val, 10 );
92104
scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
93105
});
@@ -232,6 +244,21 @@ angular.module( 'ui.bootstrap.tooltip', [] )
232244
})
233245

234246
.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
235-
return $tooltip( 'tooltip', 'mouseenter', 'mouseleave' );
236-
}]);
247+
return $tooltip( 'tooltip', 'tooltip', 'mouseenter', 'mouseleave' );
248+
}])
249+
250+
.directive( 'tooltipHtmlUnsafePopup', function () {
251+
return {
252+
restrict: 'E',
253+
replace: true,
254+
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
255+
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
256+
};
257+
})
258+
259+
.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
260+
return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter', 'mouseleave' );
261+
}])
262+
263+
;
237264

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div class="tooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">
2+
<div class="tooltip-arrow"></div>
3+
<div class="tooltip-inner" ng-bind-html-unsafe="content"></div>
4+
</div>

0 commit comments

Comments
 (0)