Repository: mui/material-ui (TypeScript)
Date: 2018-07-09 23:37:47+00:00
Task type: Bug Fix

Issue:
Prototypes are lost when set as theme defaults
- [x] This is a v1.x issue (v0.x is no longer maintained).
- [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.

## Expected Behavior
Default props in a theme keep their prototype.

## Current Behavior
The prototype in theme props is removed due to the use of `deepmerge`.

## Steps to Reproduce (for bugs)
The following works:
```jsx
const popoverContainer = document.createElement('div');

<MuiThemeProvider
  theme={createMuiTheme({
    props: {
      MuiPopover: {
        container: () => popoverContainer,
      },
    },
  })}
>
  <App />
</MuiThemeProvider>
```
The following does not (but should):
```jsx
const popoverContainer = document.createElement('div');

<MuiThemeProvider
  theme={createMuiTheme({
    props: {
      MuiPopover: {
        // The popover will receive a regular object
        container: popoverContainer,
      },
    },
  })}
>
  <App />
</MuiThemeProvider>
```

https://codesandbox.io/s/04j9lxymow (based on https://github.com/mui-org/material-ui/issues/12011#issuecomment-401550066)

1. Pass an HTML element as a `MuiPopover` prop to `<MuiThemeProvider />`
2. Render a `<Popover />`
3. Notice the app crashes.


## Context
<!---
    How has this issue affected you? What are you trying to accomplish?
    Providing context helps us come up with a solution that is most useful in the real world.
-->
https://material-ui.com/customization/themes/#properties

## Your Environment

| Tech         | Version |
|--------------|---------|
| Material-UI  | v1.3.0  |
| React        | v16.3.0 |
| browser      | Chrome 67        |
| deepmerge          |  v2.1.1       |



Issue discussion:
> The prototype in theme props is removed due to the use of deepmerge.

@remcohaszing Looking at the options of deepmerge, I think that we should be ignoring non "plain object" : https://github.com/KyleAMathews/deepmerge#ismergeableobject.
This implementation is intereseting: https://github.com/jonschlinkert/is-plain-object/blob/master/index.js.

Solution patch:
diff --git a/.size-limit.js b/.size-limit.js
--- a/.size-limit.js
+++ b/.size-limit.js
@@ -21,13 +21,13 @@ module.exports = [
     name: 'The initial cost people pay for using one component',
     webpack: true,
     path: 'packages/material-ui/build/Paper/index.js',
-    limit: '17.6 KB',
+    limit: '17.8 KB',
   },
   {
     name: 'The size of all the modules of material-ui.',
     webpack: true,
     path: 'packages/material-ui/build/index.js',
-    limit: '94.3 KB',
+    limit: '94.5 KB',
   },
   {
     name: 'The main bundle of the docs',
diff --git a/packages/material-ui/package.json b/packages/material-ui/package.json
--- a/packages/material-ui/package.json
+++ b/packages/material-ui/package.json
@@ -44,6 +44,7 @@
     "deepmerge": "^2.0.1",
     "dom-helpers": "^3.2.1",
     "hoist-non-react-statics": "^2.5.0",
+    "is-plain-object": "^2.0.4",
     "jss": "^9.3.3",
     "jss-camel-case": "^6.0.0",
     "jss-default-unit": "^8.0.2",
diff --git a/packages/material-ui/src/styles/createMuiTheme.js b/packages/material-ui/src/styles/createMuiTheme.js
--- a/packages/material-ui/src/styles/createMuiTheme.js
+++ b/packages/material-ui/src/styles/createMuiTheme.js
@@ -1,6 +1,7 @@
 // @flow
 
 import deepmerge from 'deepmerge'; // < 1kb payload overhead when lodash/merge is > 3kb.
+import isPlainObject from 'is-plain-object';
 import warning from 'warning';
 import createBreakpoints from './createBreakpoints';
 import createMixins from './createMixins';
@@ -42,6 +43,9 @@ function createMuiTheme(options: Object = {}) {
         zIndex,
       },
       other,
+      {
+        isMergeableObject: isPlainObject,
+      },
     ),
   };
 


Test patch:
diff --git a/packages/material-ui/src/styles/createMuiTheme.test.js b/packages/material-ui/src/styles/createMuiTheme.test.js
--- a/packages/material-ui/src/styles/createMuiTheme.test.js
+++ b/packages/material-ui/src/styles/createMuiTheme.test.js
@@ -77,6 +77,9 @@ describe('createMuiTheme', () => {
         MuiButtonBase: {
           disableRipple: true,
         },
+        MuiPopover: {
+          container: document.createElement('div'),
+        },
       };
       const muiTheme = createMuiTheme({ props });
       assert.deepEqual(muiTheme.props, props);