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

Commit b1ba821

Browse files
Josh David Millerpkozlowski-opensource
Josh David Miller
authored andcommitted
feat($tooltip): support for custom triggers
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
1 parent 6458f48 commit b1ba821

File tree

7 files changed

+228
-145
lines changed

7 files changed

+228
-145
lines changed

src/popover/docs/demo.html

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ <h4>Positional</h4>
1414
<button popover-placement="right" popover="On the Right!" class="btn">Right</button>
1515
<button popover-placement="bottom" popover="On the Bottom!" class="btn">Bottom</button>
1616
</div>
17+
<div>
18+
<h4>Triggers</h4>
19+
<button popover="I appeared on mouse enter!" popover-trigger="mouseenter" class="btn">Mouseenter</button>
20+
<input type="text" value="Click me!"
21+
popover="I appeared on focus! Click away and I'll vanish..."
22+
popover-trigger="focus" />
23+
</div>
1724
<div>
1825
<h4>Other</h4>
1926
<button Popover-animation="true" popover="I fade in and out!" class="btn">fading</button>

src/popover/docs/readme.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ will display:
1313
- `popover-animation`: Should it fade in and out? Defaults to "true".
1414
- `popover-popup-delay`: For how long should the user have to have the mouse
1515
over the element before the popover shows (in milliseconds)? Defaults to 0.
16+
- `popover-trigger`: What should trigger the show of the popover? See the
17+
`tooltip` directive for supported values.
1618

1719
The popover directives require the `$position` service.
1820

src/popover/test/popoverSpec.js

-86
Original file line numberDiff line numberDiff line change
@@ -44,92 +44,6 @@ describe('popover', function() {
4444
elm.trigger( 'click' );
4545
expect( elmScope.tt_isOpen ).toBe( false );
4646
}));
47-
48-
it('should have default placement of "top"', inject(function() {
49-
elm.trigger( 'click' );
50-
expect( elmScope.tt_placement ).toBe( "top" );
51-
}));
52-
53-
it('should allow specification of placement', inject( function( $compile ) {
54-
elm = $compile( angular.element(
55-
'<span popover="popover text" popover-placement="bottom">Selector Text</span>'
56-
) )( scope );
57-
elmScope = elm.scope();
58-
59-
elm.trigger( 'click' );
60-
expect( elmScope.tt_placement ).toBe( "bottom" );
61-
}));
62-
63-
it('should work inside an ngRepeat', inject( function( $compile ) {
64-
65-
elm = $compile( angular.element(
66-
'<ul>'+
67-
'<li ng-repeat="item in items">'+
68-
'<span popover="{{item.popover}}">{{item.name}}</span>'+
69-
'</li>'+
70-
'</ul>'
71-
) )( scope );
72-
73-
scope.items = [
74-
{ name: "One", popover: "First popover" }
75-
];
76-
77-
scope.$digest();
78-
79-
var tt = angular.element( elm.find("li > span")[0] );
80-
81-
tt.trigger( 'click' );
82-
83-
expect( tt.text() ).toBe( scope.items[0].name );
84-
expect( tt.scope().tt_content ).toBe( scope.items[0].popover );
85-
86-
tt.trigger( 'click' );
87-
}));
88-
89-
it('should only have an isolate scope on the popup', inject( function ( $compile ) {
90-
var ttScope;
91-
92-
scope.popoverContent = "Popover Content";
93-
scope.popoverTitle = "Popover Title";
94-
scope.alt = "Alt Message";
95-
96-
elmBody = $compile( angular.element(
97-
'<div><span alt={{alt}} popover="{{popoverContent}}" popover-title="{{popoverTitle}}">Selector Text</span></div>'
98-
) )( scope );
99-
100-
$compile( elmBody )( scope );
101-
scope.$digest();
102-
elm = elmBody.find( 'span' );
103-
elmScope = elm.scope();
104-
105-
elm.trigger( 'click' );
106-
expect( elm.attr( 'alt' ) ).toBe( scope.alt );
107-
108-
ttScope = angular.element( elmBody.children()[1] ).scope();
109-
expect( ttScope.placement ).toBe( 'top' );
110-
expect( ttScope.title ).toBe( scope.popoverTitle );
111-
expect( ttScope.content ).toBe( scope.popoverContent );
112-
113-
elm.trigger( 'click' );
114-
}));
115-
116-
117-
it( 'should allow specification of delay', inject( function ($timeout, $compile) {
118-
119-
elm = $compile( angular.element(
120-
'<span popover="popover text" popover-popup-delay="1000">Selector Text</span>'
121-
) )( scope );
122-
elmScope = elm.scope();
123-
scope.$digest();
124-
125-
elm.trigger( 'click' );
126-
expect( elmScope.tt_isOpen ).toBe( false );
127-
128-
$timeout.flush();
129-
expect( elmScope.tt_isOpen ).toBe( true );
130-
131-
} ) );
132-
13347
});
13448

