diff --git a/src/components/tooltip/tooltip.js b/src/components/tooltip/tooltip.js index 398ff0f9eba..389331dd705 100644 --- a/src/components/tooltip/tooltip.js +++ b/src/components/tooltip/tooltip.js @@ -134,7 +134,25 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe function bindEvents () { var mouseActive = false; - var enterHandler = function() { + + var ngWindow = angular.element($window); + + // Store whether the element was focused when the window loses focus. + var windowBlurHandler = function() { + elementFocusedOnWindowBlur = document.activeElement === parent[0]; + }; + var elementFocusedOnWindowBlur = false; + ngWindow.on('blur', windowBlurHandler); + scope.$on('$destroy', function() { + ngWindow.off('blur', windowBlurHandler); + }); + + var enterHandler = function(e) { + // Prevent the tooltip from showing when the window is receiving focus. + if (e.type === 'focus' && elementFocusedOnWindowBlur) { + elementFocusedOnWindowBlur = false; + return; + } parent.on('blur mouseleave touchend touchcancel', leaveHandler ); setVisible(true); }; diff --git a/src/components/tooltip/tooltip.spec.js b/src/components/tooltip/tooltip.spec.js index 197b8549cfb..b36c3af5deb 100644 --- a/src/components/tooltip/tooltip.spec.js +++ b/src/components/tooltip/tooltip.spec.js @@ -3,6 +3,7 @@ describe(' directive', function() { var element; beforeEach(module('material.components.tooltip')); + beforeEach(module('material.components.button')); beforeEach(inject(function(_$compile_, _$rootScope_, _$animate_, _$timeout_){ $compile = _$compile_; $rootScope = _$rootScope_; @@ -19,7 +20,7 @@ describe(' directive', function() { buildTooltip( '' + 'Hello' + - 'Tooltip' + + 'Tooltip' + '' ); @@ -29,7 +30,7 @@ describe(' directive', function() { it('should label parent', function(){ buildTooltip( '' + - '' + + '' + 'Tooltip' + ''+ '' @@ -46,7 +47,7 @@ describe(' directive', function() { buildTooltip( '' + '' + - '' + + '' + 'Hello world' + '' + '' + @@ -54,7 +55,7 @@ describe(' directive', function() { ); triggerEvent('mouseenter', true); - expect($rootScope.isVisible).toBeUndefined(); + expect($rootScope.testModel.isVisible).toBeUndefined(); })); @@ -62,22 +63,22 @@ describe(' directive', function() { buildTooltip( '' + 'Hello' + - '' + + '' + 'Tooltip' + '' + '' ); triggerEvent('focus', true); - expect($rootScope.isVisible).toBeFalsy(); + expect($rootScope.testModel.isVisible).toBeFalsy(); // Wait 1 below delay, nothing should happen $timeout.flush(98); - expect($rootScope.isVisible).toBeFalsy(); + expect($rootScope.testModel.isVisible).toBeFalsy(); // Total 300 == tooltipDelay $timeout.flush(1); - expect($rootScope.isVisible).toBe(true); + expect($rootScope.testModel.isVisible).toBe(true); }); @@ -90,7 +91,7 @@ describe(' directive', function() { buildTooltip( '' + 'Hello' + - '' + + '' + 'Tooltip' + '' + '' @@ -111,41 +112,41 @@ describe(' directive', function() { buildTooltip( '' + 'Hello' + - '' + + '' + 'Tooltip' + '' + '' ); triggerEvent('mouseenter'); - expect($rootScope.isVisible).toBe(true); + expect($rootScope.testModel.isVisible).toBe(true); triggerEvent('mouseleave'); - expect($rootScope.isVisible).toBe(false); + expect($rootScope.testModel.isVisible).toBe(false); }); it('should set visible on focus and blur', function() { buildTooltip( '' + 'Hello' + - '' + + '' + 'Tooltip' + '' + '' ); triggerEvent('focus'); - expect($rootScope.isVisible).toBe(true); + expect($rootScope.testModel.isVisible).toBe(true); triggerEvent('blur'); - expect($rootScope.isVisible).toBe(false); + expect($rootScope.testModel.isVisible).toBe(false); }); it('should set visible on touchstart and touchend', function() { buildTooltip( '' + 'Hello' + - '' + + '' + 'Tooltip' + '' + '' @@ -153,42 +154,59 @@ describe(' directive', function() { triggerEvent('touchstart'); - expect($rootScope.isVisible).toBe(true); + expect($rootScope.testModel.isVisible).toBe(true); triggerEvent('touchend'); - expect($rootScope.isVisible).toBe(false); + expect($rootScope.testModel.isVisible).toBe(false); }); - it('should not be visible on mousedown and then mouseleave', inject(function( $document) { - jasmine.mockElementFocus(this); - + it('should not be visible on mousedown and then mouseleave', inject(function($document) { buildTooltip( '' + 'Hello' + - '' + + '' + 'Tooltip' + '' + '' ); - // this focus is needed to set `$document.activeElement` - // and wouldn't be required if `document.activeElement` was settable. - element.focus(); - triggerEvent('focus, mousedown'); + // Append element to DOM so it can be set as activeElement. + $document[0].body.appendChild(element[0]); + element[0].focus(); + triggerEvent('focus,mousedown'); - expect($document.activeElement).toBe(element[0]); - expect($rootScope.isVisible).toBe(true); + expect($document[0].activeElement).toBe(element[0]); + expect($rootScope.testModel.isVisible).toBe(true); triggerEvent('mouseleave'); + expect($rootScope.testModel.isVisible).toBe(false); + })); - // very weak test since this is really always set to false because - // we are not able to set `document.activeElement` to the parent - // of `md-tooltip`. we compensate by testing `$document.activeElement` - // which sort of mocks the behavior through `jasmine.mockElementFocus` - // which should be replaced by a true `document.activeElement` check - // if the problem gets fixed. - expect($rootScope.isVisible).toBe(false); + it('should not be visible when the window is refocused', inject(function($window, $document) { + buildTooltip( + '' + + 'Hello' + + '' + + 'Tooltip' + + '' + + '' + ); + + // Append element to DOM so it can be set as activeElement. + $document[0].body.appendChild(element[0]); + element[0].focus(); + triggerEvent('focus,mousedown'); + expect(document.activeElement).toBe(element[0]); + + triggerEvent('mouseleave'); + + // Simulate tabbing away. + angular.element($window).triggerHandler('blur'); + + // Simulate focus event that occurs when tabbing back to the window. + triggerEvent('focus'); + expect($rootScope.testModel.isVisible).toBe(false); })); }); @@ -199,6 +217,7 @@ describe(' directive', function() { function buildTooltip(markup) { element = $compile(markup)($rootScope); + $rootScope.testModel = {}; $rootScope.$apply(); $animate.triggerCallbacks(); @@ -209,7 +228,7 @@ describe(' directive', function() { function showTooltip(isVisible) { if (angular.isUndefined(isVisible)) isVisible = true; - $rootScope.$apply('isVisible = ' + (isVisible ? 'true' : 'false') ); + $rootScope.$apply('testModel.isVisible = ' + (isVisible ? 'true' : 'false') ); $animate.triggerCallbacks(); }