Skip to content

Commit b71177f

Browse files
author
Mikhail Bashkirov
committed
fix(button): redispatch click event with all original properties
1 parent 4a8c6eb commit b71177f

File tree

2 files changed

+81
-11
lines changed

2 files changed

+81
-11
lines changed

packages/button/src/LionButton.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,14 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
152152
this.__teardownDelegation();
153153
}
154154

155-
__clickDelegationHandler(e) {
156-
e.stopPropagation(); // prevent click on the fake element and cause click on the native button
157-
this.$$slot('_button').click();
155+
/**
156+
* Prevent click on the fake element and cause click on the native button.
157+
*/
158+
__clickDelegationHandler(oldEvent) {
159+
oldEvent.stopPropagation();
160+
// replacing `MouseEvent` with `oldEvent.constructor` breaks IE
161+
const newEvent = new MouseEvent(oldEvent.type, oldEvent);
162+
this.$$slot('_button').dispatchEvent(newEvent);
158163
}
159164

160165
__setupDelegation() {
@@ -180,7 +185,7 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
180185
if (e.keyCode === 32 /* space */ || e.keyCode === 13 /* enter */) {
181186
e.preventDefault();
182187
this.shadowRoot.querySelector('.btn').removeAttribute('active');
183-
this.$$slot('_button').click();
188+
this.shadowRoot.querySelector('.click-area').click();
184189
}
185190
}
186191

packages/button/test/lion-button.test.js

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
1+
import { expect, fixture, html, aTimeout, oneEvent } from '@open-wc/testing';
22
import sinon from 'sinon';
3-
import { pressEnter, pressSpace } from '@polymer/iron-test-helpers/mock-interactions.js';
3+
import {
4+
makeMouseEvent,
5+
pressEnter,
6+
pressSpace,
7+
} from '@polymer/iron-test-helpers/mock-interactions.js';
48

59
import '../lion-button.js';
610

11+
function getTopElement(el) {
12+
const { left, top } = el.getBoundingClientRect();
13+
// to support elementFromPoint() in polyfilled browsers we have to use document
14+
const crossBrowserRoot = el.shadowRoot.elementFromPoint ? el.shadowRoot : document;
15+
return crossBrowserRoot.elementFromPoint(left, top);
16+
}
17+
718
describe('lion-button', () => {
819
it('behaves like native `button` in terms of a11y', async () => {
920
const el = await fixture(`<lion-button>foo</lion-button>`);
@@ -99,11 +110,7 @@ describe('lion-button', () => {
99110
`);
100111

101112
const button = form.querySelector('lion-button');
102-
const { left, top } = button.getBoundingClientRect();
103-
// to support elementFromPoint() in polyfilled browsers we have to use document
104-
const crossBrowserRoot = button.shadowRoot.elementFromPoint ? button.shadowRoot : document;
105-
const shadowClickAreaElement = crossBrowserRoot.elementFromPoint(left, top);
106-
shadowClickAreaElement.click();
113+
getTopElement(button).click();
107114

108115
expect(formSubmitSpy.called).to.be.true;
109116
});
@@ -138,4 +145,62 @@ describe('lion-button', () => {
138145
expect(formSubmitSpy.called).to.be.true;
139146
});
140147
});
148+
149+
describe('click event', () => {
150+
it('is fired once', async () => {
151+
const clickSpy = sinon.spy();
152+
const el = await fixture(
153+
html`
154+
<lion-button @click="${clickSpy}"></lion-button>
155+
`,
156+
);
157+
158+
getTopElement(el).click();
159+
160+
// trying to wait for other possible redispatched events
161+
await aTimeout();
162+
await aTimeout();
163+
164+
expect(clickSpy.callCount).to.equal(1);
165+
});
166+
167+
describe('event after redispatching', async () => {
168+
async function prepareClickEvent(el, host) {
169+
setTimeout(() => {
170+
if (host) {
171+
// click on host like in native button
172+
makeMouseEvent('click', { x: 11, y: 11 }, el);
173+
} else {
174+
// click on click-area which is then redispatched
175+
makeMouseEvent('click', { x: 11, y: 11 }, getTopElement(el));
176+
}
177+
});
178+
return oneEvent(el, 'click');
179+
}
180+
181+
let hostEvent;
182+
let redispatchedEvent;
183+
184+
before(async () => {
185+
const el = await fixture('<lion-button></lion-button>');
186+
hostEvent = await prepareClickEvent(el, true);
187+
redispatchedEvent = await prepareClickEvent(el, false);
188+
});
189+
190+
const sameProperties = [
191+
'constructor',
192+
'composed',
193+
'bubbles',
194+
'cancelable',
195+
'clientX',
196+
'clientY',
197+
];
198+
199+
sameProperties.forEach(property => {
200+
it(`has same value of the property "${property}"`, async () => {
201+
expect(redispatchedEvent[property]).to.equal(hostEvent[property]);
202+
});
203+
});
204+
});
205+
});
141206
});

0 commit comments

Comments
 (0)