13549

src/tooltip/docs/demo.html

+7
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,12 @@
2020
<p>
2121
I can even contain HTML. <a><span tooltip-html-unsafe="{{htmlTooltip}}">Check me out!</span></a>
2222
</p>
23+
<p>
24+
Or use custom triggers, like focus:
25+
<input type="text" value="Click me!"
26+
tooltip="See? Now click away..."
27+
tooltip-trigger="focus"
28+
tooltip-placement="right" />
29+
</p>
2330
</div>
2431
</div>

src/tooltip/docs/readme.md

+13
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ will display:
1515
- `tooltip-animation`: Should it fade in and out? Defaults to "true".
1616
- `tooltip-popup-delay`: For how long should the user have to have the mouse
1717
over the element before the tooltip shows (in milliseconds)? Defaults to 0.
18+
- `tooltip-trigger`: What should trigger a show of the tooltip?
1819

1920
The tooltip directives require the `$position` service.
2021

22+
**Triggers**
23+
24+
The following show triggers are supported out of the box, along with their
25+
provided hide triggers:
26+
27+
- `mouseenter`: `mouseleave`
28+
- `click`: `click`
29+
- `focus`: `blur`
30+
31+
For any non-supported value, the trigger will be used to both show and hide the
32+
tooltip.
33+

src/tooltip/test/tooltip.spec.js

+102-11
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ describe('tooltip', function() {
5454
elm = $compile( angular.element(
5555
'<span tooltip="tooltip text" tooltip-placement="bottom">Selector Text</span>'
5656
) )( scope );
57+
scope.$apply();
5758
elmScope = elm.scope();
5859

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

162163
});
163164

165+
describe( 'with a trigger attribute', function() {
166+
var scope, elmBody, elm, elmScope;
167+
168+
beforeEach( inject( function( $rootScope ) {
169+
scope = $rootScope;
170+
}));
171+
172+
it( 'should use it to show but set the hide trigger based on the map for mapped triggers', inject( function( $compile ) {
173+
elmBody = angular.element(
174+
'<div><input tooltip="Hello!" tooltip-trigger="focus" /></div>'
175+
);
176+
$compile(elmBody)(scope);
177+
scope.$apply();
178+
elm = elmBody.find('input');
179+
elmScope = elm.scope();
180+
181+
expect( elmScope.tt_isOpen ).toBeFalsy();
182+
elm.trigger('focus');
183+
expect( elmScope.tt_isOpen ).toBeTruthy();
184+
elm.trigger('blur');
185+
expect( elmScope.tt_isOpen ).toBeFalsy();
186+
}));
187+
188+
it( 'should use it as both the show and hide triggers for unmapped triggers', inject( function( $compile ) {
189+
elmBody = angular.element(
190+
'<div><input tooltip="Hello!" tooltip-trigger="fakeTriggerAttr" /></div>'
191+
);
192+
$compile(elmBody)(scope);
193+
scope.$apply();
194+
elm = elmBody.find('input');
195+
elmScope = elm.scope();
196+
197+
expect( elmScope.tt_isOpen ).toBeFalsy();
198+
elm.trigger('fakeTriggerAttr');
199+
expect( elmScope.tt_isOpen ).toBeTruthy();
200+
elm.trigger('fakeTriggerAttr');
201+
expect( elmScope.tt_isOpen ).toBeFalsy();
202+
}));
203+
});
204+
164205
});
165206

