fix(button): redispatch click event with all original properties

This commit is contained in:
Mikhail Bashkirov 2019-07-12 14:31:41 +02:00
parent 4a8c6ebbed
commit b71177f667
2 changed files with 81 additions and 11 deletions

View file

@ -152,9 +152,14 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
this.__teardownDelegation();
}
__clickDelegationHandler(e) {
e.stopPropagation(); // prevent click on the fake element and cause click on the native button
this.$$slot('_button').click();
/**
* Prevent click on the fake element and cause click on the native button.
*/
__clickDelegationHandler(oldEvent) {
oldEvent.stopPropagation();
// replacing `MouseEvent` with `oldEvent.constructor` breaks IE
const newEvent = new MouseEvent(oldEvent.type, oldEvent);
this.$$slot('_button').dispatchEvent(newEvent);
}
__setupDelegation() {
@ -180,7 +185,7 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
if (e.keyCode === 32 /* space */ || e.keyCode === 13 /* enter */) {
e.preventDefault();
this.shadowRoot.querySelector('.btn').removeAttribute('active');
this.$$slot('_button').click();
this.shadowRoot.querySelector('.click-area').click();
}
}

View file

@ -1,9 +1,20 @@
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
import { expect, fixture, html, aTimeout, oneEvent } from '@open-wc/testing';
import sinon from 'sinon';
import { pressEnter, pressSpace } from '@polymer/iron-test-helpers/mock-interactions.js';
import {
makeMouseEvent,
pressEnter,
pressSpace,
} from '@polymer/iron-test-helpers/mock-interactions.js';
import '../lion-button.js';
function getTopElement(el) {
const { left, top } = el.getBoundingClientRect();
// to support elementFromPoint() in polyfilled browsers we have to use document
const crossBrowserRoot = el.shadowRoot.elementFromPoint ? el.shadowRoot : document;
return crossBrowserRoot.elementFromPoint(left, top);
}
describe('lion-button', () => {
it('behaves like native `button` in terms of a11y', async () => {
const el = await fixture(`<lion-button>foo</lion-button>`);
@ -99,11 +110,7 @@ describe('lion-button', () => {
`);
const button = form.querySelector('lion-button');
const { left, top } = button.getBoundingClientRect();
// to support elementFromPoint() in polyfilled browsers we have to use document
const crossBrowserRoot = button.shadowRoot.elementFromPoint ? button.shadowRoot : document;
const shadowClickAreaElement = crossBrowserRoot.elementFromPoint(left, top);
shadowClickAreaElement.click();
getTopElement(button).click();
expect(formSubmitSpy.called).to.be.true;
});
@ -138,4 +145,62 @@ describe('lion-button', () => {
expect(formSubmitSpy.called).to.be.true;
});
});
describe('click event', () => {
it('is fired once', async () => {
const clickSpy = sinon.spy();
const el = await fixture(
html`
<lion-button @click="${clickSpy}"></lion-button>
`,
);
getTopElement(el).click();
// trying to wait for other possible redispatched events
await aTimeout();
await aTimeout();
expect(clickSpy.callCount).to.equal(1);
});
describe('event after redispatching', async () => {
async function prepareClickEvent(el, host) {
setTimeout(() => {
if (host) {
// click on host like in native button
makeMouseEvent('click', { x: 11, y: 11 }, el);
} else {
// click on click-area which is then redispatched
makeMouseEvent('click', { x: 11, y: 11 }, getTopElement(el));
}
});
return oneEvent(el, 'click');
}
let hostEvent;
let redispatchedEvent;
before(async () => {
const el = await fixture('<lion-button></lion-button>');
hostEvent = await prepareClickEvent(el, true);
redispatchedEvent = await prepareClickEvent(el, false);
});
const sameProperties = [
'constructor',
'composed',
'bubbles',
'cancelable',
'clientX',
'clientY',
];
sameProperties.forEach(property => {
it(`has same value of the property "${property}"`, async () => {
expect(redispatchedEvent[property]).to.equal(hostEvent[property]);
});
});
});
});
});