diff --git a/.travis.yml b/.travis.yml index 1fb368be41..475a17ebbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js sudo: false dist: trusty -node_js: stable +node_js: 8 addons: firefox: latest chrome: stable diff --git a/lib/utils/gestures.html b/lib/utils/gestures.html index 2a05daa462..c968bc914a 100644 --- a/lib/utils/gestures.html +++ b/lib/utils/gestures.html @@ -113,6 +113,20 @@ 'select': true }; + // Defined at https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute + /** @type {!Object} */ + const canBeDisabled = { + 'button': true, + 'command': true, + 'fieldset': true, + 'input': true, + 'keygen': true, + 'optgroup': true, + 'option': true, + 'select': true, + 'textarea': true + }; + /** * @param {HTMLElement} el Element to check labelling status * @return {boolean} element can have labels @@ -1058,7 +1072,7 @@ let dy = Math.abs(e.clientY - this.info.y); // find original target from `preventer` for TouchEvents, or `e` for MouseEvents let t = Gestures._findOriginalTarget(/** @type {Event} */(preventer || e)); - if (!t || t.disabled) { + if (!t || (canBeDisabled[/** @type {!HTMLElement} */(t).localName] && t.hasAttribute('disabled'))) { return; } // dx,dy can be NaN if `click` has been simulated and there was no `down` for `start` diff --git a/test/unit/gestures-elements.html b/test/unit/gestures-elements.html index 15b7abeb1a..a7685ee947 100644 --- a/test/unit/gestures-elements.html +++ b/test/unit/gestures-elements.html @@ -244,24 +244,94 @@ + + + + + + + \ No newline at end of file diff --git a/test/unit/gestures.html b/test/unit/gestures.html index b1a001bdd4..b65c8761df 100644 --- a/test/unit/gestures.html +++ b/test/unit/gestures.html @@ -615,44 +615,111 @@ }); }); - test('disabled elements don\'t fire taps', function() { - let el = document.createElement('x-disabled-tap'); - document.body.appendChild(el); - // tap an element with disabled attribute - let target = el.$.disabled; - // simulate the event sequence of a touch on the screen - let touches = [{ - clientX: 0, - clientY: 0, - identifier: 1, - // target is set to the element with `addEventListener`, which is `target` - target - }]; - let touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true}); - touchstart.changedTouches = touchstart.touches = touches; - target.dispatchEvent(touchstart); - let touchend = new CustomEvent('touchend', {bubbles: true, composed: true}); - touchend.touches = touchend.changedTouches = touches; - target.dispatchEvent(touchend); - assert.equal(el.taps.length, 0); - // tap an element with a disabled ancestor - target = el.$.nested; - // simulate the event sequence of a touch on the screen - touches = [{ - clientX: 0, - clientY: 0, - identifier: 1, - // target is set to the element with `addEventListener`, which is `target` - target - }]; - touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true}); - touchstart.changedTouches = touchstart.touches = touches; - target.dispatchEvent(touchstart); - touchend = new CustomEvent('touchend', {bubbles: true, composed: true}); - touchend.touches = touchend.changedTouches = touches; - target.dispatchEvent(touchend); - assert.equal(el.taps.length, 1); - document.body.removeChild(el); + suite('disabled', function() { + let shouldSkip = true; + + suiteSetup(function() { + /* + * IE 11 does not dispatch events to elements with `disabled` attribute + * This is different from all other browsers, so skip these tests in IE 11 + */ + const div = document.createElement('div'); + div.setAttribute('disabled', ''); + document.body.appendChild(div); + div.addEventListener('click', () => { + shouldSkip = false; + }); + div.click(); + document.body.removeChild(div); + }); + setup(function() { + Polymer.Gestures.resetMouseCanceller(); + }); + + test('click() function works as expected on disabled elements', function() { + if (shouldSkip) { + this.skip(); + } + let el = document.createElement('x-disabled-tap'); + document.body.appendChild(el); + el.$.disabled.click(); + el.$.nested.click(); + el.$.disabledEl.click(); + assert.deepEqual(el.taps, ['button#nested', 'x-disabled#disabledEl']); + document.body.removeChild(el); + }); + + test('disabled elements don\'t fire taps', function() { + if (shouldSkip) { + this.skip(); + } + let el = document.createElement('x-disabled-tap'); + document.body.appendChild(el); + // tap an element with disabled attribute + let target = el.$.disabled; + // simulate the event sequence of a touch on the screen + let touches = [{ + clientX: 0, + clientY: 0, + identifier: 1, + // target is set to the element with `addEventListener`, which is `target` + target + }]; + let touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true}); + touchstart.changedTouches = touchstart.touches = touches; + target.dispatchEvent(touchstart); + let touchend = new CustomEvent('touchend', {bubbles: true, composed: true}); + touchend.touches = touchend.changedTouches = touches; + target.dispatchEvent(touchend); + assert.deepEqual(el.taps, []); + + // tap an element with a disabled ancestor + target = el.$.nested; + // simulate the event sequence of a touch on the screen + touches = [{ + clientX: 0, + clientY: 0, + identifier: 1, + // target is set to the element with `addEventListener`, which is `target` + target + }]; + touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true}); + touchstart.changedTouches = touchstart.touches = touches; + target.dispatchEvent(touchstart); + touchend = new CustomEvent('touchend', {bubbles: true, composed: true}); + touchend.touches = touchend.changedTouches = touches; + target.dispatchEvent(touchend); + assert.deepEqual(el.taps, ['button#nested']); + + Polymer.Gestures.resetMouseCanceller(); + + // tap a custom element with a `disabled` property + target = el.$.disabledEl; + // simulate the event sequence of a touch on the screen + touches = [{ + clientX: 0, + clientY: 0, + identifier: 1, + // target is set to the element with `addEventListener`, which is `target` + target + }]; + touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true}); + touchstart.changedTouches = touchstart.touches = touches; + target.dispatchEvent(touchstart); + touchend = new CustomEvent('touchend', {bubbles: true, composed: true}); + touchend.touches = touchend.changedTouches = touches; + target.dispatchEvent(touchend); + assert.deepEqual(el.taps, ['button#nested', 'x-disabled#disabledEl']); + document.body.removeChild(el); + }); + + test('test all "disableable" elements', function() { + const el = document.createElement('all-disabled'); + document.body.appendChild(el); + el.tapAll(); + assert.deepEqual(el.taps, []); + document.body.removeChild(el); + }); }); });