166207
describe( 'tooltipHtmlUnsafe', function() {
@@ -202,13 +243,13 @@ describe( 'tooltipHtmlUnsafe', function() {
202243
});
203244

204245
describe( '$tooltipProvider', function() {
205-
206-
describe( 'popupDelay', function() {
207-
var elm,
246+
var elm,
208247
elmBody,
209-
scope,
210-
elmScope;
248+
scope,
249+
elmScope,
250+
body;
211251

252+
describe( 'popupDelay', function() {
212253
beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider){
213254
$tooltipProvider.options({popupDelay: 1000});
214255
}));
@@ -241,12 +282,6 @@ describe( '$tooltipProvider', function() {
241282
});
242283

243284
describe('appendToBody', function() {
244-
var elm,
245-
elmBody,
246-
scope,
247-
elmScope,
248-
body;
249-
250285
// load the tooltip code
251286
beforeEach(module('ui.bootstrap.tooltip', function ( $tooltipProvider ) {
252287
$tooltipProvider.options({ appendToBody: true });
@@ -275,5 +310,61 @@ describe( '$tooltipProvider', function() {
275310
expect( $body.children().length ).toEqual( bodyLength + 1 );
276311
}));
277312
});
313+
314+
describe( 'triggers', function() {
315+
describe( 'triggers with a mapped value', function() {
316+
beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider){
317+
$tooltipProvider.options({trigger: 'focus'});
318+
}));
319+
320+
// load the template
321+
beforeEach(module('template/tooltip/tooltip-popup.html'));
322+
323+
it( 'should use the show trigger and the mapped value for the hide trigger', inject( function ( $rootScope, $compile ) {
324+
elmBody = angular.element(
325+
'<div><input tooltip="tooltip text" /></div>'
326+
);
327+
328+
scope = $rootScope;
329+
$compile(elmBody)(scope);
330+
scope.$digest();
331+
elm = elmBody.find('input');
332+
elmScope = elm.scope();
333+
334+
expect( elmScope.tt_isOpen ).toBeFalsy();
335+
elm.trigger('focus');
336+
expect( elmScope.tt_isOpen ).toBeTruthy();
337+
elm.trigger('blur');
338+
expect( elmScope.tt_isOpen ).toBeFalsy();
339+
}));
340+
});
341+
342+
describe( 'triggers without a mapped value', function() {
343+
beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider){
344+
$tooltipProvider.options({trigger: 'fakeTrigger'});
345+
}));
346+
347+
// load the template
348+
beforeEach(module('template/tooltip/tooltip-popup.html'));
349+
350+
it( 'should use the show trigger to hide', inject( function ( $rootScope, $compile ) {
351+
elmBody = angular.element(
352+
'<div><span tooltip="tooltip text">Selector Text</span></div>'
353+
);
354+
355+
scope = $rootScope;
356+
$compile(elmBody)(scope);
357+
scope.$digest();
358+
elm = elmBody.find('span');
359+
elmScope = elm.scope();
360+
361+
expect( elmScope.tt_isOpen ).toBeFalsy();
362+
elm.trigger('fakeTrigger');
363+
expect( elmScope.tt_isOpen ).toBeTruthy();
364+
elm.trigger('fakeTrigger');
365+
expect( elmScope.tt_isOpen ).toBeFalsy();
366+
}));
367+
});
368+
});
278369
});
279370

0 commit comments

Comments
 (0)