Repository: mui/material-ui (TypeScript)
Date: 2019-11-15 09:02:20+00:00
Task type: Bug Fix

Issue:
Popper.js document is not defined with SSR
I need help with SSR using the `Popper` component. 

When I access to my page via a link from another page, the page is rendered as it should be.

But when I try to access the same page directly from its url, I get an HTTP error 500 and this error message:

```
Error running template: ReferenceError: document is not defined
    at flipPlacement (/Users/xxx/Documents/dev/epotek/microservices/www/node_modules/@material-ui/core/Popper/Popper.js:37:52)
    at Object.Popper (/Users/xxx/Documents/dev/epotek/microservices/www/node_modules/@material-ui/core/Popper/Popper.js:112:22)
    at ReactDOMServerRenderer.render (/Users/xxx/Documents/dev/epotek/microservices/www/node_modules/react-dom/cjs/react-dom-server.node.development.js:3758:44)
    at ReactDOMServerRenderer.read (/Users/xxx/Documents/dev/epotek/microservices/www/node_modules/react-dom/cjs/react-dom-server.node.development.js:3538:29)
    at renderToString (/Users/xxx/Documents/dev/epotek/microservices/www/node_modules/react-dom/cjs/react-dom-server.node.development.js:4247:27)
    at Promise.asyncApply (imports/startup/server/ssr-server.js:33:5)
    at /Users/xxx/.meteor/packages/promise/.0.11.2.c7kmck.hukra++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
```


Issue discussion:
This concern is related to https://github.com/mui-org/material-ui/issues/15798#issuecomment-495078241. In this context, I would suggest this diff:
```diff
diff --git a/packages/material-ui/src/Popper/Popper.js b/packages/material-ui/src/Popper/Popper.js
index 80f5f9909..da7f77e6c 100644
--- a/packages/material-ui/src/Popper/Popper.js
+++ b/packages/material-ui/src/Popper/Popper.js
@@ -13,7 +13,7 @@ import ownerWindow from '../utils/ownerWindow';
  * @param {string} placement
  */
 function flipPlacement(placement) {
-  const direction = (typeof window !== 'undefined' && document.body.getAttribute('dir')) || 'ltr';
+  const direction = (typeof document !== 'undefined' && document.body.getAttribute('dir')) || 'ltr';

   if (direction !== 'rtl') {
     return placement;
```

