I would like to add documentation alongside my components in Storybook thanks to markdown syntax. I've followed Storybook's guidelines, but when launching the app, I receive the following error in my terminal:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory. (...) Command failed with signal "SIGABRT".
Here is my storybook config:
// main.ts
module.exports = {
stories: ["../**/stories.tsx", "../**/stories.mdx"],
addons: [
{
name: "#storybook/addon-essentials",
options: { backgrounds: false, docs: false, viewport: false },
},
"storybook-addons-abstract/register",
],
};
// webpack.config.js
module: {
rules: [
{ test: /\.mdx?$/, use: ["babel-loader", "#mdx-js/loader"]}
//... + all necessary loaders such as ts-loader, etc.)
]
}
My component:
import React from "react";
export default function Text({ content }) {
return <h1>{content}</h1>;
}
And the story (stories.mdx):
import { Story, Meta, Canvas } from "#storybook/components";
import Text from ".";
<Meta title="MDX/Text" component={Text} />
# Checkbox
Here is the text component:
<Canvas>
<Story name="defaut">
<Text text="hello" />
</Story>
</Canvas>
How to fix this?
Try to do it this way:
import { Story, Meta, Canvas } from "#storybook/components";
import Text from ".";
<Meta title="MDX/Text" component={Text} />
export const Template = (args) => <Text {...args} />
# Checkbox
Here is the text component:
<Canvas>
<Story name="Primary" args={{ text: 'hello' }}>
{Template.bind({})}
</Story>
</Canvas>
import Text from ".";
I think this might be where the problem is, seems like it's an infinite loop
Related
I'm working on a project to capture React component usage. I'm using Babel to transpile each file and grab JSX. For example, I can transpile a file like the following.
const profile = (
<div>
<img src="avatar.png" className="profile" />
<h3>{[user.firstName, user.lastName].join(" ")}</h3>
</div>
);
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
const profile = _jsxs("div", {
children: [
_jsx("img", {
src: "avatar.png",
className: "profile",
}),
_jsx("h3", {
children: [user.firstName, user.lastName].join(" "),
}),
],
});
I'm wondering if there's a way to go back to the original from the transpiled code?
I am working on a storybook project and here is a story of a sample component (sample.stories.mdx):
import Sample from './index';
import { ComponentStory, ComponentMeta } from '#storybook/react';
import { Canvas, Meta, Story, ArgsTable } from '#storybook/addon-docs';
<Meta title='Molecules/Sample' component={Sample} />
export const Template = (props) => <Sample {...props} />;
export const props = {
label: 'Sample',
children: 'Click me'
};
<Canvas>
<Story name='Default' args={props}>
{Template.bind({})}
</Story>
</Canvas>
<ArgsTable story='Default' />
But every time I start the project, it shows the following error:
./components/molecules/Sample/Sample.stories.mdx: Module parse failed: Unexpected token (5:64)
You may need an appropriate loader to handle this file type, currently no loaders are
configured to process this file
Can anyone help? Is this mdx file correct?
Thanks in advance.
Final edit: Thanks everyone for your help, however ultimately it was easier for me to transition to Webpack and Storybook. I'm leaving my original question untouched just in case it helps anyone in the future. Also, if anyone stumbles upon any issues configuring these (like I did), the link to the GitHub repo is below.
I'm creating a small lib using React and Rollup and trying to test locally with a CRA-powered project, however I'm facing this issue when importing a component from my library. I don't know if the problem is in my configuration or if this is a bug.
Uncaught ReferenceError: r is not defined
at Object.../dist/bundle.js (index.jsx:44)
Imported "Message" component where the error is happening
import React, { useEffect, useState } from 'react';
import { string, number, arrayOf } from 'prop-types';
import Container from './Container';
function Message({
text,
type,
timeout,
classes,
}) {
const [show, setShow] = useState(false);
useEffect(() => {
if (text && type) {
setShow(true);
setTimeout(() => setShow(false), timeout);
}
}, [text, type]);
const date = new Date();
return (
<Container
id={`message-${date}`}
key={`message-${date}`}
className={`${type}${classes?.map((className) => ` ${className}`)}`}
>
{
show
? (
<p>{text}</p>
) : ''
}
</Container>
);
}
// The source map points to this line when the error happens, but it still happens if I remove it and don't use prop-types, instead pointing to the closing bracket of the 'Message' function
Message.defaultProps = {
timeout: 3000,
classes: [],
};
Message.propTypes = {
text: string.isRequired,
type: string.isRequired,
timeout: number,
classes: arrayOf(string),
};
export default Message;
Test component where it's being used:
import React from 'react';
import { Message } from 'pure-ui';
import { getRandomArrayElement } from 'formatadores';
const types = [
'warning',
'error',
'success',
];
const texts = [
'This is a test',
'I will randomly display a message every so often, so stay sharp',
'Yet another test message',
];
const timeouts = [
5000,
3000,
1000,
];
function App() {
return (
<div>
<h1>Running...</h1>
<Message
type={getRandomArrayElement(types)}
text={getRandomArrayElement(texts)}
timeout={getRandomArrayElement(timeouts)}
/>
</div>
);
}
export default App;
rollup config file:
import babel from '#rollup/plugin-babel';
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import React from 'react';
import propTypes from 'prop-types';
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
export default [
{
input: 'src/index.js',
watch: true,
output: {
file: 'dist/bundle.js',
format: 'iife',
sourcemap: true,
globals: {
react: 'React',
'react-dom': 'ReactDOM',
'prop-types': 'PropTypes',
},
},
plugins: [
external(),
babel({
exclude: 'node_modules/**',
presets: [
'#babel/preset-env',
['#babel/preset-react', { runtime: 'automatic' }],
],
}),
resolve({ extensions }),
commonjs({
namedExports: {
react: Object.keys(React),
'react/jsx-runtime': ['jsx', 'jsxs', 'Fragment'],
'react/jsx-dev-runtime': ['jsx', 'jsxs', 'jsxDEV'],
'prop-types': Object.keys(propTypes),
},
}),
],
external: [
'react',
'react-dom',
'prop-types',
],
},
];
I tried changing the namedExports (and also removing them), linking React from the lib to use the same version from the CRA project (in the lib both React and React DOM are listed as peer dependencies), but I always end with the same result. Is there something wrong with my config? This is the first time I use Rollup for creating a React component lib, so maybe there's something I missed
If the above info is insufficient, here's the GitHub repo
Thanks in advance
Edit: I just saw that I forgot to import React in my test project, however after doing so the results were the same, editing my original question just to fix that.
Update 1: I changed several configurations (changed deprecated rollup-plugins to their currently maintained versions, added globals to the output part of rollup.config.js, added namedExports to commonjs plugin configuration, added an external section specifying react, react-dom and prop-types), but now what I'm getting is a React is not defined error, updating the question with the new config
I'm trying to add Storybook to an existing React app but getting errors with imported svg files. The svg is imported and used like:
import Border from './images/border.inline.svg'
...
<Border className="card__border" />
This works when the app is run and built, but I get an error in Storybook. How come?
Failed to execute 'createElement' on 'Document': The tag name provided ('static/media/border.inline.258eb86a.svg') is not a valid name.
Error: Failed to execute 'createElement' on 'Document': The tag name provided ('static/media/border.inline.258eb86a.svg') is not a valid name.
The default webpack.config.js has:
...
{
test: /\.inline.svg$/,
loader: 'svg-react-loader'
},
...
Also, the existing code uses webpack 3, and I'm using Storybook V4.
This is happening because Storybook's default webpack config has its own svg config:
{
test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
loader: 'file-loader',
query: { name: 'static/media/[name].[hash:8].[ext]' }
},
I'm pretty sure this is the cause, because you can see the path outlined in error message: query: { name: 'static/media/[name].[hash:8].[ext]' } -> static/media/border.inline.258eb86a.svg
The solution can be to find the existing loader & change / or add an exclude rule to it. Here's an example of a custom .storybook/webpack.config.js:
// storybook 4
module.exports = (_, _, config) => {
// storybook 5
module.exports = ({ config }) => {
const rules = config.module.rules;
// modify storybook's file-loader rule to avoid conflicts with your inline svg
const fileLoaderRule = rules.find(rule => rule.test.test('.svg'));
fileLoaderRule.exclude = /\.inline.svg$/;
rules.push({
test: /\.inline.svg$/,
...
}],
});
return config;
};
It appears that Storybook V6 they have changed the default Webpack config. I found that the above answers didn't work for me.
They no longer have an SVG rule, therefore testing for SVG will either error or return back undefined.
There is a oneOf rule on the module.rules which contains a loader without a test as the last rule:
{
loader: '/Users/alexwiley/Work/OneUp/resources/client/node_modules/react-scripts/node_modules/file-loader/dist/cjs.js',
exclude: [Array],
options: [Object]
}
This is the culprit, you need to make sure that the file load is excluding all inline SVG file otherwise it will error.
Add the following to your .storybook/main.js file:
webpackFinal: async(config, { configType }) => {
config.module.rules.forEach((rule) => {
if (rule.oneOf) {
// Iterate over the oneOf array and look for the file loader
rule.oneOf.forEach((oneOfRule) => {
if (oneOfRule.loader && oneOfRule.loader.test('file-loader')) {
// Exclude the inline SVGs from the file loader
oneOfRule.exclude.push(/\.inline\.svg$/);
}
});
// Push your SVG loader onto the end of the oneOf array
rule.oneOf.push({
test: /\.inline\.svg$/,
exclude: /node_modules/,
loader: 'svg-react-loader', // use whatever SVG loader you need
});
}
});
return config;
}
In Storybook 6, You have to import it like this:
import { ReactComponent as Border } from './images/border.inline.svg'
Try that if it also works for your version since this question is from a year ago.
This is another way that fixed the issue for me
import Border from './images/border.inline.svg'
And then in your code
<img src={Border} alt="Border" className="w-25"/>
I got it working with
...
module.exports = {
module: {
rules: [
{
test: /\.inline.svg$/,
loader: 'svg-react-loader'
}
]
}
}
For me it was happening as I was using wrong tag name:
import React from 'react';
import RMDBLogo from '../../images/react-movie-logo.svg';
import TMDBLogo from '../../images/tmdb_logo.svg';
import { Wrapper, Content,LogoImg,TMDBLogoImg } from './Header.styles';
const Header = () => (
<Wrapper>
<Content>
<LogoImg src={RMDBLogo} alt='RMDBLogo' />
<TMDBLogo src={TMDBLogo} alt='TMDBLogo'/>
</Content>
</Wrapper>
);
export default Header;
I had imported TMDBLogoImg component while I'm using TMDBLogo tag inside Content tag.
I'm writing an isomorphic React app based on :
https://github.com/choonkending/react-webpack-node
Instead of css modules used in the examples I'd like to use scss though. And for some reason I'm having a really hard time getting them to work. My first step was to remove the css webpack loaders from both the server and the client configs replacing them with scss-specific loaders (as well as removing postcss) :
loaders: [
'style-loader',
'css-loader?modules&localIdentName=[name]_[local]_[hash:base64:3]',
'sass-loader?sourceMap',
]
But this throws ReferenceError: window is not defined when building as style-loader is apparently not suitable for server-side rendering. So my next idea was to use isomorphic-style-loader. As far as I understand to get it working I need to decorate my component with their higher order component withStyles:
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from '../assets/scss/common/index.scss';
const App = (props, context) => (
<div className={classNames('app')}>
<h1 className="home_header">Welcome!</h1>
{props.children}
</div>
);
export default withStyles(s)(App);
and then do some trickery in the code rendering page on the server. But the problem is, example from the package docs shows a flux action fired inside Express (https://libraries.io/npm/isomorphic-style-loader#webpack-configuration), and the boilerplate that I'm using uses react-router. So I'm kinda lost as how should I inject this object with insertCss into context. I tried this :
import React from 'react';
import { renderToString } from 'react-dom/server';
import { RouterContext, match, createMemoryHistory } from 'react-router';
import axios from 'axios';
import { Provider } from 'react-redux';
import createRoutes from 'routes.jsx';
import configureStore from 'store/configureStore';
import headconfig from 'components/Meta';
import { fetchComponentDataBeforeRender } from 'api/fetchComponentDataBeforeRender';
const clientConfig = {
host: process.env.HOSTNAME || 'localhost',
port: process.env.PORT || '3001'
};
// configure baseURL for axios requests (for serverside API calls)
axios.defaults.baseURL = `http://${clientConfig.host}:${clientConfig.port}`;
function renderFullPage(renderedContent, initialState, head = {
title: 'cs3',
css: ''
}) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
${head.title}
${head.link}
<style type="text/css">${head.css.join('')}</style>
</head>
<body>
<div id="app">${renderedContent}</div>
<script type="text/javascript">window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};</script>
<script type="text/javascript" charset="utf-8" src="/assets/app.js"></script>
</body>
</html>
`;
}
export default function render(req, res) {
const history = createMemoryHistory();
const store = configureStore({
project: {}
}, history);
const routes = createRoutes(store);
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
const css = [];
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
const context = { insertCss: (styles) => css.push(styles._getCss()) };
const InitialView = (
<Provider context={context} store={store}>
<RouterContext {...renderProps} />
</Provider>
);
fetchComponentDataBeforeRender(store.dispatch, renderProps.components, renderProps.params)
.then(() => {
const componentHTML = renderToString(InitialView);
const initialState = store.getState();
res.status(200).end(renderFullPage(componentHTML, initialState, {
title: 'foo',
css
}));
})
.catch(() => {
res.end(renderFullPage('', {}));
});
} else {
res.status(404).send('Not Found');
}
});
}
but I'm still getting Warning: Failed context type: Required context 'insertCss' was not specified in 'WithStyles(App)'. Any ideas how to tackle this ? And more importantly - is there no easier way to do it ? This seems like a lot of additional work.
There's a few parts to handling scss compilation with webpack when you're doing server-side rendering. First of all, you don't want node trying to import .scss files into your components.
So set a global variable WEBPACK: true in your webpack config:
plugins: [
new webpack.DefinePlugin({
'process.env': {
WEBPACK: JSON.stringify(true),
}
})
],
And in your components, only attempt to import .scss files if the component is being handled by webpack (either during build or development):
if (process.env.WEBPACK) require('../assets/scss/common/index.scss');
If you only have one Sass file per component (you should) then this is always just a one-liner. Any additional Sass files can be imported inside index.scss if you need to.
Then in your config you probably still want the css loader, so it should look like this for your dev server:
{
test: /\.s?css$/,
loaders: ['style', 'css', 'sass']
},
And something like this for you build config:
{
test: /\.s?css$/,
loader: ExtractTextPlugin.extract('style', 'css!sass')
},