Storybook not applying styles from .module.css file - reactjs

I'm trying to use Storybook 6.1.20 for a very simple component in my project which uses Next.js, React and Typescript, however Storybook does not apply the styles that I have defined in my .module.css file. The styles are however applied when I start the project itself via npm run dev
This is my component that I want to display in Storybook (Rating.tsx):
import React from "react"
import ratingStyles from '../../../styles/Rating.module.css'
export interface RatingProps {
value: number
}
const Rating: React.FC<RatingProps> = ({value}) => {
return (
<span className={ratingStyles.text}>
{value}
</span>
)
}
export default Rating
The css file (Rating.module.css):
.text {
font-weight: 600;
font-size: 200px;
}
The story file (Rating.stories.tsx)
import React from 'react'
import { Story, Meta } from '#storybook/react';
import Rating, { RatingProps } from '../components/generic/Rating'
export default {
title: 'Rating',
component: Rating,
} as Meta
const Template: Story<RatingProps> = (args) => <Rating {...args}/>
export const FirstStory = Template.bind({})
FirstStory.args = {
value: 10
}

Related

TypeError: Cannot read property 'primaryColor' of undefined - Typescript, Styled Components (tsx)

I am running a few random react-library tests here and I keep running into the following TypeError: Cannot read property 'primaryColor' of undefined. The component renders. But I seem to be missing something. I have been scratching my head for hours on this one to no avail as is believe the types have already been set. I have included the relevant code below. Can you help me spot the issue? A 1000 thank yous in advance for your help.
TypeError: Cannot read property 'primaryColor' of undefined
22 | props.isBackgroundPrimary2Color
23 | ? "white"
> 24 | : props.theme.colors.primaryColor}
| ^
25 | 0%,
26 | white 190%
27 | );
import React from "react";
import styled from "styled-components";
interface PrimaryButtonProps {
children: string;
//note boolean props start with is to convey a question
isBackgroundPrimary2Color?: boolean;
}
//shortened this section for brevity
const PrimaryButtonStyles = styled.button<PrimaryButtonProps>`
background: linear-gradient(
45deg,
${(props) =>
props.isBackgroundPrimary2Color
? "white"
: props.theme.colors.primaryColor}
0%,
white 190%
);
`
const PrimaryButton = ({
isBackgroundPrimary2Color,
children,
...otherProps
}: PrimaryButtonProps) => {
return (
<PrimaryButtonStyles
isBackgroundPrimary2Color={isBackgroundPrimary2Color}
{...otherProps}
>
{children}
</PrimaryButtonStyles>
);
};
export default PrimaryButton;
As per the documentation here is my styled.d.ts file:
import "styled-components";
// extend the default theme using declaration merging theme into styled components
declare module "styled-components" {
export interface DefaultTheme {
fonts: {
family: string;
size: {
standard: string;
};
fontColors: {
primaryDisabledButtonFont: string;
primaryButton2Font: string;
};
};
colors: {
background: string;
primaryColor: string;
};
}
}
Here is my theme.tsx file:
import React from "react";
import { DefaultTheme } from "styled-components";
const theme: DefaultTheme = {
fonts: {
family: "sans-serif",
size: {
standard: "16px",
},
fontColors: {
primaryDisabledButtonFont: "#0000004d",
primaryButton2Font: "#64E2D3",
},
},
colors: {
background: "#f8f8f8",
primaryColor: "#645f43",
},
};
export { theme };
And finally the App.tsx file and index.tsx file:
import React, { useState } from "react";
import NavBar from "./components/NavBar/NavBar";
import Grid from "./layout/Grid";
import { Footer } from "./components/Footer/Footer";
import PrimaryButton from "./components/Buttons/PrimaryButton";
function App() {
return (
<div>
<PrimaryButton>Sign Up</PrimaryButton>
</div>
);
}
export default App;
and lastly my index.tsx file
import React from "react";
import ReactDOM from "react-dom";
import { ThemeProvider } from "styled-components";
// import "./index.css"; removed and moved to globalStyles
import App from "./App";
import { theme } from "./styles/theme";
import GlobalStyle from "./styles/GlobalStyles";
ReactDOM.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<GlobalStyle />
<App />
</ThemeProvider>
</React.StrictMode>,
document.getElementById("root")
);
What am I missing?
It's throwing that error because you probably did not add the theme decorator in your .preview file. This is how I solved this problem.
I created decorator.ts file with my theme decorator like this
import React from 'react'
import { DecoratorFn } from '#storybook/react'
import { ThemeProvider } from 'styled-components'
import { lightTheme, darkTheme } from '../styles/theme'
import { GlobalStyle } from '../styles/GlobalStyle'
const withTheme: DecoratorFn = (StoryFn, context) => {
return (
<ThemeProvider theme={lightTheme}>
<GlobalStyle />
<StoryFn />
</ThemeProvider>
)
}
export const globaDecorators = [withTheme]
Then inside the .preview.ts file, you can export the theme decorator like this
export const decorators = globaDecorators
This will work. You will have to restart your storybook dev server for it work

How do I bundle MUI theme with rollup

