Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
fix(tooltip): pointer-events 'none' used properly with activate events
Browse files Browse the repository at this point in the history
Mouseenter, focus, and touchstart properly checks computed style and will not allow tooltips to be shown when pointer-events  == 'none'
  • Loading branch information
ThomasBurleson committed May 29, 2015
1 parent abadb53 commit 667e4c2
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 43 deletions.
19 changes: 15 additions & 4 deletions src/components/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe

function getParentWithPointerEvents () {
var parent = element.parent();
while ($window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
while (parent && $window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
parent = parent.parent();
}
return parent;
Expand All @@ -120,8 +120,19 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
return current;
}

function hasComputedStyleValue(key, value) {
// Check if we should show it or not...
var computedStyles = $window.getComputedStyle(element[0]);
return angular.isDefined(computedStyles[key]) && (computedStyles[key] == value);
}

function bindEvents () {
var mouseActive = false;
var enterHandler = function() {
if (!hasComputedStyleValue('pointer-events','none')) {
setVisible(true);
}
};
var leaveHandler = function () {
var autohide = scope.hasOwnProperty('autohide') ? scope.autohide : attr.hasOwnProperty('mdAutohide');
if ($document[0].activeElement !== parent[0] || autohide || mouseActive) {
Expand All @@ -132,7 +143,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe

// to avoid `synthetic clicks` we listen to mousedown instead of `click`
parent.on('mousedown', function() { mouseActive = true; });
parent.on('focus mouseenter touchstart', function() { setVisible(true); });
parent.on('focus mouseenter touchstart', enterHandler );
parent.on('blur mouseleave touchend touchcancel', leaveHandler );


Expand Down Expand Up @@ -161,8 +172,8 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe

// Check if we should display it or not.
// This handles hide-* and show-* along with any user defined css
var computedStyles = $window.getComputedStyle(element[0]);
if (angular.isDefined(computedStyles.display) && computedStyles.display == 'none') {
if ( hasComputedStyleValue('display','none') ) {
scope.visible = false;
element.detach();
return;
}
Expand Down
72 changes: 33 additions & 39 deletions src/components/tooltip/tooltip.spec.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
describe('<md-tooltip> directive', function() {
var $compile, $rootScope, $animate;
var $compile, $rootScope, $animate, $timeout;
var element;

beforeEach(module('material.components.tooltip', 'ngAnimateMock'));
beforeEach(inject(function(_$compile_, _$rootScope_, _$animate_){
beforeEach(inject(function(_$compile_, _$rootScope_, _$animate_, _$timeout_){
$compile = _$compile_;
$rootScope = _$rootScope_;
$animate = _$animate_;
$timeout = _$timeout_;
}));
afterEach(function() {
// Make sure to remove/cleanup after each test
Expand Down Expand Up @@ -39,7 +40,7 @@ describe('<md-tooltip> directive', function() {

it('should not set parent to items with no pointer events', inject(function($window){
spyOn($window, 'getComputedStyle').and.callFake(function(el) {
return { 'pointer-events': el.nodeName == 'INNER' ? 'none' : '' };
return { 'pointer-events': el ? 'none' : '' };
});

buildTooltip(
Expand All @@ -52,12 +53,12 @@ describe('<md-tooltip> directive', function() {
'</outer>'
);

element.triggerHandler('mouseenter');
simulateEvent('mouseenter', true);
expect($rootScope.isVisible).toBeUndefined();

}));

it('should show after tooltipDelay ms', inject(function($timeout) {
it('should show after tooltipDelay ms', function() {
buildTooltip(
'<md-button>' +
'Hello' +
Expand All @@ -78,7 +79,7 @@ describe('<md-tooltip> directive', function() {
$timeout.flush(1);
expect($rootScope.isVisible).toBe(true);

}));
});

describe('show and hide', function() {

Expand Down Expand Up @@ -106,7 +107,7 @@ describe('<md-tooltip> directive', function() {
expect(findTooltip().length).toBe(0);
});

it('should set visible on mouseenter and mouseleave', inject(function($timeout) {
it('should set visible on mouseenter and mouseleave', function() {
buildTooltip(
'<md-button>' +
'Hello' +
Expand All @@ -116,18 +117,14 @@ describe('<md-tooltip> directive', function() {
'</md-button>'
);

element.triggerHandler('mouseenter');
$timeout.flush();

expect($rootScope.isVisible).toBe(true);

element.triggerHandler('mouseleave');
$timeout.flush();
simulateEvent('mouseenter');
expect($rootScope.isVisible).toBe(true);

expect($rootScope.isVisible).toBe(false);
}));
simulateEvent('mouseleave');
expect($rootScope.isVisible).toBe(false);
});

it('should set visible on focus and blur', inject(function($timeout) {
it('should set visible on focus and blur', function() {
buildTooltip(
'<md-button>' +
'Hello' +
Expand All @@ -137,18 +134,14 @@ describe('<md-tooltip> directive', function() {
'</md-button>'
);

element.triggerHandler('focus');
$timeout.flush();

simulateEvent('focus');
expect($rootScope.isVisible).toBe(true);

element.triggerHandler('blur');
$timeout.flush();

simulateEvent('blur');
expect($rootScope.isVisible).toBe(false);
}));
});

it('should set visible on touchstart and touchend', inject(function($timeout) {
it('should set visible on touchstart and touchend', function() {
buildTooltip(
'<md-button>' +
'Hello' +
Expand All @@ -159,18 +152,15 @@ describe('<md-tooltip> directive', function() {
);


element.triggerHandler('touchstart');
$timeout.flush();

expect($rootScope.isVisible).toBe(true);
simulateEvent('touchstart');
expect($rootScope.isVisible).toBe(true);

element.triggerHandler('touchend');
$timeout.flush();
simulateEvent('touchend');
expect($rootScope.isVisible).toBe(false);

expect($rootScope.isVisible).toBe(false);
}));
});

it('should not be visible on mousedown and then mouseleave', inject(function($timeout, $document) {
it('should not be visible on mousedown and then mouseleave', inject(function( $document) {
jasmine.mockElementFocus(this);

buildTooltip(
Expand All @@ -185,15 +175,12 @@ describe('<md-tooltip> directive', function() {
// this focus is needed to set `$document.activeElement`
// and wouldn't be required if `document.activeElement` was settable.
element.focus();
element.triggerHandler('focus');
element.triggerHandler('mousedown');
$timeout.flush();
simulateEvent('focus, mousedown');

expect($document.activeElement).toBe(element[0]);
expect($rootScope.isVisible).toBe(true);

element.triggerHandler('mouseleave');
$timeout.flush();
simulateEvent('mouseleave');

// very weak test since this is really always set to false because
// we are not able to set `document.activeElement` to the parent
Expand Down Expand Up @@ -228,4 +215,11 @@ describe('<md-tooltip> directive', function() {
}


function simulateEvent(eventType, skipFlush) {
angular.forEach(eventType.split(','),function(name) {
element.triggerHandler(name);
});
!skipFlush && $timeout.flush();
}

});

0 comments on commit 667e4c2

Please sign in to comment.