From 82ce4f9f6a77c143de33b0774619827d7c98cbd7 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Sun, 18 Feb 2024 18:04:31 +0100 Subject: [PATCH] Add support of onBeforeToggle and onToggle --- .../src/client/ReactDOMComponent.js | 12 +++++ .../src/events/DOMEventNames.js | 1 + .../src/events/DOMEventProperties.js | 1 + .../src/events/DOMPluginEventSystem.js | 1 + .../src/events/ReactDOMEventListener.js | 1 + .../ReactDOMEventPropagation-test.js | 46 +++++++++++++++++-- .../src/test-utils/ReactTestUtils.js | 1 + 7 files changed, 58 insertions(+), 5 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactDOMComponent.js b/packages/react-dom-bindings/src/client/ReactDOMComponent.js index a94c990733915..421ddaf29766f 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMComponent.js +++ b/packages/react-dom-bindings/src/client/ReactDOMComponent.js @@ -792,6 +792,11 @@ function setProp( } break; } + case 'popover': + listenToNonDelegatedEvent('beforetoggle', domElement); + listenToNonDelegatedEvent('toggle', domElement); + setValueForAttribute(domElement, 'popover', value); + break; case 'xlinkActuate': setValueForNamespacedAttribute( domElement, @@ -2827,6 +2832,13 @@ export function diffHydratedProperties( } } + if (props.popover != null) { + // We listen to this event in case to ensure emulated bubble + // listeners still fire for the toggle event. + listenToNonDelegatedEvent('beforetoggle', domElement); + listenToNonDelegatedEvent('toggle', domElement); + } + if (props.onScroll != null) { listenToNonDelegatedEvent('scroll', domElement); } diff --git a/packages/react-dom-bindings/src/events/DOMEventNames.js b/packages/react-dom-bindings/src/events/DOMEventNames.js index 9145e316f9d1f..b9d298cd78ab1 100644 --- a/packages/react-dom-bindings/src/events/DOMEventNames.js +++ b/packages/react-dom-bindings/src/events/DOMEventNames.js @@ -18,6 +18,7 @@ export type DOMEventName = // 'animationstart' | | 'beforeblur' // Not a real event. This is used by event experiments. | 'beforeinput' + | 'beforetoggle' | 'blur' | 'canplay' | 'canplaythrough' diff --git a/packages/react-dom-bindings/src/events/DOMEventProperties.js b/packages/react-dom-bindings/src/events/DOMEventProperties.js index 55b3c4f987186..591489ad25b26 100644 --- a/packages/react-dom-bindings/src/events/DOMEventProperties.js +++ b/packages/react-dom-bindings/src/events/DOMEventProperties.js @@ -34,6 +34,7 @@ export const topLevelEventsToReactNames: Map = const simpleEventPluginEvents = [ 'abort', 'auxClick', + 'beforeToggle', 'cancel', 'canPlay', 'canPlayThrough', diff --git a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js index ca89e1d1bd8ab..87907448125f7 100644 --- a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js +++ b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js @@ -220,6 +220,7 @@ export const mediaEventTypes: Array = [ // set them on the actual target element itself. This is primarily // because these events do not consistently bubble in the DOM. export const nonDelegatedEvents: Set = new Set([ + 'beforetoggle', 'cancel', 'close', 'invalid', diff --git a/packages/react-dom-bindings/src/events/ReactDOMEventListener.js b/packages/react-dom-bindings/src/events/ReactDOMEventListener.js index 237cde231176b..4ec8d3788f00e 100644 --- a/packages/react-dom-bindings/src/events/ReactDOMEventListener.js +++ b/packages/react-dom-bindings/src/events/ReactDOMEventListener.js @@ -345,6 +345,7 @@ export function getEventPriority(domEventName: DOMEventName): EventPriority { case 'select': case 'selectstart': return DiscreteEventPriority; + case 'beforetoggle': case 'drag': case 'dragenter': case 'dragexit': diff --git a/packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js b/packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js index eb093d389b629..e3db724afbe24 100644 --- a/packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js @@ -1207,6 +1207,40 @@ describe('ReactDOMEventListener', () => { }); }); + it('onBeforeToggle Popover API', () => { + testEmulatedBubblingEvent({ + type: 'div', + targetProps: {popover: 'any'}, + reactEvent: 'onBeforeToggle', + reactEventType: 'beforetoggle', + nativeEvent: 'beforetoggle', + dispatch(node) { + const e = new Event('beforetoggle', { + bubbles: false, + cancelable: true, + }); + node.dispatchEvent(e); + }, + }); + }); + + it('onToggle Popover API', () => { + testEmulatedBubblingEvent({ + type: 'div', + targetProps: {popover: 'any'}, + reactEvent: 'onToggle', + reactEventType: 'toggle', + nativeEvent: 'toggle', + dispatch(node) { + const e = new Event('toggle', { + bubbles: false, + cancelable: true, + }); + node.dispatchEvent(e); + }, + }); + }); + it('onVolumeChange', () => { testEmulatedBubblingEvent({ type: 'video', @@ -1908,6 +1942,7 @@ describe('ReactDOMEventListener', () => { type={eventConfig.type} targetRef={targetRef} targetProps={{ + ...eventConfig.targetProps, [eventConfig.reactEvent]: e => { log.push('---- inner'); }, @@ -2074,11 +2109,10 @@ describe('ReactDOMEventListener', () => { { log.push('--- inner parent'); @@ -2307,6 +2341,7 @@ describe('ReactDOMEventListener', () => { type={eventConfig.type} targetRef={targetRef} targetProps={{ + ...eventConfig.targetProps, [eventConfig.reactEvent]: e => { e.stopPropagation(); // <--------- log.push('---- inner'); @@ -2642,6 +2677,7 @@ describe('ReactDOMEventListener', () => { } }} targetProps={{ + ...eventConfig.targetProps, [eventConfig.reactEvent]: e => { log.push('---- inner'); }, diff --git a/packages/react-dom/src/test-utils/ReactTestUtils.js b/packages/react-dom/src/test-utils/ReactTestUtils.js index 568e8327198f1..b21d890571b1b 100644 --- a/packages/react-dom/src/test-utils/ReactTestUtils.js +++ b/packages/react-dom/src/test-utils/ReactTestUtils.js @@ -625,6 +625,7 @@ function makeSimulator(eventType) { // A one-time snapshot with no plans to update. We'll probably want to deprecate Simulate API. const simulatedEventTypes = [ + 'beforeToggle', 'blur', 'cancel', 'click',