issue loading css module classes in storybook v6.4 - css

I'm having trouble getting storybook to play nice with css modules within my Gatsby project. I'm able to render the button component but it does not add any of my styling. On inspection of the element, I'm only getting undefined undefined from the following code.
button.jsx
import React from "react"
import * as css from "./style.module.css"
const Button = ({ variant = "button", type, value = null }) => {
const baseOfVariant = () => {
if (variant === "input") {
return (
<input
type={type}
value={value}
className={`${css.button} ${css.clear_button}`}
/>
)
}
return (
<button type={type} className={`${css.button} ${css.submit_button}`}>
{value}
</button>
)
}
return baseOfVariant()
}
export default Button
button.stories.jsx
import React from "react"
import Button from "./button"
export default {
title: "Button",
component: Button,
}
export const Template = args => <Button {...args} />
export const ButtonRegular = Template.bind({})
ButtonRegular.args = {
variant: "button",
value: "Click Me",
type: "submit",
}
main.js
module.exports = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.#(js|jsx|ts|tsx)"],
addons: ["#storybook/addon-links", "#storybook/addon-essentials"],
core: {
builder: "webpack5",
},
}
Storybook stuff in my devDeps
"devDependencies": {
"#babel/core": "^7.14.6",
"#babel/polyfill": "^7.12.1",
"#storybook/addon-actions": "^6.4.0-alpha.2",
"#storybook/addon-essentials": "^6.4.0-alpha.2",
"#storybook/addon-links": "^6.4.0-alpha.2",
"#storybook/addon-viewport": "^6.4.0-alpha.2",
"#storybook/builder-webpack5": "^6.4.0-alpha.2",
"#storybook/manager-webpack5": "^6.4.0-alpha.2",
"#storybook/react": "^6.4.0-alpha.2",
"babel-loader": "^8.2.2",
"prettier": "2.2.1",
"resize-observer-polyfill": "^1.5.1"
}

Gatsby needs to import css modules with the following synthax:
import * as css from "./style.module.css"
Where Storybook recognizes only this synthax:
import css from "./style.module.css"
This is due to the fact that Gatsby and Storybook don't use the same import convention. Fortunately, you can configure Storybook css module import mechanism via .storybook/main.js file.
const path = require("path")
module.exports = {
// You will want to change this to wherever your Stories will live
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.#(js|jsx|ts|tsx)"],
addons: ["#storybook/addon-links", "#storybook/addon-essentials"],
core: {
builder: "webpack5",
},
webpackFinal: async config => {
// Prevent webpack from using Storybook CSS rules to process CSS modules
config.module.rules.find(
rule => rule.test.toString() === "/\\.css$/"
).exclude = /\.module\.css$/
// Tell webpack what to do with CSS modules
config.module.rules.push({
test: /\.module\.css$/,
include: path.resolve(__dirname, "../src"),
use: [
{
loader: "style-loader",
options: {
modules: {
namedExport: true,
},
},
},
{
loader: "css-loader",
options: {
importLoaders: 1,
modules: {
namedExport: true,
},
},
},
],
})
// Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]
// use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
config.module.rules[0].use[0].options.plugins.push(
require.resolve("babel-plugin-remove-graphql-queries")
)
return config
},
}
With the above configuration, Storybook now accept this import synthax and button.jsx is correctly styled.
import * as css from "./style.module.css"

