From 29d9d3184484b03cb0369e0494617207df777b7a Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Thu, 27 Aug 2026 00:45:29 +0500 Subject: [PATCH] [DOM] Copy `source` onto the synthetic toggle event (#37389) `ToggleEvent` carries a `source` property pointing at the control that opened or closed a popover, but `ToggleEventInterface` only lists `newState` and `oldState`, so the synthetic event never copies it. An `onToggle` handler reads `event.source` as `undefined` even when the native event has it. Adding `source` to the interface copies it off the native event the same way `newState` and `oldState` are copied. --- .../src/events/SyntheticEvent.js | 1 + .../__tests__/SimpleEventPlugin-test.js | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/packages/react-dom-bindings/src/events/SyntheticEvent.js b/packages/react-dom-bindings/src/events/SyntheticEvent.js index 1fa5dafba11b..6e7fd585e7c9 100644 --- a/packages/react-dom-bindings/src/events/SyntheticEvent.js +++ b/packages/react-dom-bindings/src/events/SyntheticEvent.js @@ -609,6 +609,7 @@ const ToggleEventInterface: EventInterfaceType = { ...EventInterface, newState: 0, oldState: 0, + source: 0, }; export const SyntheticToggleEvent: $FlowFixMe = createSyntheticEvent(ToggleEventInterface); diff --git a/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js b/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js index f14fcab168b9..eb8532e22de8 100644 --- a/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js +++ b/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js @@ -15,6 +15,7 @@ class ToggleEvent extends Event { super(type, eventInit); this.newState = eventInit.newState; this.oldState = eventInit.oldState; + this.source = eventInit.source; } } @@ -623,4 +624,39 @@ describe('SimpleEventPlugin', function () { const event = onSubmit.mock.calls[0][0]; expect(event.submitter).toBe(submitter); }); + + it('includes the source in toggle events', async function () { + container = document.createElement('div'); + + const onToggle = jest.fn(); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( + <> + +
+ popover content +
+ , + ); + }); + + const source = container.querySelector('button'); + const target = container.querySelector('#popover'); + await act(() => { + target.dispatchEvent( + new ToggleEvent('toggle', { + bubbles: false, + cancelable: true, + oldState: 'closed', + newState: 'open', + source: source, + }), + ); + }); + + expect(onToggle).toHaveBeenCalledTimes(1); + const event = onToggle.mock.calls[0][0]; + expect(event.source).toBe(source); + }); });