diff --git a/src/tooltip/test/tooltip2.spec.js b/src/tooltip/test/tooltip2.spec.js index fb5956e641..7162d23208 100644 --- a/src/tooltip/test/tooltip2.spec.js +++ b/src/tooltip/test/tooltip2.spec.js @@ -102,4 +102,35 @@ describe('tooltip directive', function () { }); }); + + it('should show even after close trigger is called multiple times - issue #1847', function () { + var fragment = compileTooltip('Trigger here'); + + fragment.find('span').trigger( 'mouseenter' ); + expect(fragment).toHaveOpenTooltips(); + + closeTooltip(fragment.find('span'), null, true); + // Close trigger is called again before timer completes + // The close trigger can be called any number of times (even after close has already been called) + // since users can trigger the hide triggers manually. + closeTooltip(fragment.find('span'), null, true); + expect(fragment).toHaveOpenTooltips(); + + fragment.find('span').trigger( 'mouseenter' ); + expect(fragment).toHaveOpenTooltips(); + + $timeout.flush(); + expect(fragment).toHaveOpenTooltips(); + }); + + it('should hide even after show trigger is called multiple times', function () { + var fragment = compileTooltip('Trigger here'); + + fragment.find('span').trigger( 'mouseenter' ); + fragment.find('span').trigger( 'mouseenter' ); + + closeTooltip(fragment.find('span')); + expect(fragment).not.toHaveOpenTooltips(); + }); + }); \ No newline at end of file diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js index da1b356799..de6d66dc16 100644 --- a/src/tooltip/tooltip.js +++ b/src/tooltip/tooltip.js @@ -147,8 +147,12 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap return; } if ( scope.tt_popupDelay ) { - popupTimeout = $timeout( show, scope.tt_popupDelay, false ); - popupTimeout.then(function(reposition){reposition();}); + // Do nothing if the tooltip was already scheduled to pop-up. + // This happens if show is triggered multiple times before any hide is triggered. + if (!popupTimeout) { + popupTimeout = $timeout( show, scope.tt_popupDelay, false ); + popupTimeout.then(function(reposition){reposition();}); + } } else { show()(); } @@ -163,6 +167,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap // Show the tooltip popup element. function show() { + popupTimeout = null; + + // If there is a pending remove transition, we must cancel it, lest the + // tooltip be mysteriously removed. + if ( transitionTimeout ) { + $timeout.cancel( transitionTimeout ); + transitionTimeout = null; + } // Don't show empty tooltips. if ( ! scope.tt_content ) { @@ -171,12 +183,6 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap createTooltip(); - // If there is a pending remove transition, we must cancel it, lest the - // tooltip be mysteriously removed. - if ( transitionTimeout ) { - $timeout.cancel( transitionTimeout ); - } - // Set the initial positioning. tooltip.css({ top: 0, left: 0, display: 'block' }); @@ -206,12 +212,15 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap //if tooltip is going to be shown after delay, we must cancel this $timeout.cancel( popupTimeout ); + popupTimeout = null; // 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 ( scope.tt_animation ) { - transitionTimeout = $timeout(removeTooltip, 500); + if (!transitionTimeout) { + transitionTimeout = $timeout(removeTooltip, 500); + } } else { removeTooltip(); } @@ -229,6 +238,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap } function removeTooltip() { + transitionTimeout = null; if (tooltip) { tooltip.remove(); tooltip = null;