If any one is looking for sass/scss related fix
import * as style from "./style.module.scss"
Where Storybook recognizes only this synthax:
your .storybook/main.js file.
const path = require("path");
module.exports = {
stories: ["../src/**/*.stories.{js,mdx}"],
addons: [
"#storybook/addon-docs",
"#storybook/addon-actions",
"#storybook/addon-controls",
"#storybook/addon-a11y",
"#storybook/addon-viewport",
],
// https://gist.github.com/shilman/8856ea1786dcd247139b47b270912324
core: {
builder: "webpack5",
},
webpackFinal: async config => {
// https://www.gatsbyjs.com/docs/how-to/testing/visual-testing-with-storybook/
config.module.rules.push({
test: /\.(js)$/,
use: [
{
loader: require.resolve("babel-loader"),
options: {
presets: [
// use #babel/preset-react for JSX and env (instead of staged presets)
require.resolve("#babel/preset-react"),
require.resolve("#babel/preset-env"),
],
plugins: [
// use #babel/plugin-proposal-class-properties for class arrow functions
require.resolve("#babel/plugin-proposal-class-properties"),
// use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
require.resolve("babel-plugin-remove-graphql-queries"),
// use babel-plugin-react-docgen to ensure PropTables still appear
require.resolve("babel-plugin-react-docgen"),
],
},
},
],
exclude: [/node_modules\/(?!(gatsby)\/)/],
});
config.module.rules.push({
test: /\.s[ac]ss$/i,
oneOf: [
// module.scss files (e.g component styles.module.scss)
// https://webpack.js.org/loaders/style-loader/#modules
{
test: /\.module\.s?css$/,
use: [
// Add exports of a module as style to DOM
{
loader: "style-loader",
options: {
esModule: true,
modules: {
namedExport: true,
},
},
},
// Loads CSS file with resolved imports and returns CSS code
{
loader: "css-loader",
options: {
esModule: true,
modules: {
namedExport: true,
},
},
},
// Loads and compiles a SASS/SCSS file
{
loader: "sass-loader",
// only if you are using additional global variable
options: {
additionalData: "#import 'src/styles/global.scss';",
sassOptions: {
includePaths: ["src/styles"],
},
},
},
],
},
// scss files that are not modules (e.g. custom.scss)
{
use: [
// Add exports of a module as style to DOM
"style-loader",
// Loads CSS file with resolved imports and returns CSS code
"css-loader",
// Loads and compiles a SASS/SCSS file
{
loader: "sass-loader",
// only if you are using additional global variable
options: {
additionalData: "#import 'src/styles/global.scss';",
sassOptions: {
includePaths: ["src/styles"],
},
},
},
],
},
],
});
return config;
},
};
With the above configuration, Storybook now accept this import synthax and button.jsx is correctly styled.
import * as styles from "./style.module.scss"

Related

TailwindCSS 3.0 Upgrade overriding button styles

Problem:
Button class being overridden by default tailwind base classes. Not sure why my classes on the element aren't being applied.
Question:
How can I get my styles to apply properly?
Screenshot:
As you can see background color on .documentCategory__row is being overridden by button, [type=button] on index.scss which is being defined within #tailwind/base.
/* index.scss */
:root {
--color-primary: #00a3e0;
--color-secondary: #470a68;
--color-success: #87d500;
--color-accent: #e87722;
/* Dark themes below */
--color-dark-primary: rgba(31, 41, 55, 1);
--dark-text: rgba(187, 193, 198, 1);
}
#import "tailwindcss/base";
#import "tailwindcss/components";
#import "tailwindcss/utilities";
I'm not sure if this has to do with me switching to dart-scss so here is my webpack configuration in case I am missing something
import path from 'path'
import { Configuration as WebpackConfiguration, HotModuleReplacementPlugin } from 'webpack'
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
import HtmlWebpackPlugin from 'html-webpack-plugin'
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'
import ESLintPlugin from 'eslint-webpack-plugin'
import tailwindcss from 'tailwindcss'
import autoprefixer from 'autoprefixer'
const CopyPlugin = require('copy-webpack-plugin');
interface Configuration extends WebpackConfiguration {
devServer?: WebpackDevServerConfiguration;
}
const config: Configuration = {
mode: 'development',
devServer: {
static: path.join(__dirname, 'build'),
historyApiFallback: true,
port: 4000,
open: true,
hot: true,
},
output: {
publicPath: '/',
},
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.(ts|js)x?$/i,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'#babel/preset-env',
'#babel/preset-react',
'#babel/preset-typescript',
],
},
},
},
{
test: /\.(sa|sc|c)ss$/i,
use: [
'style-loader',
'css-loader',
'sass-loader',
{
loader: 'postcss-loader', // postcss loader needed for tailwindcss
options: {
postcssOptions: {
ident: 'postcss',
plugins: [tailwindcss, autoprefixer],
},
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader',
options: {
outputPath: '../fonts',
},
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
}),
new HotModuleReplacementPlugin(),
new CopyPlugin({
patterns: [
// relative path is from src
{ from: 'public/images', to: 'images' },
],
}),
// Add type checking on dev run
new ForkTsCheckerWebpackPlugin({
async: false,
}),
// Add lint checking on dev run
new ESLintPlugin({
extensions: ['js', 'jsx', 'ts', 'tsx'],
}),
],
devtool: 'inline-source-map',
};
export default config
If there are other files I am missing that are needed let me know!
without seeing what your index.tsx looks like I can only make a guess, but here's what caused this issue in our app:
in our index.tsx we were importing index.css after importing our component tree with import App from 'src/App. thus the css was loaded into the site in the wrong order. imports from components first (css modules, normal css imports), tailwind last.
go to your entry file (probably index.tsx) and try moving your import 'index.scss' line above importing the root component.
like this for example
/* index.tsx */
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css'; // this file holds all tailwind styles
import { App } from 'src/App';
// ...
read more here:
https://github.com/tailwindlabs/tailwindcss/discussions/7304#discussioncomment-2256226
Even i faced the same issue but I am using Vue3 + element-ui-plus, after spending more than 6 hours my solution is to set :native-type='null':
<el-button type='primary' round #click='handleClick' :native-type='null'>Click Me</el-button>
but this is kinda "hack", this either need to be fixed by Tailwind or by element-ui team. Anyhow, for now enjoy ;)
And the discussion is on here
I got the same issue using tailwindcss v3 and NextUI, and button's background were "transparent". By adding type = {null}, to button's I solve the issue