Would it help in your case? (you likely shouldn't define a window on the server).
Alternatively, we could start to use `canUseDOM`, this would probably be a more resilient solution.
Yes thank you that fixed my issue ! 

Solution patch:
diff --git a/packages/material-ui/src/Popper/Popper.js b/packages/material-ui/src/Popper/Popper.js
--- a/packages/material-ui/src/Popper/Popper.js
+++ b/packages/material-ui/src/Popper/Popper.js
@@ -2,20 +2,17 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import PopperJS from 'popper.js';
 import { chainPropTypes, refType } from '@material-ui/utils';
+import { useTheme } from '@material-ui/styles';
 import Portal from '../Portal';
 import createChainedFunction from '../utils/createChainedFunction';
 import setRef from '../utils/setRef';
 import useForkRef from '../utils/useForkRef';
 import ownerWindow from '../utils/ownerWindow';
 
-/**
- * Flips placement if in <body dir="rtl" />
- * @param {string} placement
- */
-function flipPlacement(placement) {
-  const direction = (typeof window !== 'undefined' && document.body.getAttribute('dir')) || 'ltr';
+function flipPlacement(placement, theme) {
+  const direction = (theme && theme.direction) || 'ltr';
 
-  if (direction !== 'rtl') {
+  if (direction === 'ltr') {
     return placement;
   }
 
@@ -72,7 +69,8 @@ const Popper = React.forwardRef(function Popper(props, ref) {
 
   const [exited, setExited] = React.useState(true);
 
-  const rtlPlacement = flipPlacement(initialPlacement);
+  const theme = useTheme();
+  const rtlPlacement = flipPlacement(initialPlacement, theme);
   /**
    * placement initialized from prop but can change during lifetime if modifiers.flip.
    * modifiers.flip is essentially a flip for controlled/uncontrolled behavior


Test patch:
diff --git a/packages/material-ui/src/Popper/Popper.test.js b/packages/material-ui/src/Popper/Popper.test.js
--- a/packages/material-ui/src/Popper/Popper.test.js
+++ b/packages/material-ui/src/Popper/Popper.test.js
@@ -3,6 +3,7 @@ import { assert, expect } from 'chai';
 import { spy, useFakeTimers } from 'sinon';
 import PropTypes from 'prop-types';
 import { createMount } from '@material-ui/core/test-utils';
+import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
 import describeConformance from '@material-ui/core/test-utils/describeConformance';
 import { createClientRender } from 'test/utils/createClientRender';
 import consoleErrorMock from 'test/utils/consoleErrorMock';
@@ -12,6 +13,7 @@ import Popper from './Popper';
 
 describe('<Popper />', () => {
   let mount;
+  let rtlTheme;
   const render = createClientRender({ strict: true });
   const defaultProps = {
     anchorEl: () => document.createElement('svg'),
@@ -21,6 +23,9 @@ describe('<Popper />', () => {
 
   before(() => {
     mount = createMount({ strict: true });
+    rtlTheme = createMuiTheme({
+      direction: 'rtl',
+    });
   });
 
   after(() => {
@@ -40,23 +45,17 @@ describe('<Popper />', () => {
   }));
 
   describe('prop: placement', () => {
-    before(() => {
-      document.body.setAttribute('dir', 'rtl');
-    });
-
-    after(() => {
-      document.body.removeAttribute('dir');
-    });
-
     it('should have top placement', () => {
       const renderSpy = spy();
       mount(
-        <Popper {...defaultProps} placement="top">
-          {({ placement }) => {
-            renderSpy(placement);
-            return null;
-          }}
-        </Popper>,
+        <ThemeProvider theme={rtlTheme}>
+          <Popper {...defaultProps} placement="top">
+            {({ placement }) => {
+              renderSpy(placement);
+              return null;
+            }}
+          </Popper>
+        </ThemeProvider>,
       );
       assert.strictEqual(renderSpy.callCount, 2); // 2 for strict mode
       assert.strictEqual(renderSpy.args[0][0], 'top');
@@ -87,12 +86,15 @@ describe('<Popper />', () => {
       it(`should flip ${test.in} when direction=rtl is used`, () => {
         const renderSpy = spy();
         mount(
-          <Popper {...defaultProps} placement={test.in}>
-            {({ placement }) => {
-              renderSpy(placement);
-              return null;
-            }}
-          </Popper>,
+          <ThemeProvider theme={rtlTheme}>
+            <Popper {...defaultProps} placement={test.in}>
+              {({ placement }) => {
+                renderSpy(placement);
+                return null;
+              }}
+            </Popper>
+            ,
+          </ThemeProvider>,
         );
         assert.strictEqual(renderSpy.callCount, 2);
         assert.strictEqual(renderSpy.args[0][0], test.out);
@@ -103,12 +105,15 @@ describe('<Popper />', () => {
       const renderSpy = spy();
       const popperRef = React.createRef();
       render(
-        <Popper popperRef={popperRef} {...defaultProps} placement="bottom">
-          {({ placement }) => {
-            renderSpy(placement);
-            return null;
-          }}
-        </Popper>,
+        <ThemeProvider theme={rtlTheme}>
+          <Popper popperRef={popperRef} {...defaultProps} placement="bottom">
+            {({ placement }) => {
+              renderSpy(placement);
+              return null;
+            }}
+          </Popper>
+          ,
+        </ThemeProvider>,
       );
       expect(renderSpy.args).to.deep.equal([['bottom'], ['bottom']]);
       popperRef.current.options.onUpdate({