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':