React Storybook SVG Failed to execute 'createElement' on 'Document' - reactjs

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.

Related

I use SVG files as a component with "#svgr/webpack", How to use next/image module with it

In some places, I need to import my SVG file as a component, to have a control on it ( change style for example, change the fill color of the SVG element ) I used SVGR for that, and in other places, I want to use the SVG file as a normal image and get benefit with the built-in "next/image" module features.
I can't use next/image module as when I import it like
import FacebookIcon from "public/images/icons/social/facebook.svg";
FacebookIcon is a component.
Not a format that the src attribute of next/image component can accept.
From what I understood is src accept string or object with this format
{ src: "**", width: **px, height: **px }
Desired usage
import MySVG from "./mySVG.svg?svgr"; // SVGR loader
<MySVG />
import Image from "next/image";
import mySVG from "./mySVG.svg"; // Default NextJS loader
<Image src={mySVG} alt="" /> // (width and height will be applied automatically)
Required next.config.js
webpack(config, { dev: isDev, isServer }) {
config.module.rules.push({
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
resourceQuery: /svgr/, // only use svgr to load svg if path ends with *.svg?svgr
use: ["#svgr/webpack"],
});
// Re-add default nextjs loader for svg
config.module.rules.push({
test: /\.svg$/i,
loader: "next-image-loader",
issuer: { not: /\.(css|scss|sass)$/ },
dependency: { not: ["url"] },
resourceQuery: { not: [/svgr/] }, // Ignore this rule if the path ends with *.svg?svgr
options: { isServer, isDev, basePath: "", assetPrefix: "" },
});
}
Required typescript declaration (if using ts)
declare module "*.svg?svgr";
How I figured it out
Read these docs: https://react-svgr.com/docs/webpack/
Used this snippet to get the default rule applying to svgs
webpack(config) {
const defaultSvgLoader = config.module.rules.find(
(rule) => typeof rule?.test?.test === "function" && rule.test.test(".svg")
);
console.log(defaultSvgLoader);
}
Added resourceQuery: { not: [/svgr/] } into the logged output object so that *.svg?svgr paths will be ignored

How to import SVG in ReactJS with craco?

