diff --git a/packages/react-dom/src/client/ReactDOMClientInjection.js b/packages/react-dom/src/client/ReactDOMClientInjection.js index cb4d2315f4cd2..49e10b3ed11e2 100644 --- a/packages/react-dom/src/client/ReactDOMClientInjection.js +++ b/packages/react-dom/src/client/ReactDOMClientInjection.js @@ -9,6 +9,7 @@ import * as EventPluginHub from 'events/EventPluginHub'; import * as EventPluginUtils from 'events/EventPluginUtils'; import * as ReactDOMComponentTree from './ReactDOMComponentTree'; +import AuxClickEventPlugin from '../events/AuxClickEventPlugin'; import BeforeInputEventPlugin from '../events/BeforeInputEventPlugin'; import ChangeEventPlugin from '../events/ChangeEventPlugin'; import DOMEventPluginOrder from '../events/DOMEventPluginOrder'; @@ -32,6 +33,7 @@ EventPluginUtils.injection.injectComponentTree(ReactDOMComponentTree); */ EventPluginHub.injection.injectEventPluginsByName({ SimpleEventPlugin: SimpleEventPlugin, + AuxClickEventPlugin: AuxClickEventPlugin, EnterLeaveEventPlugin: EnterLeaveEventPlugin, ChangeEventPlugin: ChangeEventPlugin, SelectEventPlugin: SelectEventPlugin, diff --git a/packages/react-dom/src/events/AuxClickEventPlugin.js b/packages/react-dom/src/events/AuxClickEventPlugin.js new file mode 100644 index 0000000000000..5fcd19f111067 --- /dev/null +++ b/packages/react-dom/src/events/AuxClickEventPlugin.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import {accumulateTwoPhaseDispatches} from 'events/EventPropagators'; + +import SyntheticMouseEvent from './SyntheticMouseEvent'; + +const eventTypes = { + auxClick: { + phasedRegistrationNames: { + bubbled: 'onAuxClick', + captured: 'onAuxClickCapture', + }, + dependencies: ['topAuxClick', 'topClick'], + }, +}; + +const AuxClickEventPlugin = { + eventTypes, + + extractEvents( + topLevelType: mixed, + targetInst: mixed, + nativeEvent: MouseEvent, + nativeEventTarget: EventTarget, + ) { + if (topLevelType === 'topClick' && nativeEvent.button === 0) { + return null; + } + + let event = SyntheticMouseEvent.getPooled( + eventTypes.auxClick, + targetInst, + nativeEvent, + nativeEventTarget, + ); + event.type = 'auxclick'; + + accumulateTwoPhaseDispatches(event); + return event; + }, +}; + +export default AuxClickEventPlugin; diff --git a/packages/react-dom/src/events/BrowserEventConstants.js b/packages/react-dom/src/events/BrowserEventConstants.js index 276df6ee76f22..179b5a7efbe40 100644 --- a/packages/react-dom/src/events/BrowserEventConstants.js +++ b/packages/react-dom/src/events/BrowserEventConstants.js @@ -21,6 +21,7 @@ var topLevelTypes = { getVendorPrefixedEventName('animationiteration') || 'animationiteration', topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart', + topAuxClick: 'auxclick', topBlur: 'blur', topCancel: 'cancel', topCanPlay: 'canplay', diff --git a/packages/react-dom/src/events/DOMEventPluginOrder.js b/packages/react-dom/src/events/DOMEventPluginOrder.js index bc7052f324643..e7583a2ce896f 100644 --- a/packages/react-dom/src/events/DOMEventPluginOrder.js +++ b/packages/react-dom/src/events/DOMEventPluginOrder.js @@ -17,6 +17,7 @@ var DOMEventPluginOrder = [ 'ResponderEventPlugin', 'SimpleEventPlugin', + 'AuxClickEventPlugin', 'TapEventPlugin', 'EnterLeaveEventPlugin', 'ChangeEventPlugin', diff --git a/packages/react-dom/src/events/__tests__/AuxClickEventPlugin-test.js b/packages/react-dom/src/events/__tests__/AuxClickEventPlugin-test.js new file mode 100644 index 0000000000000..efe040ae70c5f --- /dev/null +++ b/packages/react-dom/src/events/__tests__/AuxClickEventPlugin-test.js @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react-core + */ + +'use strict'; + +var React; +var ReactDOM; + +describe('AuxClickEventPlugin', () => { + var container; + + beforeEach(() => { + jest.resetModules(); + + React = require('react'); + ReactDOM = require('react-dom'); + + // The container has to be attached for events to fire. + container = document.createElement('div'); + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + container = null; + }); + + it('should not fire auxclick on primary mouse button click', () => { + let cb = jest.fn(); + let node = ReactDOM.render(, container); + + node.dispatchEvent( + new MouseEvent('click', { + bubbles: true, + cancelable: true, + button: 0, + }), + ); + + expect(cb).not.toBeCalled(); + }); + + it('should fire auxclick on secondary mouse button click', () => { + let cb = jest.fn(); + let node = ReactDOM.render(, container); + + node.dispatchEvent( + new MouseEvent('click', { + bubbles: true, + cancelable: true, + button: 1, + }), + ); + + expect(cb).toBeCalled(); + }); + + it('should respond to native auxclick', () => { + let cb = jest.fn(); + let node = ReactDOM.render(, container); + + node.dispatchEvent( + new MouseEvent('auxclick', { + bubbles: true, + cancelable: true, + button: 1, + }), + ); + + expect(cb).toBeCalled(); + }); +});