Skip to content

Commit

Permalink
Merge pull request #5232 from Polymer/3.x-tap-fix-disabled
Browse files Browse the repository at this point in the history
Port disabled fixes from 2.x
  • Loading branch information
dfreedm authored May 15, 2018
2 parents dfc8ea3 + 44e725b commit 041e31b
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 46 deletions.
16 changes: 15 additions & 1 deletion lib/utils/gestures.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@ const labellable = {
'select': true
};

// Defined at https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute
/** @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 @@ -1051,7 +1065,7 @@ register({
let dy = Math.abs(e.clientY - this.info.y);
// find original target from `preventer` for TouchEvents, or `e` for MouseEvents
let t = _findOriginalTarget((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`
Expand Down
80 changes: 73 additions & 7 deletions test/unit/gestures-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Polymer } from '../../lib/legacy/polymer-fn.js';
import { html } from '../../lib/utils/html-tag.js';
import { PolymerElement } from '../../polymer-element.js';
import { GestureEventListeners } from '../../lib/mixins/gesture-event-listeners.js';
import { addListener } from '../../lib/utils/gestures.js';
Polymer({
_template: html`
<style>
Expand Down Expand Up @@ -196,23 +197,88 @@ class XNativeLabelNested extends PolymerElement {
}
}
customElements.define(XNativeLabelNested.is, XNativeLabelNested);
class XDisabled extends GestureEventListeners(PolymerElement) {

class XDisabled extends PolymerElement {
static get is() {
return 'x-disabled';
}
static get properties() {
return {
disabled: {
type: Boolean,
reflectToAttribute: true
}
};
}
constructor() {
super();
this.disabled = true;
}
}
customElements.define(XDisabled.is, XDisabled);

class XDisabledTap extends GestureEventListeners(PolymerElement) {
constructor() {
super();
this.taps = [];
}
static get template() {
return html`
<button id="disabled" on-tap="tap" disabled=""></button>
<div disabled="">
<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>`;
}
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);


class AllDisabled extends GestureEventListeners(PolymerElement) {
static get is() {
return 'all-disabled';
}
static get template() {
return html`
<button></button>
<!-- MDN lists as obsolete -->
<!-- <command></command> -->
<fieldset></fieldset>
<input>
<!-- MDN lists as obsolete -->
<!-- <keygen> -->
<select>
<optgroup>
<option></option>
</optgroup>
</select>
<textarea></textarea>`;
}
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', '');
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);
143 changes: 105 additions & 38 deletions test/unit/gestures.html
Original file line number Diff line number Diff line change
Expand Up @@ -619,44 +619,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() {
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']);

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);
});
});
});
</script>
Expand Down

0 comments on commit 041e31b

Please sign in to comment.