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

Commit

Permalink
feat(tooltip): added appendToBody to $tooltip
Browse files Browse the repository at this point in the history
Tooltips and popovers can now be appended to the body of the HTML
document instead of directly after the element on which the directive
was invoked, by setting `appendToBody` to true through
$tooltipProvider.options().

The tooltip specs were refactored slightly to bucket all
provider-related specs under a single `describe` for cleanliness.
  • Loading branch information
Josh David Miller committed Mar 22, 2013
2 parents a79a2ba + e5d593b commit 1ee467f
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 33 deletions.
91 changes: 64 additions & 27 deletions src/tooltip/test/tooltip.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,42 +163,79 @@ describe('tooltip', function() {

});

describe('tooltip with popup delay configured through provider', function(){
describe( '$tooltipProvider', function() {

var elm,
elmBody,
scope,
elmScope;
describe( 'popupDelay', function() {
var elm,
elmBody,
scope,
elmScope;

beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider){
$tooltipProvider.options({popupDelay: 1000});
}));
beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider){
$tooltipProvider.options({popupDelay: 1000});
}));

// load the template
beforeEach(module('template/tooltip/tooltip-popup.html'));
// load the template
beforeEach(module('template/tooltip/tooltip-popup.html'));

beforeEach(inject(function($rootScope, $compile) {
elmBody = angular.element(
'<div><span tooltip="tooltip text">Selector Text</span></div>'
);
beforeEach(inject(function($rootScope, $compile) {
elmBody = angular.element(
'<div><span tooltip="tooltip text">Selector Text</span></div>'
);

scope = $rootScope;
$compile(elmBody)(scope);
scope.$digest();
elm = elmBody.find('span');
elmScope = elm.scope();
}));
scope = $rootScope;
$compile(elmBody)(scope);
scope.$digest();
elm = elmBody.find('span');
elmScope = elm.scope();
}));

it('should open after timeout', inject(function($timeout) {
it('should open after timeout', inject(function($timeout) {

elm.trigger( 'mouseenter' );
expect( elmScope.tt_isOpen ).toBe( false );
elm.trigger( 'mouseenter' );
expect( elmScope.tt_isOpen ).toBe( false );

$timeout.flush();
expect( elmScope.tt_isOpen ).toBe( true );
$timeout.flush();
expect( elmScope.tt_isOpen ).toBe( true );

}));
}));

});

describe('appendToBody', function() {
var elm,
elmBody,
scope,
elmScope,
body;

// load the tooltip code
beforeEach(module('ui.bootstrap.tooltip', function ( $tooltipProvider ) {
$tooltipProvider.options({ appendToBody: true });
}));

// load the template
beforeEach(module('template/tooltip/tooltip-popup.html'));

it( 'should append to the body', inject( function( $rootScope, $compile, $document ) {
$body = $document.find( 'body' );
elmBody = angular.element(
'<div><span tooltip="tooltip text">Selector Text</span></div>'
);

scope = $rootScope;
$compile(elmBody)(scope);
scope.$digest();
elm = elmBody.find('span');
elmScope = elm.scope();

var bodyLength = $body.children().length;
elm.trigger( 'mouseenter' );

expect( elmScope.tt_isOpen ).toBe( true );
expect( elmBody.children().length ).toBe( 1 );
expect( $body.children().length ).toEqual( bodyLength + 1 );
}));
});
});


18 changes: 12 additions & 6 deletions src/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ angular.module( 'ui.bootstrap.tooltip', [] )
var globalOptions = {};

/**
* The `options({})` allows global configuration of all dialogs in the
* `options({})` allows global configuration of all tooltips in the
* application.
*
* var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
Expand All @@ -37,7 +37,7 @@ angular.module( 'ui.bootstrap.tooltip', [] )
* Returns the actual instance of the $tooltip service.
* TODO support multiple triggers
*/
this.$get = [ '$window', '$compile', '$timeout', '$parse', function ( $window, $compile, $timeout, $parse ) {
this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', function ( $window, $compile, $timeout, $parse, $document ) {
return function $tooltip ( type, defaultTriggerShow, defaultTriggerHide ) {
var options = angular.extend( {}, defaultOptions, globalOptions );

Expand Down Expand Up @@ -66,9 +66,10 @@ angular.module( 'ui.bootstrap.tooltip', [] )
restrict: 'EA',
scope: true,
link: function link ( scope, element, attrs ) {
var tooltip = $compile( template )( scope ),
transitionTimeout,
popupTimeout;
var tooltip = $compile( template )( scope );
var transitionTimeout;
var popupTimeout;
var $body;

attrs.$observe( type, function ( val ) {
scope.tt_content = val;
Expand Down Expand Up @@ -127,7 +128,12 @@ angular.module( 'ui.bootstrap.tooltip', [] )

// Now we add it to the DOM because need some info about it. But it's not
// visible yet anyway.
element.after( tooltip );
if ( options.appendToBody ) {
$body = $body || $document.find( 'body' );
$body.append( tooltip );
} else {
element.after( tooltip );
}

// Get the position of the directive element.
position = getPosition( element );
Expand Down

4 comments on commit 1ee467f

@klebba
Copy link

@klebba klebba commented on 1ee467f Mar 27, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior limits the equivalent 'container' option to the body element instead of any element; in Bootstrap we can pass a selector for the option:

container string | false false Appends the tooltip to a specific element container: 'body'

Is there a specific reason why angular-ui/bootstrap is choosing to deviate from the Bootstrap option?

@pkozlowski-opensource
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@klebba the main reason is that we don't require dependency on jQuery and the jqLite baked into AngularJS supports only tag selectors.

@klebba
Copy link

@klebba klebba commented on 1ee467f Mar 27, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I forgot about that. In my case I have a dependency on jQuery already (using the angular-ui select2 directive, which is great) and it was not immediately apparent that angular-ui/bootstrap did not have this dependency. I'm midway through a project and upgraded to 0.2.0 from 0.1.0 to fix a problem with the modal directive and found that tooltip position broke as a result of upgrading. I realize ui/bootstrap is still ironing out kinks but right now its usefulness is diminished to me with this kind of difference from Bootstrap formal which does depend on jQuery. Is there any hope of getting a 1:1 feature parity over time or will these kind of differences have to persist? Thanks for your quick reply!

@joshdmiller
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@klebba There's a difference between feature parity and API parity. In this case, what we really care about is that the tooltip plugin will be able to do everything that the Twitter plugin does; but due to the differences in how AngularJS and jQuery work, the implementation in terms of the API will be different in many cases.

Twitter introduced the container option not as a feature but as their way of solving some positioning issues with the way the plugin adds elements to the DOM. We chose to fix that same placement issue with the more AngularJS-friendly appendToBody option.

That said, there are still some outstanding bugs which we do very much want to fix. The modal issue has been documented in #265. But actually, I've tested it and the case works with the Twitter plugins without using the container option, so this is a bug in our code which we will fix.

Can you confirm that #265 is also your issue?

To directly answer your question: yes, we intend 1:1 feature parity, but how we solution bugs and how we implement the API to get to the same features will, at least in some cases, be very different.

Please sign in to comment.