UPDATE: I never got styled-components to apply in a React custom element. What I ended up doing is upgrading to mui v5(including the use of emotion) and there I was able to use styled from #mui/material/styles and makeStyles from #mui/styles just fine.
See here How to create insertion point to mount styles in shadow dom for MUI material v5 in React custom element
ORIGINAL:
I had this React application in which the styled-components were applied just fine and everything worked great. I have now converted this app to a custom element and now the styled-components are not being applied. Any help is appreciated.
Item.styles.tsx <---holds the styled components
export const ItemBody = styled.section`
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 0 1.25rem;
`;
export const ItemDetailContainer = styled.div`
margin: 0.25rem;
min-height: 5rem;
`;
// and so on
MyItem.tsx
import {
ItemBody,
ItemDetailContainer,
ItemButton,
} from './../styles/Item.styles';
const useStyles = makeStyles((theme) => ({
descriptionWrapper: {
fontSize: '1.5rem',
fontWeight: 900,
}
// and so on. NOTE: These styles ARE still getting applied but not the imported ones
}));
type Props = {
itemId: string;
itemName: string;
itemDescription?: string[];
};
const MyItem: React.FC<Props> = ({
itemId,
itemName,
itemDescription,
}) => {
const classes = useStyles();
return (
<div key={tileIndex}>
<ItemBody> //style NOT applied
<ItemDetailContainer> //style NOT applied
<span className={classes.descriptionWrapper}> //style IS applied
{itemDescription}
</span>
</ItemDetailContainer>
</ItemBody>
//and so on
);
};
export default MyItem;
App.tsx
import React from 'react';
import MyItem from './components/MyItem';
import './App.styles.css';
class App extends React.Component {
render() {
return (
<div className='App'>
<MyItem />
</div>
);
}
}
export default App;
index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { render } from 'react-dom';
import {
StylesProvider,
jssPreset,
} from '#material-ui/core/styles';
import { create } from 'jss';
class MyWebComponent extends HTMLElement {
connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });
const mountPoint = document.createElement('custom-jss-insertion-point');
const reactRoot = shadowRoot.appendChild(mountPoint);
const jss = create({
...jssPreset(),
insertionPoint: reactRoot,
});
render(
<StylesProvider jss={jss}>
<App />
</StylesProvider>,
mountPoint
);
}
}
customElements.define('my-element', MyWebComponent);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>React Custom Element</title>
</head>
<body>
<my-element id="elem"></my-element>
<div id="root"></div>
</body>
</html>
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const Dotenv = require('dotenv-webpack');
module.exports = {
mode: 'development',
entry: {
app: path.join(__dirname, 'src', 'index.tsx'),
},
devtool: 'source-map',
devServer: {
port: 3000,
historyApiFallback: {
index: '/index.html',
},
},
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'react-web-component-style-loader' },
{ loader: 'css-loader' },
],
},
{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ },
{ test: /\.js$/, use: 'babel-loader' },
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
fallback: { constants: require.resolve('constants-browserify') },
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.ico',
filename: 'index.html',
manifest: './public/manifest.json',
logo192: './logo192.png',
}),
new Dotenv(),
],
};
I don't have the rep to comment, and im no tsx coder, but could it be that the import in MyItem.tsx is missing "from"?
import {
ItemBody
ItemDetailContainer
ItemButton
}'./../styles/Item.styles';
should be
import {
ItemBody,
ItemDetailContainer,
ItemButton
} from './../styles/Item.styles';
Don't forget to comma separate the imports!
Related
My css-module is not working with Storybook. There are not any error, it is only not working. I don´t understand what is the problem. This is the image of how storybook is rendering the button:
Button.js file:
import React from "react";
import PropTypes from "prop-types";
import style from "./styles.module.css";
const Button = ({ type, children }) => (
<button className={style.button}>{children}</button>
);
Button.propTypes = {
children: PropTypes.node.isRequired,
type: PropTypes.string,
};
Button.defaultProps = {
children: "primary",
type: "primary",
};
export default Button;
Button.stories.js file:
import React from "react";
import Button from "./Button";
export default {
component: Button,
title: "Test/Button",
};
const Template = (args) => <Button {...args} />;
export const Primary = Template.bind({});
Primary.args = {
children: "xx ",
};
export const Secondary = Template.bind({});
Secondary.args = {
children: "xx ",
type: "secundary",
};
styles.module.css file:
.button {
background: yellow;
border: 1px solid var(--colors-transparent);
}
.test {
background: red;
color: var(--colors-white);
}
.type-primary {
background: red;
color: var(--colors-white);
}
.type-secundary {
background: rgb(12, 177, 163);
color: var(--colors-white);
}
package.json file:
"devDependencies": {
"#babel/core": "^7.16.0",
"#storybook/addon-actions": "^6.3.12",
"#storybook/addon-essentials": "^6.3.12",
"#storybook/addon-links": "^6.3.12",
"#storybook/react": "^6.3.12",
"babel-loader": "^8.2.3",
"classnames": "^2.3.1",
"react": "^16.14.0",
"react-scripts": "^4.0.3"
}
I have tried these other options
<button className={style["button"]}>{children}</button>
Maybe some idea how to solve it?
#Slava Zoref you can see this video css-modules storybook 6 minute 15:52 into .storybook/main.js you can write this code:
async function supportCssModules(config) {
// console.log('1=================================')
// console.log('>>>config', config.module.rules)
// console.log('1=================================')
config.module.rules.find(
(rule) => rule.test.toString() === '/\\.css$/'
).exclude = /\.module\.css$/
config.module.rules.push({
test: /\.module\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
})
return config
}
module.exports = {
stories: [
'../atomDesign/**/*.stories.#(js|jsx|ts|tsx)',
// "../ejemplos/**/*.stories.#(js|jsx|ts|tsx)",
// "../stories/**/*.stories.mdx",
// "../stories/**/*.stories.#(js|jsx|ts|tsx)"
],
addons: [
'#storybook/addon-links',
'#storybook/addon-essentials', //,'storybook-css-modules-preset',
],
// FIXME: Support CSS Modules for Storybook
webpackFinal: supportCssModules,
}
Use Storybook preset addon to add CSS Modules capabilities.
Installation
npm install -D storybook-css-modules
Configuration
Next, update .storybook/main.js to the following:
// .storybook/main.js
module.exports = {
stories: [
// ...
],
addons: [
// Other Storybook addons
"storybook-css-modules", // 👈 The addon registered here
],
};
By default this preset configured CSS Modules
When working with scss modules in typescript environment, my modules are stored in a property called default
Button.style.scss
.button {
background-color: black;
}
index.tsx
import * as React from 'react';
import * as styles from './Button.module.scss';
console.log(styles);
export const Button = () => (
<button className={styles.default.button}>Hello</button>
);
Console.log output
Module {__esModule: true, default: {button: "_2t432kRILm79F3WhLGdN6N"}, Symbol(Symbol.toStringTag): "Module"}
Why is that ? I cannot properly generate types with typed-scss-modules because of that.
EDIT:
Storybook config is
const path = require('path');
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.#(js|jsx|ts|tsx)'],
addons: ['#storybook/addon-links', '#storybook/addon-essentials'],
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader?modules=true', 'sass-loader'],
include: path.resolve(__dirname, '../'),
});
return config;
},
};
You need to adjust your css-loader rule to have the "esModule": false option, like follows:
{
loader: 'css-loader',
options: {
esModule: false,
}
},
{
loader: 'sass-loader',
}
This ensures import statements will return just the string, only
I am trying to get under the hoods of styled-components library.
reading this article have helped me a-lot:
How styled-components works
But there is something I can't fully understand:
the article describes how styled-component creates the 'style' tag inside the 'head' section and at runtime injects the component style in-there.
so I have created a minimal react project and the UI is working fine, except I don't
see the injected CSS style in the DOM.
I do see an empty styled tag in the head:<style data-styled-components=""></style>.
scratching my head how the css manages to apply successfully...
I even downgraded the library version to 3.3.3, the same as in the article - and it still doesn't work.
Assuming it will be more efficient to ask Stack-overflow than in the article,
may be someone here can help?
webpack-config:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const config = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'), // base path where to send compiled assets
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
// config for sass compilation
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader'
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
// plugin for inserting scripts automatically into html
template: 'src/index.html',
}),
new MiniCssExtractPlugin({
// plugin for controlling how compiled css will be outputted and named
filename: '[name].css',
}),
],
};
module.exports = config;
the react code
import React from 'react'
import styled from 'styled-components'
import ReactDOM from 'react-dom';
let counter = 6
const Button = styled.button`
color: coral;
padding: 0.25rem 1rem;
border: solid 2px coral;
border-radius: 3px;
margin: 0.5rem;
font-size: ${({size}) => { return size }}px;
`
const MyButton = (props) => {
return (<Button onClick={()=>update()} size={props.size}> Hi, I am your new button </Button>)
}
const update=()=>{
counter ++;
ReactDOM.render(<MyButton size={counter}/>, document.getElementById('root'))
}
export default MyButton
:
I am trying to import global css file inside react javascript file. I am importing in following way:
import React from "react";
import MainWrapper from "./components";
import "./global.css";
const App = () => {
return (
<MainWrapper />
);
};
export default App;
My global.css file looks like following:
#import '/node_modules/antd/dist/antd.css';
i.anticon.anticon-menu-unfold.trigger, i.anticon.anticon-menu-fold.trigger {
margin: auto;
display: flex;
justify-content: center;
padding: 12px;
color: white;
}
i.anticon.anticon-menu-fold.trigger{
float: right;
}
Here above, I am overidding predefined css of ANT Design. Here it is not able to override ant's predefined css.
My webpack.config.js looks like following:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const WebapckManifestPlugin = require("webpack-manifest-plugin");
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
"#babel/preset-env",
"#babel/preset-react"
],
plugins: [
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-proposal-class-properties"
]
}
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader"
}
]
},
// {
// test: /\.css$/,
// use: [
// "style-loader"
// ,
// "css-loader"
// ]
// },
{
test: /\.css$/,
use:
[
{
loader: "style-loader",
},
// {
// loader: "isomorphic-style-loader",
// },
{
loader: "css-loader",
options: {
importLoaders: 1,
modules: {
localIdentName: "[name]__[local]___[hash:base64:5]"
}
}
}
]
},
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
loader: "url-loader?limit=100000" }
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./public/index.html",
filename: "./index.html"
}),
new WebapckManifestPlugin({
template: "./public/manifest.json"
})
],
resolve: {
extensions: ["*",".js",".jsx"]
}
}
However, look below. I am importing css file in below js file. It works
import React, { useState } from "react";
import { Layout, Menu, Icon } from "antd";
// import useStyles from 'isomorphic-style-loader/useStyles'
import style from "./style.css";
const Dashboard = () => {
// useStyles(style);
return(
<div className={style.dashboard}>
karan
</div>
);
};
export default Dashboard;
My style.css file looks like this:
.dashboard{
text-align: center;
}
I think the problem is with webpack.config.js.
When I change style syntax from:
className={style.dashboard}
To:
className="dashboard"
And import statement from:
import style from "./style.css";
To:
import "./style.css";
And webpack.config.js test: /.css$/ to:
{
test: /\.css$/,
use: [
"style-loader"
,
"css-loader"
]
}
Everythings works then.
Please refer my files for this project.
The issue that I am facing is that when I apply some custom css (LESS file) to any element what happens is that first my bootstrap css gets applied and after a fraction of second my custom css gets applied.
I am able to see the lag.
For example, first my div is 100% width and after a lag I see my custom width of 300px getting applied.
index.js
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import {
BrowserRouter,
Route,
browserHistory,
Switch
} from 'react-router-dom';
import {
HomePage,
LoginPage,
SignupPage,
ForgotPasswordPage
} from './screens';
import {
Provider
} from 'react-redux';
import 'bootstrap/dist/css/bootstrap.min.css';
import './style/main.less';
import configureStore from './store';
const store = configureStore();
class App extends React.Component {
render() {
return (
<Provider store={store}>
<BrowserRouter history={browserHistory}>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/login" component={LoginPage} />
<Route exact path="/register" component={SignupPage} />
<Route exact path="/forgot-password" component={ForgotPasswordPage} />
</Switch>
</BrowserRouter>
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'development',
entry: {
app: ['./src/index.js']
},
output: {
path: path.join(__dirname, './build'),
publicPath: '/',
filename: 'bundle.[name].[hash].js'
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['react', 'env', 'stage-1']
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader', options: {
sourceMap: true
}
}, {
loader: 'less-loader', options: {
sourceMap: true
}
}]
}
]
},
resolve: {
extensions: ['.js', '.jsx', '.json']
}
};
Simply writing my css in LESS files. Calling the main LESS file and bootstrap css in the index.js. Using classes on the div.