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

Commit

Permalink
feat($tooltip): support for custom triggers
Browse files Browse the repository at this point in the history
The `$tooltip` service now has two ways to customize the default triggers.
The `$tooltipProvider` takes a `trigger` option and the `*-trigger`
attribute can be applied to a single element.

The `$tooltipProvider`'s `trigger` option overwrites the default value
but the attribute will overwrite both.

A few logical default triggers are supported out of the box and have an
associated map to determine which hide trigger to use. `mouseenter` ->
`mouseleave`, `focus` -> `blur`, and `click` -> `click`. If any other
trigger is provided, it will be used to both show and hide the tooltip.

Custom hide triggers are not yet supported as they would require some
code trickery due to the way `$observe` works.

Closes #131
  • Loading branch information
joshdmiller authored and pkozlowski-opensource committed Apr 27, 2013
1 parent 6458f48 commit b1ba821
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 145 deletions.
7 changes: 7 additions & 0 deletions src/popover/docs/demo.html
Expand Up @@ -14,6 +14,13 @@ <h4>Positional</h4>
<button popover-placement="right" popover="On the Right!" class="btn">Right</button>
<button popover-placement="bottom" popover="On the Bottom!" class="btn">Bottom</button>
</div>
<div>
<h4>Triggers</h4>
<button popover="I appeared on mouse enter!" popover-trigger="mouseenter" class="btn">Mouseenter</button>
<input type="text" value="Click me!"
popover="I appeared on focus! Click away and I'll vanish..."
popover-trigger="focus" />
</div>
<div>
<h4>Other</h4>
<button Popover-animation="true" popover="I fade in and out!" class="btn">fading</button>
Expand Down
2 changes: 2 additions & 0 deletions src/popover/docs/readme.md
Expand Up @@ -13,6 +13,8 @@ will display:
- `popover-animation`: Should it fade in and out? Defaults to "true".
- `popover-popup-delay`: For how long should the user have to have the mouse
over the element before the popover shows (in milliseconds)? Defaults to 0.
- `popover-trigger`: What should trigger the show of the popover? See the
`tooltip` directive for supported values.

The popover directives require the `$position` service.

86 changes: 0 additions & 86 deletions src/popover/test/popoverSpec.js
Expand Up @@ -44,92 +44,6 @@ describe('popover', function() {
elm.trigger( 'click' );
expect( elmScope.tt_isOpen ).toBe( false );
}));

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

it('should allow specification of placement', inject( function( $compile ) {
elm = $compile( angular.element(
'<span popover="popover text" popover-placement="bottom">Selector Text</span>'
) )( scope );
elmScope = elm.scope();

elm.trigger( 'click' );
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 popover="{{item.popover}}">{{item.name}}</span>'+
'</li>'+
'</ul>'
) )( scope );

scope.items = [
{ name: "One", popover: "First popover" }
];

scope.$digest();

var tt = angular.element( elm.find("li > span")[0] );

tt.trigger( 'click' );

expect( tt.text() ).toBe( scope.items[0].name );
expect( tt.scope().tt_content ).toBe( scope.items[0].popover );

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

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

scope.popoverContent = "Popover Content";
scope.popoverTitle = "Popover Title";
scope.alt = "Alt Message";

elmBody = $compile( angular.element(
'<div><span alt={{alt}} popover="{{popoverContent}}" popover-title="{{popoverTitle}}">Selector Text</span></div>'
) )( scope );

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

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

ttScope = angular.element( elmBody.children()[1] ).scope();
expect( ttScope.placement ).toBe( 'top' );
expect( ttScope.title ).toBe( scope.popoverTitle );
expect( ttScope.content ).toBe( scope.popoverContent );

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


it( 'should allow specification of delay', inject( function ($timeout, $compile) {

elm = $compile( angular.element(
'<span popover="popover text" popover-popup-delay="1000">Selector Text</span>'
) )( scope );
elmScope = elm.scope();
scope.$digest();

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

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

} ) );

});


7 changes: 7 additions & 0 deletions src/tooltip/docs/demo.html
Expand Up @@ -20,5 +20,12 @@
<p>
I can even contain HTML. <a><span tooltip-html-unsafe="{{htmlTooltip}}">Check me out!</span></a>
</p>
<p>
Or use custom triggers, like focus:
<input type="text" value="Click me!"
tooltip="See? Now click away..."
tooltip-trigger="focus"
tooltip-placement="right" />
</p>
</div>
</div>
13 changes: 13 additions & 0 deletions src/tooltip/docs/readme.md
Expand Up @@ -15,6 +15,19 @@ will display:
- `tooltip-animation`: Should it fade in and out? Defaults to "true".
- `tooltip-popup-delay`: For how long should the user have to have the mouse
over the element before the tooltip shows (in milliseconds)? Defaults to 0.
- `tooltip-trigger`: What should trigger a show of the tooltip?