I've been working on pulling our existing react FE components out of our main repo, and into a separate repo that I am bundling with rollup. Our old code was using makeStyles and I have been switching that over to styled-components but still keeping the previous MUI theme. I've setup storybook and am wrapping that in styled components theme provider, in order to access the theme inside the styled components.
The structure looks like
components
\src
index.ts(imports and exports components)
\theme(MUI theme)
\components
\buttons
button.tsx(react button code)
index.ts(imports and exports button)
\lib(rollup spits this out)
Finally, to the question. After I bundle everything with rollup, I do an NPM install, and import it into a different project. The problem is, I'm not getting the proper theming in the imported components. Here is a somewhat simplified version of my button.
import React from "react";
import { Button as MaterialButton, ButtonProps } from "#material-ui/core";
import styled from "styled-components";
export interface MyButtonProps extends ButtonProps {
error?: boolean;
target?: string;
}
const StyledButton = styled(MaterialButton)`
&.error {
background: ${(props) => props.theme.palette.error.main};
color: #fff;
&:hover {
background: ${(props) => props.theme.palette.error.main};
}
}
`;
const Button = ({
error,
className,
...rest}: MyButtonProps) => {
className += error ? " error" : "";
return (
<StyledButton
{...rest}
className={className}
>
{children}
</StyledButton>
);
};
export default Button;
So, if I put error attribute on the button, I do get the correct color from my theme. However, if I put color="primary" I do not get the correct color. I also don't have any of my base styles from the theme.
I haven't been able to figure out how to get this theme into the components I'm bundling with rollup. Finally, here is my rollup config.
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import resolve from "#rollup/plugin-node-resolve";
import commonjs from "#rollup/plugin-commonjs";
import typescript from "rollup-plugin-typescript2";
import postcss from "rollup-plugin-postcss";
import svg from "rollup-plugin-svg-import";
const packageJson = require("./package.json");
export default {
input: "src/index.ts",
output: [
{
file: packageJson.main,
format: "cjs",
sourcemap: true,
},
{
file: packageJson.module,
format: "esm",
sourcemap: true,
},
],
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
svg(),
typescript({ useTsconfigDeclarationDir: true }),
postcss({
extensions: [".css"],
}),
],
};
For anyone who has stumbled upon this and is curious about the answer. Here is what I ended up doing. Hopefully this is the correct solution. One thing that is a bummer, I end up with MUI ThemeProvider giving me it's generated class names. This means in my styled components, I'll occasionally need to do this to target things [class^="MuiSvgIcon-root"], but maybe i can access the class through props, still messing with it. Anyway, here is the button.
import React from "react";
import { Button as MaterialButton, ButtonProps } from "#material-ui/core";
import styled from "styled-components";
import { ThemeProvider } from "#material-ui/core/styles";
import theme from "../../../theme";
export interface MyButtonProps extends ButtonProps {
error?: boolean;
target?: string;
}
const StyledButton = styled(MaterialButton)`
&.error {
background: ${theme.palette.error.main};
color: #fff;
&:hover {
background: ${theme.palette.error.main};
}
}
`;
const Button = ({
error,
size,
variant,
endIcon,
children,
className,
...rest
}: MyButtonProps) => {
if (className && error) {
className += " error";
} else if (!className && error) {
className = "error";
}
return (
<ThemeProvider theme={theme}>
<StyledButton
{...rest}
variant={variant}
size={size}
endIcon={endIcon}
className={className}
>
{children}
</StyledButton>
</ThemeProvider>
);
};
export default Button;
I guess each component will need to be wrapped.

how to load scss module as object?

According to Using SASS#3
By default, Laravel Mix will handle any import for SASS files. This means that anytime you do a import styles from 'blabla.scss', Mix will fetch that object and parse its CSS.
in my case CSS Modules can not operate properly with Laravel Mix (4.1.4)/ React 16.2.0
// webpack.mix.js
const mix = require('laravel-mix');
let rules = [
{
test: /\.ico$/,
loaders: ['file-loader']
}
];
mix.react('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.webpackConfig(
{
module: {
rules: rules
}
}
);
mix.browserSync('localhost:8000');
I'm trying to import a SCSS module like this:
my React component Progress.js
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {
Progress as BsProgress
} from 'reactstrap';
import classes from './../../../sass/Progress.scss';
const Progress = (props) => {
const { children, slim, className, ...otherProps } = props;
const progressClass = classNames(className, {
[classes['slim']]: slim
});
return (
<BsProgress className={ progressClass } { ...otherProps }>
{ !slim && children }
</BsProgress>
);
};
Progress.propTypes = {
slim: PropTypes.bool,
className: PropTypes.string,
children: PropTypes.node
};
export { Progress };
Just try importing a .module.scss
import classes from './../../../sass/Progress.module.scss'

storybook doesn't find components based on chakra-ui

