From 6936c516c7cb267fc0c37f775d4a64ae4a694139 Mon Sep 17 00:00:00 2001 From: Wasuwat Limsuparhat <86233706+wsuwt@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:44:42 +0700 Subject: [PATCH] fix: tap event should not be triggered on mouse right-click (#981) --- .../core/__test__/events/TapEvent.test.js | 94 ++++++++++++++++++- packages/core/src/events/TapEvent.ts | 14 ++- 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/packages/core/__test__/events/TapEvent.test.js b/packages/core/__test__/events/TapEvent.test.js index 240b553419..4c8a6cf2b4 100644 --- a/packages/core/__test__/events/TapEvent.test.js +++ b/packages/core/__test__/events/TapEvent.test.js @@ -14,7 +14,7 @@ const getIdentifier = (element) => { return identifiers.get(element); }; -const createMouseEvent = (element, eventType) => { +const createMouseEvent = (element, eventType, button) => { const position = element.getBoundingClientRect(); return new MouseEvent(eventType, { @@ -26,7 +26,7 @@ const createMouseEvent = (element, eventType) => { screenY: position.top + element.offsetTop + position.height / 2, clientX: position.left + position.width / 2, clientY: position.top + position.height / 2, - button: 0, + button: button, buttons: 1, isTrusted: true, view: window @@ -56,8 +56,8 @@ const createTouchEvent = (element, eventType) => { }); }; -const dispatchMouseEvent = (element, eventType) => { - element.dispatchEvent(createMouseEvent(element, eventType)); +const dispatchMouseEvent = (element, eventType, button = 0) => { + element.dispatchEvent(createMouseEvent(element, eventType, button)); }; const dispatchTouchEvent = (element, eventType) => { @@ -71,6 +71,20 @@ const click = async (element1, element2) => { await nextFrame(); }; +const auxiliaryClick = async (element1, element2) => { + dispatchMouseEvent(element1, 'mousedown', 1); + await nextFrame(); + dispatchMouseEvent(element2, 'mouseup', 1); + await nextFrame(); +}; + +const secondaryClick = async (element1, element2) => { + dispatchMouseEvent(element1, 'mousedown', 2); + await nextFrame(); + dispatchMouseEvent(element2, 'mouseup', 2); + await nextFrame(); +}; + const touch = async (element1, element2) => { dispatchTouchEvent(element1, 'touchstart'); await nextFrame(); @@ -248,6 +262,78 @@ describe('TestTapEvent', function () { expect(tapCount).to.equal(1, 'tap event should be fired just once'); }); + it('Should not fire tap event on right-click', async function () { + const element = await fixture( + html`
` + ); + + await secondaryClick(element, element); + + expect(tapEvent).to.not.exist; + expect(tapEvent).to.not.instanceOf(Event); + expect(tapCount).to.equal(0, 'tap event should not be fired on right click'); + }); + + it('Should not fire tapstart event on right-click', async function () { + const element = await fixture( + html`
` + ); + + await secondaryClick(element, element); + + expect(tapStartEvent).to.not.exist; + expect(tapStartEvent).to.not.instanceOf(Event); + expect(tapStartCount).to.equal(0, 'tapstart event should not be fired on right click'); + }); + + it('Should not fire tapend event on right-click', async function () { + const element = await fixture( + html`
` + ); + + await secondaryClick(element, element); + + expect(tapEndEvent).to.not.exist; + expect(tapEndEvent).to.not.instanceOf(Event); + expect(tapEndCount).to.equal(0, 'tapend event should not be fired on right click'); + }); + + it('Should not fire tap event on middle-click', async function () { + const element = await fixture( + html`
` + ); + + await auxiliaryClick(element, element); + + expect(tapEvent).to.not.exist; + expect(tapEvent).to.not.instanceOf(Event); + expect(tapCount).to.equal(0, 'tap event should not be fired on right click'); + }); + + it('Should not fire tapstart event on middle-click', async function () { + const element = await fixture( + html`
` + ); + + await auxiliaryClick(element, element); + + expect(tapStartEvent).to.not.exist; + expect(tapStartEvent).to.not.instanceOf(Event); + expect(tapStartCount).to.equal(0, 'tapstart event should not be fired on right click'); + }); + + it('Should not fire tapend event on middle-click', async function () { + const element = await fixture( + html`
` + ); + + await auxiliaryClick(element, element); + + expect(tapEndEvent).to.not.exist; + expect(tapEndEvent).to.not.instanceOf(Event); + expect(tapEndCount).to.equal(0, 'tapend event should not be fired on right click'); + }); + it('Should support tap event on role=button when Enter is pressed', async function () { const el = await fixture(html`
Fake Button
`); const keyDownEvent = keyboardEvent('keydown', { key: 'Enter' }); diff --git a/packages/core/src/events/TapEvent.ts b/packages/core/src/events/TapEvent.ts index c774bb9f32..7faeb4a884 100644 --- a/packages/core/src/events/TapEvent.ts +++ b/packages/core/src/events/TapEvent.ts @@ -39,6 +39,8 @@ declare global { let positions: Positions; let metaKeys: MetaKeys | undefined; +const MAIN_BUTTON = 0; + /** * Simulates consistent click/tap events across pointer/touch devices */ @@ -219,12 +221,16 @@ const applyEvent = (target: Global): void => { /** * Listen to `mousedown` events on the target. - * Use this to fire tap events, unless one + * Use this to fire tapstart events, unless one * has already been triggered from a touch event. */ target.addEventListener( 'mousedown', (event) => { + if (event.button !== MAIN_BUTTON) { + return; + } + if (!lastTapTarget && event.target && currentTouch === -1) { mouseEventPath = [...event.composedPath()]; @@ -240,12 +246,16 @@ const applyEvent = (target: Global): void => { /** * Listen to `mouseup` events on the target. - * Use this to fire tap events, unless one + * Use this to fire tapend events, unless one * has already been triggered from a touch event. */ target.addEventListener( 'mouseup', (event) => { + if (event.button !== MAIN_BUTTON) { + return; + } + if (lastTapTarget) { /** * Tap events have been dispatched,