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

Commit 99b87cc

Browse files
RobJacobswesleycho
authored andcommitted
feat(tooltip): expose isOpen property
Add support for toggling the isOpen property of the tooltip. Closes #4179 Closes #2148 Fixes #590
1 parent dbd6f73 commit 99b87cc

File tree

5 files changed

+119
-14
lines changed

5 files changed

+119
-14
lines changed

src/popover/docs/readme.md

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ will display:
2626
`tooltip` directive for supported values.
2727
- `popover-append-to-body`: Should the tooltip be appended to `$body` instead of
2828
the parent element?
29+
- `popover-is-open` <i class="glyphicon glyphicon-eye-open"></i>
30+
_(Default: false)_:
31+
Whether to show the popover.
2932

3033
The popover directives require the `$position` service.
3134

src/popover/test/popover.spec.js

+31
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,37 @@ describe('popover', function() {
137137
}));
138138

139139
});
140+
141+
describe( 'is-open', function() {
142+
beforeEach(inject(function ($compile) {
143+
scope.isOpen = false;
144+
elmBody = angular.element(
145+
'<div><span popover="popover text" popover-placement="left" popover-is-open="isOpen">Trigger here</span></div>'
146+
);
147+
$compile(elmBody)(scope);
148+
scope.$digest();
149+
elm = elmBody.find('span');
150+
elmScope = elm.scope();
151+
tooltipScope = elmScope.$$childTail;
152+
}));
153+
154+
it( 'should show and hide with the controller value', function() {
155+
expect(tooltipScope.isOpen).toBe(false);
156+
elmScope.isOpen = true;
157+
elmScope.$digest();
158+
expect(tooltipScope.isOpen).toBe(true);
159+
elmScope.isOpen = false;
160+
elmScope.$digest();
161+
expect(tooltipScope.isOpen).toBe(false);
162+
});
163+
164+
it( 'should update the controller value', function() {
165+
elm.trigger('click');
166+
expect(elmScope.isOpen).toBe(true);
167+
elm.trigger('click');
168+
expect(elmScope.isOpen).toBe(false);
169+
});
170+
});
140171

141172
});
142173

src/tooltip/docs/readme.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ will display:
2727
- `tooltip-append-to-body`: Should the tooltip be appended to `$body` instead of
2828
the parent element?
2929
- `tooltip-class`: Custom class to be applied to the tooltip.
30+
- `tooltip-is-open` <i class="glyphicon glyphicon-eye-open"></i>
31+
_(Default: false)_:
32+
Whether to show the tooltip.
3033

3134
The tooltip directives require the `$position` service.
3235

@@ -38,9 +41,11 @@ provided hide triggers:
3841
- `mouseenter`: `mouseleave`
3942
- `click`: `click`
4043
- `focus`: `blur`
44+
- `none`: ``
4145

4246
For any non-supported value, the trigger will be used to both show and hide the
43-
tooltip.
47+
tooltip. Using the 'none' trigger will disable the internal trigger(s), one can
48+
then use the `tooltip-is-open` attribute exclusively to show and hide the tooltip.
4449

4550
**$tooltipProvider**
4651

src/tooltip/test/tooltip.spec.js

+43
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,35 @@ describe('tooltip', function() {
307307
}));
308308

309309
});
310+
311+
describe( 'with an is-open attribute', function() {
312+
beforeEach(inject(function ($compile) {
313+
scope.isOpen = false;
314+
elm = $compile(angular.element(
315+
'<span tooltip="tooltip text" tooltip-is-open="isOpen" >Selector Text</span>'
316+
))(scope);
317+
elmScope = elm.scope();
318+
tooltipScope = elmScope.$$childTail;
319+
scope.$digest();
320+
}));
321+
322+
it( 'should show and hide with the controller value', function() {
323+
expect(tooltipScope.isOpen).toBe(false);
324+
elmScope.isOpen = true;
325+
elmScope.$digest();
326+
expect(tooltipScope.isOpen).toBe(true);
327+
elmScope.isOpen = false;
328+
elmScope.$digest();
329+
expect(tooltipScope.isOpen).toBe(false);
330+
});
331+
332+
it( 'should update the controller value', function() {
333+
elm.trigger('mouseenter');
334+
expect(elmScope.isOpen).toBe(true);
335+
elm.trigger('mouseleave');
336+
expect(elmScope.isOpen).toBe(false);
337+
});
338+
});
310339

