Repository: mui/material-ui (TypeScript)
Date: 2024-04-13 20:58:51+00:00
Task type: Bug Fix

Issue:
[material-ui][Dialog] Specifying onClick prop disables backdrop clicks
### Steps to reproduce

[Link to live example](https://codesandbox.io/p/sandbox/smoosh-cache-fdm94z?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522cltk5d0af00073b6k7gkkf9za%2522%252C%2522sizes%2522%253A%255B70%252C30%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522cltk5d0af00033b6kdkj6r1dx%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522cltk5d0af00043b6kuje2wgrt%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522cltk5d0af00063b6krcz40a7h%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522cltk5d0af00033b6kdkj6r1dx%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cltk5d0af00023b6ka2qetber%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fsrc%252FDemo.tsx%2522%252C%2522state%2522%253A%2522IDLE%2522%252C%2522initialSelections%2522%253A%255B%257B%2522startLineNumber%2522%253A94%252C%2522startColumn%2522%253A30%252C%2522endLineNumber%2522%253A94%252C%2522endColumn%2522%253A30%257D%255D%257D%255D%252C%2522id%2522%253A%2522cltk5d0af00033b6kdkj6r1dx%2522%252C%2522activeTabId%2522%253A%2522cltk5d0af00023b6ka2qetber%2522%257D%252C%2522cltk5d0af00063b6krcz40a7h%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cltk5d0af00053b6ky9q0ld8x%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A0%252C%2522path%2522%253A%2522%252F%2522%257D%255D%252C%2522id%2522%253A%2522cltk5d0af00063b6krcz40a7h%2522%252C%2522activeTabId%2522%253A%2522cltk5d0af00053b6ky9q0ld8x%2522%257D%252C%2522cltk5d0af00043b6kuje2wgrt%2522%253A%257B%2522tabs%2522%253A%255B%255D%252C%2522id%2522%253A%2522cltk5d0af00043b6kuje2wgrt%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D)

Steps:
1. [done in code] Specify the `onClick` prop to the `Dialog` component.
2. Click the "Open Simple Dialog" button to open the dialog.
3. Try to click on the backdrop to close the dialog (nothing happens).


### Current behavior

The dialog does not close upon clicking the backdrop if the `onClick` prop is provided by the consumer of the dialog. 

### Expected behavior

The dialog should close when the backdrop is clicked, even if an `onClick` prop is provided to the dialog component.

### Context

* The `onClick` prop can been used to stop propagation to parent components, as mentioned in [this older issue](https://github.com/mui/material-ui/issues/29060).
  * While the main point of that issue talks about the click event propagating to the parent (which I believe is working as intended), it also mentions that specifying the `onClick` callback also "... breaks the behavior of the 'backdrop'". I want to make sure this gets addressed.
* The workaround @kykungz mentioned [here](https://github.com/mui/material-ui/issues/29060#issuecomment-943617752) worked to bring back the backdrop behavior while using the `onClick` prop for v5.
* When looking at the Dialog source code, I noticed a few things:
  * The component handles the backdrop functionality through the `onClick` callback [here](https://github.com/mui/material-ui/blob/master/packages/mui-material/src/Dialog/Dialog.js#L251).
  * If the consumer also specifies an `onClick` prop, it gets overwritten by spreading `{...others}` [here](https://github.com/mui/material-ui/blob/master/packages/mui-material/src/Dialog/Dialog.js#L253).
  * My proposed fix for this would be to:
    * Destruct `onClick` from `props`.
    * Invoke it from within `handleBackdropClick` [here](https://github.com/mui/material-ui/blob/master/packages/mui-material/src/Dialog/Dialog.js#L213) (if provided in `props`) instead of grouping it with `...others`.

### Your environment

<details>
  <summary><code>npx @mui/envinfo</code></summary>

```
  System:
    OS: macOS 14.2.1
  Browsers:
    Chrome: 122.0.6261.112
    Edge: Not Found
    Safari: 17.2.1
  npmPackages:
    @emotion/react: ^11.11.4 => 11.11.4 
    @emotion/styled: ^11.11.0 => 11.11.0 
    @mui/base:  5.0.0-beta.37 
    @mui/core-downloads-tracker:  5.15.11 
    @mui/icons-material: ^5.15.11 => 5.15.11 
    @mui/lab: ^5.0.0-alpha.166 => 5.0.0-alpha.166 
    @mui/material: ^5.15.11 => 5.15.11 
    @mui/private-theming:  5.15.11 
    @mui/styled-engine:  5.15.11 
    @mui/system:  5.15.11 
    @mui/types:  7.2.13 
    @mui/utils:  5.15.11 
    @mui/x-date-pickers: ^6.19.6 => 6.19.6 
    @types/react: ^18.2.61 => 18.2.61 
    react: ^18.2.0 => 18.2.0 
    react-dom: ^18.2.0 => 18.2.0 
    typescript: 4.9.5 => 4.9.5
```
</details>


**Search keywords**: dialog onClick backdrop


Issue discussion:
I agree it's a bug.
> Invoke it from within `handleBackdropClick` [here](https://github.com/mui/material-ui/blob/master/packages/mui-material/src/Dialog/Dialog.js#L213) (if provided in props) instead of grouping it with `...others`.

Where to call `handleBackdropClick` then?

Would the following fix work:
```diff
--- a/packages/mui-material/src/Dialog/Dialog.js
+++ b/packages/mui-material/src/Dialog/Dialog.js
@@ -182,6 +182,7 @@ const Dialog = React.forwardRef(function Dialog(inProps, ref) {
     fullWidth = false,
     maxWidth = 'sm',
     onBackdropClick,
+    onClick,
     onClose,
     open,
     PaperComponent = Paper,
@@ -211,6 +212,9 @@ const Dialog = React.forwardRef(function Dialog(inProps, ref) {
     backdropClick.current = event.target === event.currentTarget;
   };
   const handleBackdropClick = (event) => {
+    if (onClick) {
+      onClick(event);
+    }
     // Ignore the events not coming from the "backdrop".
     if (!backdropClick.current) {
       return;
```
However, I believe there should be a distinction between clicking the dialog root and clicking the backdrop.
I believe so! That's pretty much what I was thinking. You don't even need to reorder `onClick={handleBackdropClick}` since the `onClick` prop won't be part of `others` anymore.

> there should be a distinction between clicking the dialog root and clicking the backdrop.

I agree and if I understand the docs correctly, that's the use case that (now deprecated) `onBackdropClick` and `onClose (with reason)` handle.
> I believe so! That's pretty much what I was thinking. You don't even need to reorder `onClick={handleBackdropClick}` since the `onClick` prop won't be part of `others` anymore.

Correct. Updated the above diff.

> 
> > there should be a distinction between clicking the dialog root and clicking the backdrop.
> 
> I agree and if I understand the docs correctly, that's the use case that (now deprecated) `onBackdropClick` and `onClose (with reason)` handle.

I mean why is the dialog root's `onClick` handling the backdrop's `click` event?


Ah I see what you're saying. Good question. Not sure of the design decision there. Found the [commit](https://github.com/mui/material-ui/commit/ea3e66f2ca1487ecaf6ab9c6d085ced891fddd6e) that put it there though.

Would an alternative be to wire this up through the `Backdrop` `onClick` prop? I haven't explored this component much yet.
> > I believe so! That's pretty much what I was thinking. You don't even need to reorder `onClick={handleBackdropClick}` since the `onClick` prop won't be part of `others` anymore.
> 
> Correct. Updated the above diff.
> 
> > > there should be a distinction between clicking the dialog root and clicking the backdrop.
> > 
> > 
> > I agree and if I understand the docs correctly, that's the use case that (now deprecated) `onBackdropClick` and `onClose (with reason)` handle.
> 
> I mean why is the dialog root's `onClick` handling the backdrop's `click` event?

DialogRoot is a Styled Modal and the underlying Modal component has the Pop Up(Modal) and Backdrop component with it, By passing onClick to DialogRoot eventually to Backdrop, callback is fired back when Backdrop is clicked to close it.

Solution patch:
diff --git a/packages/mui-material/src/Dialog/Dialog.js b/packages/mui-material/src/Dialog/Dialog.js
--- a/packages/mui-material/src/Dialog/Dialog.js
+++ b/packages/mui-material/src/Dialog/Dialog.js
@@ -182,6 +182,7 @@ const Dialog = React.forwardRef(function Dialog(inProps, ref) {
     fullWidth = false,
     maxWidth = 'sm',
     onBackdropClick,
+    onClick,
     onClose,
     open,
     PaperComponent = Paper,
@@ -211,6 +212,10 @@ const Dialog = React.forwardRef(function Dialog(inProps, ref) {
     backdropClick.current = event.target === event.currentTarget;
   };
   const handleBackdropClick = (event) => {
+    if (onClick) {
+      onClick(event);
+    }
+
     // Ignore the events not coming from the "backdrop".
     if (!backdropClick.current) {
       return;
@@ -360,6 +365,10 @@ Dialog.propTypes /* remove-proptypes */ = {
    * @deprecated Use the `onClose` prop with the `reason` argument to handle the `backdropClick` events.
    */
   onBackdropClick: PropTypes.func,
+  /**
+   * @ignore
+   */
+  onClick: PropTypes.func,
   /**
    * Callback fired when the component requests to be closed.
    *


Test patch:
diff --git a/packages/mui-material/src/Dialog/Dialog.test.js b/packages/mui-material/src/Dialog/Dialog.test.js
--- a/packages/mui-material/src/Dialog/Dialog.test.js
+++ b/packages/mui-material/src/Dialog/Dialog.test.js
@@ -187,6 +187,28 @@ describe('<Dialog />', () => {
       expect(onClose.callCount).to.equal(1);
     });
 
+    it('calls onBackdropClick when onClick callback also exists', () => {
+      const onBackdropClick = spy();
+      const onClick = spy();
+      render(
+        <Dialog
+          onClick={onClick}
+          onClose={(event, reason) => {
+            if (reason === 'backdropClick') {
+              onBackdropClick();
+            }
+          }}
+          open
+        >
+          foo
+        </Dialog>,
+      );
+
+      clickBackdrop(screen);
+      expect(onBackdropClick.callCount).to.equal(1);
+      expect(onClick.callCount).to.equal(1);
+    });
+
     it('should ignore the backdrop click if the event did not come from the backdrop', () => {
       const onBackdropClick = spy();
       const { getByRole } = render(