I'm struggling to import SVG's when I'm using craco in my react app.
It's suggested to use #svgr/webpack but I'm not sure how to put it into my craco.config.js
My current setup as per this (I prob shouldn't follow someone's config that doesn't work and expect it to work tho) that does not work:
// craco.config.js
const CracoAlias = require("craco-alias");
module.exports = {
plugins: [
{
plugin: CracoAlias,
options: {
source: "tsconfig",
baseUrl: "./src",
tsConfigPath: "./tsconfig.paths.json"
}
},
],
webpack: {
configure: (config, { env, paths }) => {
config.module.rules.push({
test: /\.svg$/,
use: ["#svgr/webpack"]
});
return config;
}
}
};
The craco.config.js webpack documentation is here but it's so confusing to me without concrete examples.
Also to note:
Writing import {ReactComponent as mySvg} from "./mySvg.svg" doesn't work because it doesn't recognize it as a ReactComponent.
If I try importing directly via import mySvg from "./mySvg.svg" Typescript doesn't recognize the file.
What I'm currently doing is putting the svg into a React component and using that but it's a nightmare doing that every time. I also put this in #types/custom.d.ts, but it still doesn't work when put into <img src={mySvg} />
// #types/custom.d.ts
declare module "*.svg" {
const content: any;
export default content;
}
import {reactComponent as GoogleLogo} from "../assets/image/googlelogo.svg;
GoogleLogo is component and reactComponent is a required magic string
i find the fix your problem in Adding svgr to create-react-app via craco

How to add #svgr/webpack to craco.config?

Similar to this question - I want to be able to load svgs in my reactjs app using the following syntax below. I'm using craco so it doesn't work by default.
import {ReactComponent as MyLogo} from "./svg/MyLogo.svg";
// ...
return (
<MyLogo />
);
My craco.config.js is...
const CracoAlias = require("craco-alias");
module.exports = {
plugins: [
{
plugin: CracoAlias,
options: {
source: "tsconfig",
// baseUrl SHOULD be specified
// plugin does not take it from tsconfig
baseUrl: "./src",
/* tsConfigPath should point to the file where "baseUrl" and "paths"
are specified*/
tsConfigPath: "./tsconfig.paths.json"
}
},
],
webpack: {
configure: (config, { env, paths }) => {
config.module.rules.push({
test: /\.svg$/,
use: ["#svgr/webpack"]
});
return config;
}
}
};
Not sure if this is the right way to hook up #svgr/webpack to my craco config.
Currently, it doesn't work and errors with this...
import {ReactComponent as MyLogo} from "./MyLogo.svg";
// Error Module '"*.svg"' has no exported member 'ReactComponent'.
// Did you mean to use 'import ReactComponent from "*.svg"' instead?
How can I make this work properly using craco?

CSS Modules cannot be imported from within node_modules

I am using one library in my main project. The library code looks like this /node_modules/#myCompany/all_components/src/Footer/components/Navlink/index.js
import React from 'react';
import styles from './style.module.scss';
export const NavLinks = ({ linksList }) => (
linksList
.map(({ url, title, id }) => (
<a
key={id}
className={styles.navLink}
href={url}>
{title}
</a>
))
);
Now in Next app /pages/index.js
import {NavLinks} from '#myCompany/all_components';
.....
export const Home = ({ data }) => {
return ()
};
.bablerc
{
"presets": ["next/babel"]
}
next.config.js
module.exports = {
webpack: (config) => {
config.resolve.extensions = ['.js', '.jsx'];
config.module.rules.push(
{test: /\.(js|jsx)$/, use: {
loader: "babel-loader",
options: {
presets: ['#babel/preset-env', '#babel/preset-react']
}
}}
);
return config;
}
}
But I am getting error -
error - ./node_modules/#myCompany/all_componentss/src/Footer/components/NavLinks /style.module.scss
CSS Modules cannot be imported from within node_modules.
Read more: https://nextjs.org/docs/messages/css-modules-npm
Location: node_modules\#myCompany/all_components\src\Footer\components\NavLinks\index.js
[BABEL] Note: The code generator has deoptimised the styling of C:\Projects\service-page\node_modules\react-dom\cjs\react-dom.development.js as it exceeds the max of 500KB.
wait - compiling /_error (client and server)...
event - compiled client and server successfully in 47.8s (176 modules)
error - SyntaxError: Unexpected token 'export'
Can you please advice what wrong I am doing here.
There's a workaround. You can use next-css. It seems to be a duplicate of https://stackoverflow.com/a/55255918/19135131
Does it help you?

No syntax highlighting with React Monaco Editor

Just installed React Monaco Editor and seems to be functioning properly except there is no syntax highlighting. I'm trying to use "python" as the language but the font stays the same gray default colour:
Any ideas on how to correct the issue?
Here is my Code.js where I'm running the Monaco Editor:
import React from "react";
import MonacoEditor from "react-monaco-editor";
class Code extends React.Component {
constructor(props) {
super(props);
this.state = {
code: 'print("Hello, World!")'
};
}
editorDidMount(editor, monaco) {
console.log("editorDidMount", editor);
editor.focus();
}
onChange(newValue, e) {
console.log("onChange", newValue, e);
}
render() {
const code = this.state.code;
const options = {
selectOnLineNumbers: true,
fontSize: 18,
colorDecorators: true
};
return (
<MonacoEditor
width="800"
height="600"
language="python"
theme="vs-dark"
value={code}
options={options}
onChange={this.onChange}
editorDidMount={this.editorDidMount}
/>
);
}
}
export default Code;
Also I added this code to the top of webpack.config.js:
const path = require('path');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
plugins: [
new MonacoWebpackPlugin({
// available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options
languages: ['python']
})
]
};
const APP_DIR = path.resolve(__dirname, './src');
const MONACO_DIR = path.resolve(__dirname, './node_modules/monaco-editor');
module.exports = {
test: /\.css$/,
include: APP_DIR,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
options: {
modules: true,
namedExport: true,
},
}],
}, {
test: /\.css$/,
include: MONACO_DIR,
use: ['style-loader', 'css-loader'],
}
If you are using the Monaco editor with create-react-app you will need a different approach, if you don't want to eject the app (to allow manually editing the webpack config file). This paragraph describes it pretty well:
The easiest way to use the react-monaco-editor with create-react-app is to use the react-app-rewired project. For setting it up, the following steps are required:
Install react-app-rewired: npm install -D react-app-rewired
Replace react-scripts by react-app-rewired in the scripts section of your packages.json
Create a config-overrides.js in the root directory of your project with the following content:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = function override(config, env) {
config.plugins.push(new MonacoWebpackPlugin({
languages: ['json']
}));
return config;
}
For more information checkout the documentation of react-app-rewired
here.
I did not have to specify anything else to make it work. No need to specify loaders for webpack manually.
For me both of the above answers are not working - not sure if it's related to Codesandbox or I did a mistake.
But using #monaco-editor/react is working with-out any changes to the CRA setup.
The only difference in the usage is that the default export is not a controlled component - so onchange is not working.
To have a controlled component, just use import {ControlledEditor as MonacoEditor} from "#monaco-editor/react". The onchange handler needs to be slightly modified, first the event & then the newText - just a small difference in the implementation.
The usage looks like following:
import React, { useState } from "react";
import { ControlledEditor as MonacoEditor } from "#monaco-editor/react";
export const Editor = () => {
const [code, setCode] = useState(`const greeting = () => {
alert("Hello world");
}`);
const options = {
minimap: {
enabled: false
}
};
const changeHandler = (evt, newText) => {
setCode(newText);
};
const editorDidMount = (editor, monaco) => {
console.log("editorDidMount", editor);
};
return (
<MonacoEditor
width="100%"
height="100%"
language="javascript"
theme="vs-dark"
value={code}
options={options}
onChange={changeHandler}
editorDidMount={editorDidMount}
/>
);
};
The options can be used to modify the Monaco editor. In my case I don't want to display the minimap. All available options can be found in the editor api docs
You can find the working demo in this Codesandbox.
The only thing that I found that is not working is undo/redo as described in the following issue. No change event triggered but I'll check this later - for now I'm happy with it.
Did you fail to configure CSS for Monaco Editor in Webpack? Perhaps that's issue since everything else looks good.
const path = require('path');
const MONACO_DIR = path.resolve(__dirname, './node_modules/monaco-editor');
{
test: /\.css$/,
include: MONACO_DIR,
use: ['style-loader', 'css-loader'],
}
Solution
The Problem was nothing with the configuration but the place where it was configured.
To customize webpack in your React CRA boilerplate, you must eject the app or use customize-cra if you don't want to eject, and do the configuration. OP here configured webpack inside node-modules/, That's not the right to configure your webpack, like at all. Use react-app-rewired with customize-cra.

Resources