The tooltip directives require the `$position` service.

**Triggers**

The following show triggers are supported out of the box, along with their
provided hide triggers:

- `mouseenter`: `mouseleave`
- `click`: `click`
- `focus`: `blur`

For any non-supported value, the trigger will be used to both show and hide the
tooltip.

113 changes: 102 additions & 11 deletions src/tooltip/test/tooltip.spec.js
Expand Up @@ -54,6 +54,7 @@ describe('tooltip', function() {
elm = $compile( angular.element(
'<span tooltip="tooltip text" tooltip-placement="bottom">Selector Text</span>'
) )( scope );
scope.$apply();
elmScope = elm.scope();

elm.trigger( 'mouseenter' );
Expand Down Expand Up @@ -161,6 +162,46 @@ describe('tooltip', function() {

});

describe( 'with a trigger attribute', function() {
var scope, elmBody, elm, elmScope;

beforeEach( inject( function( $rootScope ) {
scope = $rootScope;
}));

it( 'should use it to show but set the hide trigger based on the map for mapped triggers', inject( function( $compile ) {
elmBody = angular.element(
'<div><input tooltip="Hello!" tooltip-trigger="focus" /></div>'
);
$compile(elmBody)(scope);
scope.$apply();
elm = elmBody.find('input');
elmScope = elm.scope();

expect( elmScope.tt_isOpen ).toBeFalsy();
elm.trigger('focus');
expect( elmScope.tt_isOpen ).toBeTruthy();
elm.trigger('blur');
expect( elmScope.tt_isOpen ).toBeFalsy();
}));

it( 'should use it as both the show and hide triggers for unmapped triggers', inject( function( $compile ) {
elmBody = angular.element(
'<div><input tooltip="Hello!" tooltip-trigger="fakeTriggerAttr" /></div>'
);
$compile(elmBody)(scope);
scope.$apply();
elm = elmBody.find('input');
elmScope = elm.scope();

expect( elmScope.tt_isOpen ).toBeFalsy();
elm.trigger('fakeTriggerAttr');
expect( elmScope.tt_isOpen ).toBeTruthy();
elm.trigger('fakeTriggerAttr');
expect( elmScope.tt_isOpen ).toBeFalsy();
}));
});

});

describe( 'tooltipHtmlUnsafe', function() {
Expand Down Expand Up @@ -202,13 +243,13 @@ describe( 'tooltipHtmlUnsafe', function() {
});

describe( '$tooltipProvider', function() {

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

describe( 'popupDelay', function() {
beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider){
$tooltipProvider.options({popupDelay: 1000});
}));
Expand Down Expand Up @@ -241,12 +282,6 @@ describe( '$tooltipProvider', function() {
});

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

// load the tooltip code
beforeEach(module('ui.bootstrap.tooltip', function ( $tooltipProvider ) {
$tooltipProvider.options({ appendToBody: true });
Expand Down Expand Up @@ -275,5 +310,61 @@ describe( '$tooltipProvider', function() {
expect( $body.children().length ).toEqual( bodyLength + 1 );
}));
});

describe( 'triggers', function() {
describe( 'triggers with a mapped value', function() {
beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider){
$tooltipProvider.options({trigger: 'focus'});
}));

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

it( 'should use the show trigger and the mapped value for the hide trigger', inject( function ( $rootScope, $compile ) {
elmBody = angular.element(
'<div><input tooltip="tooltip text" /></div>'
);

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

expect( elmScope.tt_isOpen ).toBeFalsy();
elm.trigger('focus');
expect( elmScope.tt_isOpen ).toBeTruthy();
elm.trigger('blur');
expect( elmScope.tt_isOpen ).toBeFalsy();
}));
});

describe( 'triggers without a mapped value', function() {
beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider){
$tooltipProvider.options({trigger: 'fakeTrigger'});
}));

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

it( 'should use the show trigger to hide', 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();

expect( elmScope.tt_isOpen ).toBeFalsy();
elm.trigger('fakeTrigger');
expect( elmScope.tt_isOpen ).toBeTruthy();
elm.trigger('fakeTrigger');
expect( elmScope.tt_isOpen ).toBeFalsy();
}));
});
});
});

1 comment on commit b1ba821

@L42y
Copy link
Contributor

@L42y L42y commented on b1ba821 Apr 27, 2013

Choose a reason for hiding this comment

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

Awesome :-)

Please sign in to comment.