diff --git a/lib/utils/gestures.html b/lib/utils/gestures.html index ca38ec3173..2a05daa462 100644 --- a/lib/utils/gestures.html +++ b/lib/utils/gestures.html @@ -101,7 +101,7 @@ /** @type {!Array} */ const clickedLabels = []; - /** @type {!Map} */ + /** @type {!Object} */ const labellable = { 'button': true, 'input': true, @@ -126,13 +126,12 @@ * @return {!Array} Relevant label for `el` */ function matchingLabels(el) { - /** @type {!Array} */ - let labels = el.labels; + let labels = [...(/** @type {HTMLInputElement} */(el).labels || [])]; // IE doesn't have `labels` and Safari doesn't populate `labels` // if element is in a shadowroot. // In this instance, finding the non-ancestor labels is enough, // as the mouseCancellor code will handle ancstor labels - if (!labels || !labels.length) { + if (!labels.length) { labels = []; let root = el.getRootNode(); // if there is an id on `el`, check for all labels with a matching `for` attribute @@ -1059,7 +1058,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) { + if (!t || t.disabled) { return; } // dx,dy can be NaN if `click` has been simulated and there was no `down` for `start` diff --git a/test/smoke/disabled-attr-gestures.html b/test/smoke/disabled-attr-gestures.html new file mode 100644 index 0000000000..e33cf34e1a --- /dev/null +++ b/test/smoke/disabled-attr-gestures.html @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/unit/gestures-elements.html b/test/unit/gestures-elements.html index 534b4edfbf..15b7abeb1a 100644 --- a/test/unit/gestures-elements.html +++ b/test/unit/gestures-elements.html @@ -229,17 +229,39 @@ - - - \ No newline at end of file + } + customElements.define(XNativeLabelNested.is, XNativeLabelNested); + + + + + + + \ No newline at end of file diff --git a/test/unit/gestures.html b/test/unit/gestures.html index 30866d06fa..b1a001bdd4 100644 --- a/test/unit/gestures.html +++ b/test/unit/gestures.html @@ -587,10 +587,9 @@ assert.equal(el.$.check.checked, true, 'checkbox should be checked'); document.body.removeChild(el); }); - }); - test('label click with nested element', function() { - let el = document.createElement('x-native-label-nested'); + test('label click with nested element', function() { + let el = document.createElement('x-native-label-nested'); document.body.appendChild(el); let target = el.$.label; // simulate the event sequence of a touch on the screen @@ -613,6 +612,47 @@ // check that the mouse click on the label will activate the checkbox assert.equal(el.$.check.checked, true, 'checkbox should be checked'); document.body.removeChild(el); + }); + }); + + 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); }); });