From 2b429f5de514d6a3baf586e13fbaf39a8c2731b1 Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Sun, 16 Mar 2014 23:50:57 -0700 Subject: [PATCH] fix(tooltip): animation causes tooltip to hide on show Add logic to handle cases where hide/show can be called multiple times even before their timeouts complete. This is more ugly logic to handle degenerate cases. Hopefully switching over to ngAnimate and cleaning up the logic of tooltips will be better. Fixes #1847 Closes #1940 --- src/tooltip/test/tooltip2.spec.js | 31 +++++++++++++++++++++++++++++++ src/tooltip/tooltip.js | 28 +++++++++++++++++++--------- 2 files changed, 50 insertions(+), 9 deletions(-) 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;