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

fix(tooltip): Attributes no longer evaluated in isolate scope #92

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions src/tooltip/test/tooltipSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('tooltip', function() {
}));

it('should not be open initially', inject(function() {
expect( elmScope.isOpen ).toBe( false );
expect( elmScope.tt_isOpen ).toBe( false );

// We can only test *that* the tooltip-popup element wasn't created as the
// implementation is templated and replaced.
Expand All @@ -32,7 +32,7 @@ describe('tooltip', function() {

it('should open on mouseenter', inject(function() {
elm.trigger( 'mouseenter' );
expect( elmScope.isOpen ).toBe( true );
expect( elmScope.tt_isOpen ).toBe( true );

// We can only test *that* the tooltip-popup element was created as the
// implementation is templated and replaced.
Expand All @@ -42,12 +42,12 @@ describe('tooltip', function() {
it('should close on mouseleave', inject(function() {
elm.trigger( 'mouseenter' );
elm.trigger( 'mouseleave' );
expect( elmScope.isOpen ).toBe( false );
expect( elmScope.tt_isOpen ).toBe( false );
}));

it('should have default placement of "top"', inject(function() {
elm.trigger( 'mouseenter' );
expect( elmScope.placement ).toBe( "top" );
expect( elmScope.tt_placement ).toBe( "top" );
}));

it('should allow specification of placement', inject( function( $compile ) {
Expand All @@ -57,15 +57,15 @@ describe('tooltip', function() {
elmScope = elm.scope();

elm.trigger( 'mouseenter' );
expect( elmScope.placement ).toBe( "bottom" );
expect( elmScope.tt_placement ).toBe( "bottom" );
}));

it('should work inside an ngRepeat', inject( function( $compile ) {

elm = $compile( angular.element(
'<ul>'+
'<li ng-repeat="item in items">'+
'<span id="selector" tooltip="{{item.tooltip}}">{{item.name}}</span>'+
'<span tooltip="{{item.tooltip}}">{{item.name}}</span>'+
'</li>'+
'</ul>'
) )( scope );
Expand All @@ -80,15 +80,37 @@ describe('tooltip', function() {

tt.trigger( 'mouseenter' );

// Due to the transclusion, the contents of the element are in a span, so
// we select the tooltip's child and ensure its content matches.
expect( tt.children().text() ).toBe( scope.items[0].name );

// And the tooltip text should still match.
expect( tt.scope().tooltipTitle ).toBe( scope.items[0].tooltip );
expect( tt.text() ).toBe( scope.items[0].name );
expect( tt.scope().tt_tooltip ).toBe( scope.items[0].tooltip );

tt.trigger( 'mouseleave' );
}));

it('should only have an isolate scope on the popup', inject( function ( $compile ) {
var ttScope;

scope.tooltipMsg = "Tooltip Text";
scope.alt = "Alt Message";

elmBody = $compile( angular.element(
'<div><span alt={{alt}} tooltip="{{tooltipMsg}}">Selector Text</span></div>'
) )( scope );

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

elm.trigger( 'mouseenter' );
expect( elm.attr( 'alt' ) ).toBe( scope.alt );

ttScope = angular.element( elmBody.children()[1] ).scope();
expect( ttScope.placement ).toBe( 'top' );
expect( ttScope.tooltipTitle ).toBe( scope.tooltipMsg );

elm.trigger( 'mouseleave' );
}));

});


45 changes: 28 additions & 17 deletions src/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,42 @@ angular.module( 'ui.bootstrap.tooltip', [] )
return {
restrict: 'E',
replace: true,
scope: { tooltipTitle: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-popup.html'
};
})
.directive( 'tooltip', [ '$compile', '$timeout', function ( $compile, $timeout ) {
.directive( 'tooltip', [ '$compile', '$timeout', '$parse', function ( $compile, $timeout, $parse ) {

var template =
'<tooltip-popup></tooltip-popup>';
'<tooltip-popup '+
'tooltip-title="{{tt_tooltip}}" '+
'placement="{{tt_placement}}" '+
'animation="tt_animation()" '+
'is-open="tt_isOpen"'+
'>'+
'</tooltip-popup>';

return {
transclude: true,
scope: { tooltipTitle: '@tooltip', placement: '@tooltipPlacement', animation: '&tooltipAnimation' },
controller: ['$transclude', '$element', function($transclude, $element) {
$transclude(function(clone) {
$element.append(clone);
});
}],
scope: true,
link: function ( scope, element, attr ) {
var tooltip = $compile( template )( scope ),
transitionTimeout;

attr.$observe( 'tooltip', function ( val ) {
scope.tt_tooltip = val;
});

attr.$observe( 'tooltipPlacement', function ( val ) {
// If no placement was provided, default to 'top'.
scope.tt_placement = val || 'top';
});

attr.$observe( 'tooltipAnimation', function ( val ) {
scope.tt_animation = $parse( val );
});

// By default, the tooltip is not open.
scope.isOpen = false;
scope.tt_isOpen = false;

// Calculate the current position and size of the directive element.
function getPosition() {
Expand All @@ -48,9 +62,6 @@ angular.module( 'ui.bootstrap.tooltip', [] )
ttHeight,
ttPosition;

// If no placement was provided, default to 'top'.
scope.placement = scope.placement || 'top';

// If there is a pending remove transition, we must cancel it, lest the
// toolip be mysteriously removed.
if ( transitionTimeout ) {
Expand All @@ -73,7 +84,7 @@ angular.module( 'ui.bootstrap.tooltip', [] )

// Calculate the tooltip's top and left coordinates to center it with
// this directive.
switch ( scope.placement ) {
switch ( scope.tt_placement ) {
case 'right':
ttPosition = {
top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
Expand Down Expand Up @@ -104,19 +115,19 @@ angular.module( 'ui.bootstrap.tooltip', [] )
tooltip.css( ttPosition );

// And show the tooltip.
scope.isOpen = true;
scope.tt_isOpen = true;
}

// Hide the tooltip popup element.
function hide() {
// First things first: we don't show it anymore.
//tooltip.removeClass( 'in' );
scope.isOpen = false;
scope.tt_isOpen = false;

// And now we remove it from the DOM. However, if we have animation, we
// need to wait for it to expire beforehand.
// FIXME: this is a placeholder for a port of the transitions library.
if ( angular.isDefined( scope.animation ) && scope.animation() ) {
if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
transitionTimeout = $timeout( function () { tooltip.remove(); }, 500 );
} else {
tooltip.remove();
Expand Down
2 changes: 1 addition & 1 deletion template/tooltip/tooltip-popup.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="tooltip {{placement}}" ng-class="{ in: isOpen, fade: animation() }">
<div class="tooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner" ng-bind="tooltipTitle"></div>
</div>