diff --git a/packages/button/src/LionButton.js b/packages/button/src/LionButton.js index af4b6ca23..349d4e69d 100644 --- a/packages/button/src/LionButton.js +++ b/packages/button/src/LionButton.js @@ -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(); } } diff --git a/packages/button/test/lion-button.test.js b/packages/button/test/lion-button.test.js index 76eaca804..44c76af5c 100644 --- a/packages/button/test/lion-button.test.js +++ b/packages/button/test/lion-button.test.js @@ -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(`foo`); @@ -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` + + `, + ); + + 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(''); + 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]); + }); + }); + }); + }); });