Skip to content

Commit

Permalink
Handle disabled attribute correctly for tap gesture
Browse files Browse the repository at this point in the history
Fixes #4685
  • Loading branch information
dfreedm committed Feb 22, 2018
1 parent 481dea6 commit 5c0f3e6
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 21 deletions.
9 changes: 4 additions & 5 deletions lib/utils/gestures.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
/** @type {!Array<!HTMLLabelElement>} */
const clickedLabels = [];

/** @type {!Map<string, boolean>} */
/** @type {!Object<boolean>} */
const labellable = {
'button': true,
'input': true,
Expand All @@ -126,13 +126,12 @@
* @return {!Array<!HTMLLabelElement>} Relevant label for `el`
*/
function matchingLabels(el) {
/** @type {!Array<!HTMLLabelElement>} */
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
Expand Down Expand Up @@ -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`
Expand Down
39 changes: 39 additions & 0 deletions test/smoke/disabled-attr-gestures.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!doctype html>
<!--
@license
Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<script src="../../../webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="../../polymer.html">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<dom-module id="x-disabled">
<template>
<button id="disabledbutton" on-tap="tap" disabled>Disabled Button</button>
<div disabled>
<button id="nestedbutton" on-tap="tap">Nested Button</button>
</div>
</template>
<script>
addEventListener('WebComponentsReady', function() {
class XDisabled extends Polymer.GestureEventListeners(Polymer.Element) {
static get is() {return 'x-disabled';}
tap(e) {
console.log(`${e.composedPath()[0].id} tapped`);
}
}
customElements.define(XDisabled.is, XDisabled);
});
</script>
</dom-module>
<x-disabled></x-disabled>
</body>
</html>
48 changes: 35 additions & 13 deletions test/unit/gestures-elements.html
Original file line number Diff line number Diff line change
Expand Up @@ -229,17 +229,39 @@
</dom-module>

<dom-module id="x-native-label-nested">
<template>
<label id="label">
<input id="check" type="checkbox">
</label>
</template>
<script>
class XNativeLabelNested extends Polymer.Element {
static get is() {
return 'x-native-label-nested';
}
<template>
<label id="label">
<input id="check" type="checkbox">
</label>
</template>
<script>
class XNativeLabelNested extends Polymer.Element {
static get is() {
return 'x-native-label-nested';
}
customElements.define(XNativeLabelNested.is, XNativeLabelNested);
</script>
</dom-module>
}
customElements.define(XNativeLabelNested.is, XNativeLabelNested);
</script>
</dom-module>

<dom-module id="x-disabled-tap">
<template>
<button id="disabled" on-tap="tap" disabled></button>
<div disabled>
<button id="nested" on-tap="tap"></button>
</div>
</template>
<script>
class XDisabled extends Polymer.GestureEventListeners(Polymer.Element) {
constructor() {
super();
this.taps = [];
}
static get is() {return 'x-disabled-tap';}
tap(e) {
this.taps.push(e.id);
}
}
customElements.define(XDisabled.is, XDisabled);
</script>
</dom-module>
46 changes: 43 additions & 3 deletions test/unit/gestures.html
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
});
});
</script>
Expand Down

0 comments on commit 5c0f3e6

Please sign in to comment.