Skip to content

Commit

Permalink
Only disable tabs for "disablable" elements
Browse files Browse the repository at this point in the history
Fixes #5190
  • Loading branch information
dfreedm committed May 10, 2018
1 parent 5f5d2c2 commit 2bca0ee
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 43 deletions.
15 changes: 14 additions & 1 deletion lib/utils/gestures.html
Expand Up @@ -113,6 +113,19 @@
'select': true
};

/** @type {!Object<boolean>} */
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
Expand Down Expand Up @@ -1058,7 +1071,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[t.localName] && t.disabled)) {
return;
}
// dx,dy can be NaN if `click` has been simulated and there was no `down` for `start`
Expand Down
76 changes: 72 additions & 4 deletions test/unit/gestures-elements.html
Expand Up @@ -244,24 +244,92 @@
</script>
</dom-module>

<script>
class XDisabled extends Polymer.Element {
static get is() {
return 'x-disabled';
}
static get properties() {
return {
disabled: Boolean
};
}
constructor() {
super();
this.disabled = true;
}
}
customElements.define(XDisabled.is, XDisabled);
</script>

<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>
<x-disabled id="disabledEl" on-tap="tap"></x-disabled>
</template>
<script>
class XDisabledTap extends Polymer.GestureEventListeners(Polymer.Element) {
constructor() {
super();
this.taps = [];
}
static get is() {
return 'x-disabled-tap';
}
tap(e) {
const target = e.target;
this.taps.push(`${target.localName}${target.id ? '#' + target.id : ''}`);
}
}
customElements.define(XDisabledTap.is, XDisabledTap);
</script>
</dom-module>

<dom-module id="all-disabled">
<template>
<button></button>
<!-- MDN lists as obsolete -->
<!-- <command></command> -->
<fieldset></fieldset>
<input>
<!-- MDN lists as obsolete -->
<!-- <keygen> -->
<select>
<optgroup>
<option></option>
</optgroup>
</select>
<textarea></textarea>
<div></div>
</template>
<script>
class XDisabled extends Polymer.GestureEventListeners(Polymer.Element) {
class AllDisabled extends Polymer.GestureEventListeners(Polymer.Element) {
static get is() {
return 'all-disabled';
}
constructor() {
super();
this.taps = [];
}
static get is() {return 'x-disabled-tap';}
tap(e) {
this.taps.push(e.id);
this.taps.push(e.target.localName);
}
ready() {
super.ready();
this.shadowRoot.querySelectorAll('*').forEach((el) => {
el.setAttribute('disabled', '');
Polymer.Gestures.addListener(el, 'tap', (e) => this.tap(e));
});
}
tapAll() {
this.shadowRoot.querySelectorAll('*').forEach((el) => {
el.click();
});
}
}
customElements.define(XDisabled.is, XDisabled);
customElements.define(AllDisabled.is, AllDisabled);
</script>
</dom-module>
116 changes: 78 additions & 38 deletions test/unit/gestures.html
Expand Up @@ -615,44 +615,84 @@
});
});

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() {
test('click() function works as expected on disabled elements', function() {
Polymer.Gestures.resetMouseCanceller();
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() {
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']);

// 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 "disablable" elements', function() {
const el = document.createElement('all-disabled');
document.body.appendChild(el);
el.tapAll();
assert.deepEqual(el.taps, ['div']);
document.body.removeChild(el);
})
});
});
</script>
Expand Down

0 comments on commit 2bca0ee

Please sign in to comment.