Repository: mui/material-ui (TypeScript)
Date: 2021-06-02 14:18:58+00:00
Task type: Bug Fix

Issue:
[Popover/Menu] Trying to get a ref to the paper element breaks Popover/Menu
- [x] The issue is present in the latest release.
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Current Behavior 😯

If you try to get a ref to the Paper element in Menu using `<Menu PaperProps={{ ref: paperRef }}>` it will break the menu (same for Popover which Menu is based on) and positioning will no longer work.

## Expected Behavior 🤔

It should be possible to get a ref to the Paper element without breaking the Menu/Popover

## Steps to Reproduce 🕹

https://codesandbox.io/s/stupefied-frog-pkrbo?file=/src/Demo.tsx

1. Use  `<Menu PaperProps={{ ref: paperRef }}>`

## Context 🔦

I am trying to virtualize a menu with a headless virtual list library ([react-virtual](https://github.com/tannerlinsley/react-virtual)). I need a ref to the paper element (which is Menu's scroll container) so the virtualization library can do its work.

This doesn't seem too complex to fix. Popover just assigns its ref and lets PaperProps override it. Instead Popover needs to do the same thing as the className right after it. Use a fork ref for PaperProps.ref and paperRef and pass that to ref after `{...PaperProps}`.

https://github.com/mui-org/material-ui/blob/8fc684f67393eee40776bd66a71eaccae12b7ef8/packages/material-ui/src/Popover/Popover.js#L370-L374

## Your Environment 🌎

<!--
  Run `npx @material-ui/envinfo` and post the results.
  If you encounter issues with TypeScript please include the used tsconfig.
-->
<details>
  <summary>`npx @material-ui/envinfo`</summary>
  
```
  System:
    OS: Linux 5.4 Ubuntu 20.10 (Groovy Gorilla)
  Binaries:
    Node: 16.2.0 - ~/.nvm/versions/node/v16.2.0/bin/node
    Yarn: 1.22.5 - /mnt/c/Program Files (x86)/Yarn/bin/yarn
    npm: 7.13.0 - ~/.nvm/versions/node/v16.2.0/bin/npm
  Browsers:
    Chrome: Not Found
    Firefox: Not Found
  npmPackages:
    @emotion/styled:  10.0.27 
    @material-ui/core: ^4.11.2 => 4.11.2 
    @material-ui/icons: ^4.11.2 => 4.11.2 
    @material-ui/lab: ^4.0.0-alpha.57 => 4.0.0-alpha.57 
    @material-ui/pickers: ^3.2.10 => 3.2.10 
    @material-ui/styles:  4.11.2 
    @material-ui/system:  4.11.2 
    @material-ui/types:  5.1.0 
    @material-ui/utils:  4.11.2 
    @types/react: ^17.0.0 => 17.0.0 
    react: ^17.0.1 => 17.0.1 
    react-dom: ^17.0.1 => 17.0.1 
    styled-components:  5.2.1 
    typescript: ^4.1.2 => 4.1.2 
```
</details>



Issue discussion:
Thanks for the report.

We have a built-in utility called `useForkRef` which should be able to resolve this issue.

Solution patch:
diff --git a/packages/material-ui/src/Popover/Popover.js b/packages/material-ui/src/Popover/Popover.js
--- a/packages/material-ui/src/Popover/Popover.js
+++ b/packages/material-ui/src/Popover/Popover.js
@@ -14,6 +14,7 @@ import useThemeProps from '../styles/useThemeProps';
 import debounce from '../utils/debounce';
 import ownerDocument from '../utils/ownerDocument';
 import ownerWindow from '../utils/ownerWindow';
+import useForkRef from '../utils/useForkRef';
 import Grow from '../Grow';
 import Modal from '../Modal';
 import Paper from '../Paper';
@@ -120,6 +121,7 @@ const Popover = React.forwardRef(function Popover(inProps, ref) {
     ...other
   } = props;
   const paperRef = React.useRef();
+  const handlePaperRef = useForkRef(paperRef, PaperProps.ref);
 
   const styleProps = {
     ...props,
@@ -369,8 +371,8 @@ const Popover = React.forwardRef(function Popover(inProps, ref) {
       >
         <PopoverPaper
           elevation={elevation}
-          ref={paperRef}
           {...PaperProps}
+          ref={handlePaperRef}
           className={clsx(classes.paper, PaperProps.className)}
         >
           {children}


Test patch:
diff --git a/packages/material-ui/src/Popover/Popover.test.js b/packages/material-ui/src/Popover/Popover.test.js
--- a/packages/material-ui/src/Popover/Popover.test.js
+++ b/packages/material-ui/src/Popover/Popover.test.js
@@ -254,6 +254,23 @@ describe('<Popover />', () => {
     });
   });
 
+  describe('PaperProps.ref', () => {
+    it('should position popover correctly', () => {
+      const handleEntering = spy();
+      render(
+        <Popover
+          {...defaultProps}
+          open
+          PaperProps={{ 'data-testid': 'Popover', ref: () => null }}
+          TransitionProps={{ onEntering: handleEntering }}
+        >
+          <div />
+        </Popover>,
+      );
+      expect(handleEntering.args[0][0]).toHaveInlineStyle({ top: '16px', left: '16px' });
+    });
+  });
+
   describe('transition lifecycle', () => {
     describe('handleEntering(element)', () => {
       it('should set the inline styles for the enter phase', () => {