i was trying storybook and chakra ui.
Down below is how I structured my folders,
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials",
"#storybook/preset-create-react-app"
]
}
and this is my storybook config.
unluckly i receive an error, it seems like storybook doesn't find my components.
can someone help me to understand what's wrong ?
Why storybook doesn't find my components ?
-----------Update------------
this is the story that I tried to added
import React from 'react';
import {
Menu,
MenuButton,
} from '#chakra-ui/core';
export function MenuComp() {
return (
<Menu>
<MenuButton>Games</MenuButton>
</Menu>
);
}
MenuComp.storyName = 'test';
I started with an easier component and followed strictly the tutorial
i will wrote here a little guide that, I hope, will simplify the reader life.
1.I wrote a simple component in path "src/components/CustomButton"
import React, { FC } from 'react';
import { Button } from '#chakra-ui/core';
interface ButtonProps {
size: string | any;
variantColor: string;
}
const CustomButton: FC<ButtonProps> = ({ size, variantColor }) => (
<Button variantColor={variantColor} size={size}>
Button
</Button>
);
export default CustomButton;
2.I added the relative story component in "src/stories/CustomButton.stories.tsx
import React from 'react';
import CustomButton from 'components/CustomButton/index';
export default {
component: CustomButton,
title: 'CustomButton',
};
const Template = (args:any) => <CustomButton {...args} />;
export const Default: any = Template.bind({});
Default.args = {
size: 'md',
variantColor: 'green',
};
3.At this point it's really important configure your file preview.js that you will find the folder ".storybook", thanks to this commit chakraUiCommit i figured out how to include the theme provider in every storybook component
import React from 'react'
import {addDecorator} from '#storybook/react'
import {ThemeProvider, CSSReset} from '#chakra-ui/core'
addDecorator((storyFn) => (
<ThemeProvider>
<CSSReset />
{storyFn()}
</ThemeProvider>
))
well, at this point i solved my problem, and this is my final result

How to change a SASS variable value using React Js?

Before making it duplicate question please make sure you read my question
I am asking this question in 2019, Where React Js documentation specify we can use SASS in our react project here's link
I want to switch between light theme and dark theme using dynamic variable which is control by user click
My React Code
import React from 'react';
import './App.scss';
class App extends React.Component {
render() {
return (
<div className="Home">
I’m slow and smooth
<button onClick={() => console.log()}>Change theme</button>
</div>
);
}
}
export default App;
My SASS code:
$theme: light; // I want to control this variable
$bgcolor: #222222;
#if($theme== light) {
$bgcolor: white;
}
#else {
$bgcolor: black;
}
.Home {
background-color: $bgcolor;
height: 100vh;
}
in case you are still interested, you can kind of change a sass variable by instead using css variables
:root {
--app-primaryColor: #f49ad1;
--app-secondaryColor: #211f1e;
}
Then use these variables in your scss files
.button {
background-color: var(--app-primaryColor);
color: var(--app-secondaryColor);
}
and update them using React
document.documentElement.style.setProperty('--app-primaryColor', '#ffae00')
Here is a (almost) full example using react and redux. A setTheme action is used to update colors from the document root element. This way you can also configure your theme directly from your react root tag props. These props will be set as the initial state.
// index.html
<div
id="app"
primaryColor="red"
secondaryColor="#f2f2f2"
/>
// css-variables.scss
:root {
--app-primaryColor: #f49ad1;
--app-secondaryColor: #211f1e;
}
// app.module.scss
.button {
background-color: var(--app-primaryColor);
color: var(--app-secondaryColor);
}
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import './css-variables'
import App from './app'
import configureStore from './configureStore'
const rootElement = document.getElementById('app')
//Here you could extract your theme variables from your rootElement props and set it as an initial redux state
const initialProps = {}
const store = configureStore(initialProps)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement)
// app.js
import React from 'react'
import { connect } from 'react-redux'
import { setTheme } from './actions'
import styles from './app.module'
class App extends React.Component {
componentDidMount() {
//In case you have an initial state set from your rootElement
const { theme, setTheme } = this.props
setTheme(theme)
}
generateTheme() {
const primaryColors = ['#ffae00', '#f49ad1', '#d0666b', '#7c6cd0', '#6cd09d', '#d0ba6c']
const secondaryColors = ['#4c4c4e', '#2f2f2f', '#dcdcdc', '#fff']
return {
primaryColor: primaryColors[Math.floor(Math.random() * primaryColors.length)]
secondaryColor: secondaryColors[Math.floor(Math.random() * secondaryColors.length)]
}
}
onButtonClick() {
const theme = this.generateTheme()
this.props.setTheme(theme)
}
render() {
return (
<div className="{styles.button}" onClick={this.onButtonClick.bind(this)}>
Change theme
</div>
)
}
}
const mapStateToProps = (state) => ({
theme: state.theme,
})
export default connect(mapStateToProps, { setTheme })(App)
// actions.js
export const setTheme = theme => dispatch => {
//You change your theme vars directly from the root element
Object.keys(theme)
.filter(prop => typeof theme[prop] !== 'undefined' && theme[prop] !== null)
.forEach(prop => document.documentElement.style.setProperty(`--app-${prop}`, theme[prop]))
dispatch({
type: 'THEME/SET',
payload: theme
})
}

Resources