material-ui
material-ui copied to clipboard
cannot use default import in a component library
Duplicates
- [X] I have searched the existing issues
Latest version
- [X] I have tested the latest version
Steps to reproduce 🕹
Link to live example:
Steps:
- create a basic lib component with this dummy component (with a default import):
import Paper from '@mui/material/Paper';
import React from 'react';
export interface DummyProps {
message: string;
}
const Dummy = ({ message }: DummyProps) => {
return (
<Paper>{message}</Paper>
);
};
export default Dummy;
-
Set @mui and @emotion as a peer dependency (see config file below)
-
rollup (see config below) and publish the lib
-
Create a simple react application, with a component SuperDummy using Dummy from the lib
-
Write a unit test using SuperDummy => the test should fail with an error like React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.
Current behavior 😯
On one side I have a component library using react, mui and emotion as peerDep.
package.json
"peerDependencies": {
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@mui/icons-material": "^5.10.15",
"@mui/material": "^5.10.15",
"react": "18.2.0",
"react-dom": "18.2.0"
}
rollup config (note the regexp for @emotion and @mui)
external: [
'react',
'react-dom',
/@emotion\/.*/,
/@mui\/.*/,
],
output: [
{
file: 'dist/index.cjs.min.js',
format: 'cjs',
sourcemap: true,
plugins: [terser()],
},
{
file: 'dist/index.esm.min.js',
format: 'esm',
sourcemap: true,
plugins: [terser()],
},
],
plugins: [
resolve({ browser: true }),
...
typescript({ tsconfig: './tsconfig.json' }),
],
},
Lib is published with npm.
On the other side, I have a simple react application (storybook, webpack5) Nothing special on the config on this side.
When I run storybook, it works fine. When I run the app, it works fine too.
But when I run the jest unit tests, I get this error:
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.
I managed to surface that the error occurs as soon as I have a default import in the component library.
Like
import Paper from '@mui/material/Paper
instead of
import { Paper } from '@mui/material';
For those basic imports, I can leave with the second import, but then, I need to use icons too (or styled from @emotion/styled)
And these imports are only default:
import Circle from '@mui/icons-material/Circle';
As a workaround, I made this wrap and now it works but it's not ideal:
import Circle from '@mui/icons-material/Circle';
import { SvgIcon } from '@mui/material';
const normalizeIcon = (Icon: typeof SvgIcon) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return ((Icon as any).default ? (Icon as any).default : Icon) as typeof SvgIcon;
};
const CircleIcon = normalizeIcon(Circle)`
Expected behavior 🤔
I would like to be able to use default imports without any error.
For instance:
import Circle from '@mui/icons-material/Circle';
Context 🔦
I will have several applications and I want to share theme and components. Hence the creation of the component library. I want to be able to use the component library from the apps whatever the way the component library makes it's imports.
Your environment 🌎
npx @mui/envinfo
I used FireFox.
System:
OS: Linux 6.0 Fedora Linux 36 (Workstation Edition)
Binaries:
Node: 18.1.0 - /usr/bin/node
Yarn: Not Found
npm: 8.8.0 - /usr/bin/npm
Browsers:
Chrome: Not Found
Firefox: 107.0.1
npmPackages:
@emotion/react: ^11.10.5 => 11.10.5
@emotion/styled: ^11.10.5 => 11.10.5
@mui/base: 5.0.0-alpha.110
@mui/core-downloads-tracker: 5.11.0
@mui/icons-material: ^5.10.15 => 5.11.0
@mui/material: ^5.10.15 => 5.11.0
@mui/private-theming: 5.11.0
@mui/styled-engine: 5.11.0
@mui/system: 5.11.0
@mui/types: 7.2.3
@mui/utils: 5.11.0
@types/react: ^18.0.3 => 18.0.26
react: ^18.2.0 => 18.2.0
react-dom: ^18.2.0 => 18.2.0
typescript: ^4.6.3 => 4.9.4
It doesn't look like this bug report has enough info for one of us to reproduce it.
Please provide a CodeSandbox (https://material-ui.com/r/issue-template-latest), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
Since the issue is missing key information and has been inactive for 7 days, it has been automatically closed. If you wish to see the issue reopened, please provide the missing information.
Related: https://stackoverflow.com/questions/76687404/component-throwing-error-type-is-invalid-when-imported-to-another-project/76908656#76908656
@mnajdova - it's not possible to provide a minimal example with CodeSandbox. This error only appears when bundling with webpack, as the original poster mentioned.
I confirm the normalizeIcon work around makes the job, but it would be great not to have to use it.
For people that only have this problem in their jest tests, you can use the moduleNameMapper trick from here to simply ignore the icon modules:
{
"jest":{
"moduleNameMapper": {
"@mui/icons-material/.*": "<rootDir>/__mocks__/fileMock.js",
},
}
}
Beware: this may produce problems for screenshot and snapshot testing.
Possibly related to https://github.com/mui/material-ui/issues/35233 ?