diff --git a/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js b/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js index 81bbeadf32..cbe9bd985c 100644 --- a/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js @@ -502,6 +502,39 @@ describe('ReactDOMEventListener', () => { } }); + it('should delegate dialog events even without a direct listener', () => { + const container = document.createElement('div'); + const ref = React.createRef(); + const onCancel = jest.fn(); + const onClose = jest.fn(); + document.body.appendChild(container); + try { + ReactDOM.render( +
+ {/* Intentionally no handler on the target: */} + +
, + container, + ); + ref.current.dispatchEvent( + new Event('close', { + bubbles: false, + }), + ); + ref.current.dispatchEvent( + new Event('cancel', { + bubbles: false, + }), + ); + // Regression test: ensure React tree delegation still works + // even if the actual DOM element did not have a handler. + expect(onCancel).toHaveBeenCalledTimes(1); + expect(onClose).toHaveBeenCalledTimes(1); + } finally { + document.body.removeChild(container); + } + }); + it('should bubble non-native bubbling events', () => { const container = document.createElement('div'); const ref = React.createRef(); diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index bcfe629153..aac854afc1 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -96,6 +96,8 @@ import { TOP_ERROR, TOP_TOGGLE, TOP_INVALID, + TOP_CANCEL, + TOP_CLOSE, } from '../events/DOMTopLevelEventTypes'; let didWarnInvalidHydration = false; @@ -539,6 +541,11 @@ export function setInitialProperties( // TODO: Make sure that we check isMounted before firing any of these events. let props: Object; switch (tag) { + case 'dialog': + listenToNonDelegatedEvent(TOP_CANCEL, domElement); + listenToNonDelegatedEvent(TOP_CLOSE, domElement); + props = rawProps; + break; case 'iframe': case 'object': case 'embed': @@ -939,6 +946,10 @@ export function diffHydratedProperties( // TODO: Make sure that we check isMounted before firing any of these events. switch (tag) { + case 'dialog': + listenToNonDelegatedEvent(TOP_CANCEL, domElement); + listenToNonDelegatedEvent(TOP_CLOSE, domElement); + break; case 'iframe': case 'object': case 'embed':