Why scss module with typescript has .default property

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

webpack not bundling scss when using import instead of require

Simple webpack setup, when I use import to load my scss it is completely missing from the bundle. The line where the import should be is simply missing. When I use require instead, it works.
optimization: {usedExports: true} is not the problem, I tried with and without
mini-css-extract-plugin also did not work.
when I put a typo in the scss it complains, so it is parsed but simply not bundled in the end?
index.js
require("./scss/style.scss");
//import "./scss/style.scss" <--- not working
import { createApp } from 'vue';
import App from './components/App.vue';
const el = document.getElementById('app');
createApp(App).mount(el);
webpack config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const { DefinePlugin } = require('webpack');
const dist = path.resolve(__dirname, "dist");
module.exports = env => {
const mode = env.production == true ? "production" : "development";
const devtool = mode == "production" ? false : "inline-source-map";
return {
mode: mode,
entry: './web/index.js',
output: {
filename: 'bundle.js',
path: dist
},
optimization: {
usedExports: true,
},
devServer: {
static: {
directory: dist
},
port: 8888
},
module: {
rules: [{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
}, {
test: /\.(ttf|eot|woff|woff2|svg)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
},
},
}, {
test: /\.vue$/,
loader: 'vue-loader'
}]
},
plugins: [
new CleanWebpackPlugin(),
new DefinePlugin({
__VUE_OPTIONS_API__: false,
__VUE_PROD_DEVTOOLS__: false,
}),
new HtmlWebpackPlugin({
template: path.resolve("./web/index.html")
}),
new VueLoaderPlugin()
],
resolve: {
extensions: ['.js'],
alias: {
"#": path.resolve(__dirname, 'web')
}
},
devtool
};
};
I found the problem but I don't understand why webpack drops it.
Quote from https://webpack.js.org/guides/tree-shaking/
Note that any imported file is subject to tree shaking. This means if you use something like css-loader in your project and import a CSS file, it needs to be added to the side effect list so it will not be unintentionally dropped in production mode:
In my package.json I put
"sideEffects": false,
to be able to use treeshaking.
But I had to disable it in the loader rule
{
test: /\.(sa|sc|c)ss$/,
use: ['style-loader','css-loader','sass-loader'],
sideEffects: true <----
}

Webpack localIdentName mismatch on server and client in a React SSR application

