From 873730360c84baeb81957704f991deed495358c8 Mon Sep 17 00:00:00 2001 From: Timothy Zorn Date: Tue, 15 Sep 2015 14:40:07 -0400 Subject: [PATCH] feat(tooltip): add outsideClick trigger - Add `outsideClick` trigger support, which allows dismissal of the tooltip/popover on click of any element outside of the tooltip/popover Closes #4419 Closes #4871 --- src/tooltip/docs/readme.md | 3 +++ src/tooltip/test/tooltip.spec.js | 25 +++++++++++++++++++++++++ src/tooltip/tooltip.js | 30 +++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/tooltip/docs/readme.md b/src/tooltip/docs/readme.md index e0c96c3a5e..5e91c96f3a 100644 --- a/src/tooltip/docs/readme.md +++ b/src/tooltip/docs/readme.md @@ -42,9 +42,12 @@ provided hide triggers: - `mouseenter`: `mouseleave` - `click`: `click` +- `outsideClick`: `outsideClick` - `focus`: `blur` - `none`: `` +The `outsideClick` trigger will cause the tooltip to toggle on click, and hide when anything else is clicked. + For any non-supported value, the trigger will be used to both show and hide the tooltip. Using the 'none' trigger will disable the internal trigger(s), one can then use the `tooltip-is-open` attribute exclusively to show and hide the tooltip. diff --git a/src/tooltip/test/tooltip.spec.js b/src/tooltip/test/tooltip.spec.js index 04898aa07e..a6f413274a 100644 --- a/src/tooltip/test/tooltip.spec.js +++ b/src/tooltip/test/tooltip.spec.js @@ -596,6 +596,31 @@ describe('tooltip', function() { elm.trigger('mouseenter'); expect(tooltipScope.isOpen).toBeFalsy(); })); + + it('should toggle on click and hide when anything else is clicked when trigger is set to "outsideClick"', inject(function($compile, $document) { + elm = $compile(angular.element( + 'Selector Text' + ))(scope); + scope.$apply(); + elmScope = elm.scope(); + tooltipScope = elmScope.$$childTail; + + // start off + expect(tooltipScope.isOpen).toBeFalsy(); + + // toggle + trigger(elm, 'click'); + expect(tooltipScope.isOpen).toBeTruthy(); + trigger(elm, 'click'); + expect(tooltipScope.isOpen).toBeFalsy(); + + // click on, outsideClick off + trigger(elm, 'click'); + expect(tooltipScope.isOpen).toBeTruthy(); + angular.element($document[0].body).trigger('click'); + tooltipScope.$digest(); + expect(tooltipScope.isOpen).toBeFalsy(); + })); }); describe('with an append-to-body attribute', function() { diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js index 458404483b..2f363ab652 100644 --- a/src/tooltip/tooltip.js +++ b/src/tooltip/tooltip.js @@ -23,6 +23,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s var triggerMap = { 'mouseenter': 'mouseleave', 'click': 'click', + 'outsideClick': 'outsideClick', 'focus': 'blur', 'none': '' }; @@ -438,13 +439,33 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s } } + // hide tooltips/popovers for outsideClick trigger + function bodyHideTooltipBind(e) { + if (!ttScope || !ttScope.isOpen || !tooltip) { + return; + } + // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked + if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { + hideTooltipBind(); + } + } + var unregisterTriggers = function() { triggers.show.forEach(function(trigger) { - element.unbind(trigger, showTooltipBind); + if (trigger === 'outsideClick') { + element[0].removeEventListener('click', toggleTooltipBind); + } else { + element[0].removeEventListener(trigger, showTooltipBind); + element[0].removeEventListener(trigger, toggleTooltipBind); + } }); triggers.hide.forEach(function(trigger) { trigger.split(' ').forEach(function(hideTrigger) { - element[0].removeEventListener(hideTrigger, hideTooltipBind); + if (trigger === 'outsideClick') { + $document[0].removeEventListener('click', bodyHideTooltipBind); + } else { + element[0].removeEventListener(hideTrigger, hideTooltipBind); + } }); }); }; @@ -458,7 +479,10 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s if (triggers.show !== 'none') { triggers.show.forEach(function(trigger, idx) { // Using raw addEventListener due to jqLite/jQuery bug - #4060 - if (trigger === triggers.hide[idx]) { + if (trigger === 'outsideClick') { + element[0].addEventListener('click', toggleTooltipBind); + $document[0].addEventListener('click', bodyHideTooltipBind); + } else if (trigger === triggers.hide[idx]) { element[0].addEventListener(trigger, toggleTooltipBind); } else if (trigger) { element[0].addEventListener(trigger, showTooltipBind);