311340
describe( 'with a trigger attribute', function() {
312341
var scope, elmBody, elm, elmScope;
@@ -398,6 +427,20 @@ describe('tooltip', function() {
398427
elm.trigger('fakeTriggerAttr');
399428
expect( tooltipScope.isOpen ).toBeFalsy();
400429
}));
430+
431+
it( 'should not show when trigger is set to "none"', inject( function( $compile ) {
432+
elmBody = angular.element(
433+
'<div><input tooltip="Hello!" tooltip-trigger="none" /></div>'
434+
);
435+
$compile(elmBody)(scope);
436+
scope.$apply();
437+
elm = elmBody.find('input');
438+
elmScope = elm.scope();
439+
tooltipScope = elmScope.$$childTail;
440+
expect( tooltipScope.isOpen ).toBeFalsy();
441+
elm.trigger('mouseenter');
442+
expect( tooltipScope.isOpen ).toBeFalsy();
443+
}));
401444
});
402445

403446
describe( 'with an append-to-body attribute', function() {

src/tooltip/tooltip.js

+36-13
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2222
var triggerMap = {
2323
'mouseenter': 'mouseleave',
2424
'click': 'click',
25-
'focus': 'blur'
25+
'focus': 'blur',
26+
'none': ''
2627
};
2728

2829
// The options specified to the provider globally.
@@ -65,7 +66,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
6566
* Returns the actual instance of the $tooltip service.
6667
* TODO support multiple triggers
6768
*/
68-
this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', '$rootScope', function ( $window, $compile, $timeout, $document, $position, $interpolate, $rootScope ) {
69+
this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', '$rootScope', '$parse', function ( $window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse ) {
6970
return function $tooltip ( type, prefix, defaultTriggerShow, options ) {
7071
options = angular.extend( {}, defaultOptions, globalOptions, options );
7172

@@ -127,6 +128,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
127128
var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
128129
var ttScope = scope.$new(true);
129130
var repositionScheduled = false;
131+
var isOpenExp = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
130132

131133
var positionTooltip = function () {
132134
if (!tooltip) { return; }
@@ -211,7 +213,13 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
211213

212214
// And show the tooltip.
213215
ttScope.isOpen = true;
214-
ttScope.$apply(); // digest required as $apply is not called
216+
if (isOpenExp) {
217+
isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
218+
}
219+
220+
if (!$rootScope.$$phase) {
221+
ttScope.$apply(); // digest required as $apply is not called
222+
}
215223

216224
// Return positioning function as promise callback for correct
217225
// positioning after draw.
@@ -222,7 +230,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
222230
function hide() {
223231
// First things first: we don't show it anymore.
224232
ttScope.isOpen = false;
225-
233+
if (isOpenExp) {
234+
isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
235+
}
236+
226237
//if tooltip is going to be shown after delay, we must cancel this
227238
$timeout.cancel( popupTimeout );
228239
popupTimeout = null;
@@ -265,7 +276,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
265276
repositionScheduled = true;
266277
tooltipLinkedScope.$$postDigest(function() {
267278
repositionScheduled = false;
268-
positionTooltipAsync();
279+
if (ttScope.isOpen) {
280+
positionTooltipAsync();
281+
}
269282
});
270283
}
271284
});
@@ -333,6 +346,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
333346
}, 0, false);
334347
}
335348
});
349+
350+
if (isOpenExp) {
351+
scope.$watch(isOpenExp, function(val) {
352+
if (val !== ttScope.isOpen) {
353+
toggleTooltipBind();
354+
}
355+
});
356+
}
336357

337358
function prepPopupClass() {
338359
ttScope.popupClass = attrs[prefix + 'Class'];
@@ -364,14 +385,16 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
364385

365386
triggers = getTriggers( val );
366387

367-
triggers.show.forEach(function(trigger, idx) {
368-
if (trigger === triggers.hide[idx]) {
369-
element.bind(trigger, toggleTooltipBind);
370-
} else if (trigger) {
371-
element.bind(trigger, showTooltipBind);
372-
element.bind(triggers.hide[idx], hideTooltipBind);
373-
}
374-
});
388+
if (triggers.show !== 'none') {
389+
triggers.show.forEach(function(trigger, idx) {
390+
if (trigger === triggers.hide[idx]) {
391+
element.bind(trigger, toggleTooltipBind);
392+
} else if (trigger) {
393+
element.bind(trigger, showTooltipBind);
394+
element.bind(triggers.hide[idx], hideTooltipBind);
395+
}
396+
});
397+
}
375398
}
376399
prepTriggers();
377400

0 commit comments

Comments
 (0)