I am trying to use Css modules with React SSR and i have added the following webpack config .
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
},{
test:/\.(s*)css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
modules: {
mode: 'local',
localIdentName: "[name]__[local]___[hash:base64:5]"
},
import: true,
importLoaders: true,
}
},
{
loader: "sass-loader",
}
],
},
]
},
this generates the following css files in the dist bundle
.Home\.module__Container___3B08 {
display: flex;
width: 600px;
height: 300px;
border: 2px solid red; }
Where as in my DOM the div has the following style
<div class="Home-module__Container___14QBF">
how do i make this correct ? and why is the webpack config different with the one in the browser
You have to add the CSS Modules configuration in .babelrc file under plugins. Then only the CSS generated will match with the one in your html.
.babelrc
"plugins": [
["react-css-modules", {
"generateScopedName": "[name]__[local]___[hash:base64:5]"
}]
],
Update :
Had to use css-modules-require-hook for server side CSS rendering and generic-names for generating hash in both client and server.
index.js
const hook = require( "css-modules-require-hook" );
const genericNames = require( "generic-names" );
const generate = genericNames( "[name]__[local]___[hash:base64:5]", {
context: process.cwd(),
});
// Scope Generator function.
hook( {
generateScopedName: ( c, path ) => {
return generate( c, path );
},
} );
webpack.config.js
const genericNames = require( "generic-names" );
const generate = genericNames( "[name]__[local]___[hash:base64:5]", {
context: process.cwd(),
});
const getLocalIdent = ( loaderContext, localIdentName, localName ) =>
generate( localName, loaderContext.resourcePath );
.....
{
loader: "css-loader",
options: {
modules: {
getLocalIdent,
},
},
},
For more, check this repo: https://github.com/ajayvarghese/react-ssr/tree/css-modules.
Note: Repo uses updated versions of babel packages.

How to import css file for into Component .jsx file

I am trying to use the react-day-pickers component like so but I can't figure out how to import the .css file and I keep getting the error:
Module parse failed:
/Users/qliu/Documents/workspace/AppNexus/pricing_ui/contract-ui/app_contract-ui/node_modules/react-day-picker/lib/style.css Unexpected token (3:0)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (3:0)
I have "css-loader": "^0.19.0", in my package.json installed and
here is my Calender.jsx file:
import React from "react";
import DayPicker, { DateUtils } from "react-day-picker";
import "../../../node_modules/react-day-picker/lib/style.css"; // <==== ERROR ON THIS LINE
export default class Calender extends React.Component {
state = {
selectedDay: null
};
handleDayClick(e, day, modifiers) {
if (modifiers.indexOf("disabled") > -1) {
console.log("User clicked a disabled day.");
return;
}
this.setState({
selectedDay: day
});
}
render() {
// Add the `selected` modifier to the cell of the clicked day
const modifiers = {
disabled: DateUtils.isPastDay,
selected: day => DateUtils.isSameDay(this.state.selectedDay, day)
};
return <DayPicker enableOutsideDays modifiers={ modifiers } onDayClick={ this.handleDayClick.bind(this) } />;
}
}
and this is my webpack.config.js:
var path = require('path');
var webpack = require('webpack');
var settings = require('./src/server/config/settings');
var LessPluginAutoPrefix = require('less-plugin-autoprefix');
module.exports = {
devtool: '#source-map',
resolve: {
extensions: ['', '.jsx', '.js']
},
entry: [
'webpack-hot-middleware/client',
'./src/client/index.jsx'
],
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
// to avoid duplicate import of React with react-addons-css-transition-group
'./React': 'React',
'./ReactDOM': 'ReactDOM',
config: 'config'
},
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js',
publicPath: '/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
lessLoader: {
lessPlugins: [
new LessPluginAutoPrefix({ browsers: ['last 2 versions'] })
]
},
module: {
loaders: [{
test: /\.(js|jsx)$/,
loaders: ['babel'],
exclude: /node_modules/
},
{
test: /\.less$/,
loader: 'style!css!less'
},
{
test: /\.json$/,
loader: 'json-loader'
}]
}
};
How are you compiling this? If it's webpack, you'd probably need to bring in the style-loader and include something like this in the module.loaders array in your webpack config:
{
test: /\.css$/,
loader: "style!css"
}
Update: With the webpack.config.js now provided, we can confirm it needed a change in the module.loaders array. OP was using a less loader and only checking for .less files, so the exact loader object should read:
{
test: /\.(less|css)$/,
loader: 'style!css!less'
}
As suggested by #Q Liu. Leaving the original bit as if someone comes here with an error importing a .css file, it's likely what they need.
I used the following in my webpack config and it worked:
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
JSX is not CSS. In your main HTML file, just add a <link> element to the CSS file, and the DOM generated by React will use it.

Resources