Rendered Component doesn't show in storybook - reactjs

I want to render MUI Button Component in my storybook and then change MUI attributes(variant, color, disabled, etc.) from storybook. I could get result when working on simple plain html button but it doesn't work on MUI one. The current result is it doesn't show any error in terminal, nor in browser. When I type something in args it's visible on Storybook Controls but Button itself doesn't show up itself. Here is the code of MyNewButton.tsx file.
import Button from "#mui/material/Button"
export const MyNewButton = () => {
return <Button />
}
and here is the code of MyNewButton.stories.tsx
import { ComponentMeta, ComponentStory } from "#storybook/react"
import { MyNewButton } from "./MyNewButton"
export default {
title: "MyComponents/MyNewButton",
component: MyNewButton,
} as ComponentMeta<typeof MyNewButton>
const Template: ComponentStory<typeof MyNewButton> = (args) => (
<MyNewButton {...args} />
)
export const First = Template.bind({})
First.args = {
label: "MyNewButton",
backgroundColor: "red",
}
and Here is the storybook screenshot.

So, I can see you are overlooking some basic steps and I'm going to point them out to you. Your Storybook is rendering fine.
First, you are rendering a button from MUI without a label/text. You are supposed to have an opening tag and a closing tag for the button.
so, it should be <Button>Hello World!</Button> and not <Button />.
After fixing that, the next step is to use your args in your MyNewButton component so that storybook will know what to change your background color to.
import { FC } from "react";
type ButtonProps = {
backgroundColor: string;
label: string;
};
export const MyNewButton: FC<ButtonProps> = (props) => {
const { backgroundColor, label } = props;
const style = {
backgroundColor,
};
return (
<Button variant="contained" style={style}>
{label}
</Button>
);
};
Your button should be rendering fine correctly now and to make change the background in your app, you just have to pass in the props like so:
<MyNewButton backgroundColor={"green"} label={"My New Button"} />

Related

Styled-Components Not Loading Until Page Refreshed

I'm facing an issue where the css from my styled-components aren't loading until we refresh. I pared down a specific file until there is just a button. I kept the file flow, but stripped out practically everything but the button and the green background color.
I would expect that on the first load, the button has a green background. Instead, it has a transparent background. But it does have the green background after refresh.
If additional context helps, we are using material themes at the moment but are transitioning to styled components. This specific page doesn't reference the MUI themes in any way I can tell. That said, I also am getting the multiple #mui/styles warning in the console. (My package.json doesn't directly use #mui/styles, so I am assuming it's a dependency)
I'm currently stumped and would happily take any suggestions on what to check.
It looks like there are several instances of `#material-ui/styles` initialized in this application.
This may cause theme propagation issues, broken class names, specificity issues, and makes your application bigger without a good reason.
See https://material-ui.com/r/styles-instance-warning for more info.
index.tsx
import PrimaryButton from "components/Shared/Button/PrimaryButton";
export default function ReviewPurchaseOrderPage() {
return (
<PrimaryButton
text={"Approve"}
onClick={() => console.log("test for SO question")}
/>
);
}
PrimaryButton.tsx
import ActionButton from "components/Shared/Button/ActionButton";
interface Props {
text: string;
onClick: () => void;
}
export default function PrimaryButton({
text,
onClick,
}: Props) {
return (
<ActionButton
text={text}
onClick={onClick}
/>
);
}
ActionButton.tsx
import { Button } from "#material-ui/core";
import styled from "styled-components";
const StyledActionButton = styled(Button)`
background-color: #769362;
`;
interface Props {
text: string;
onClick: () => void;
}
export default function ActionButton({
text,
onClick,
}: Props) {
return (
<StyledActionButton
onClick={onClick}
>
{text}
</StyledActionButton>
);
}

Second class of multiple classes can't override style

button.module.css
.general_button {
width: 100%;
}
button.js
import React from 'react';
import styles from './button.module.css';
const GeneralButton = ({ text, className}) => {
return (
<button className={`${styles.general_button} ${className}`}>
<p className={styles.text}>{text}</p>
</button>
);
};
export { GeneralButton };
app.module.css
.next_btn {
width: 35%;
}
app.js
import React from 'react';
import classes from './app.module.css';
import { GeneralButton } from './components/Buttons';
const App = () => {
return (
<div>
<GeneralButton className={classes.next_btn} text='next' />
</div>
)
}
export default App;
When I used button component from app, button component is working with 'width:100%'. I would like to be button component is with 'width: 35%'.
This is when I inspect browser.
This is when I inspect browser.
Since both classes are equally specific, your app chooses whichever comes later in the stylesheet. Unfortunately you have much control over this order since it is likely generated by webpack (or whatever bundler you happen to be using).
One of the main benefits of using modular css is to avoid having to fight specificity battles such as this, so I would suggest reworking the button component slightly - something like this might work:
const GeneralButton = ({ text, variant }) => {
let buttonClass = styles.general_button;
if (variant === "next") {
buttonClass = styles.next_button;
}
return (
<button className={buttonClass}>
<p className={styles.text}>{text}</p>
</button>
);
};
The advantage here is that the button lays out the options for how to present it and the parent component just selects the type it needs. This makes it safer to re-work the button component in the future since it encapsulates all of it's states.

How do you use Pseudo-classes in React using css modules?

The example I worked on is the following:
I have a button component that receives the background color as props. The received color will be the background that the button must have when hovering.
Second question:
The only way to use props in css, using css modules, is to apply the inline style in the js file where you declare the component?
Below I insert a code base (in the example the background color is applied by default):
import Button from "./Button";
export default function App() {
return <Button hoverColor={"red"} />;
}
...
export default function Button({ hoverColor }) {
const buttonStyle = {
backgroundColor: hoverColor
};
return <button style={buttonStyle}>click me!</button>;
}
Thanks
You may use React useState Hook to achieve the desired functionality: (Your Button component should look like this)
import React, { useState } from "react";
export default function Button({ hoverColor }) {
const [color, setColor] = useState("");
const buttonStyle = {
backgroundColor: color
};
return (
<button
style={buttonStyle}
onMouseOver={() => setColor(hoverColor)} //set the color when user hovers over the button
onMouseOut={() => setColor("")} //set color to an empty string otherwise
>
click me!
</button>
);
}

Changing button label on click with grommet?

Does anyone have any insight into changing button label on click when using grommet UI and styled components? Is it easier to just use a custom button rather than grommets?
Here are a few examples on how to make the Button label change onClick action:
import React, { useState } from "react";
import { render } from "react-dom";
import { grommet, Box, Button, Grommet, Text } from "grommet";
const App = () => {
const [label, setLabel] = useState(1);
const [name, setName] = useState("shimi");
const flipName = name => {
return name === "shimi" ? setName("shai") : setName("shimi");
};
return (
<Grommet theme={grommet}>
<Box pad="small" gap="small" width="small">
// label is a number that is being increased on every click event
<Button
label={label}
onClick={() => {
setLabel(label + 1);
}}
/>
// label string is controlled with external logic outside of the button.
<Button
label={<Text weight="400">{name}</Text>}
onClick={() => {
flipName(name);
}}
/>
</Box>
</Grommet>
);
};
render(<App />, document.getElementById("root"));
In addition to the examples above, in Grommet, you don't have to use the label prop and you can leverage the Button children to control the way your Button display.

React JS // Editing style of one component from another component

My goal is to create a basic app that allows me to change the style of one component with an action from another component.
Lets assume I have a <Btn/> component and a <Box/> component and when the button is clicked, I want to change the background color of the box. <Btn/> and <Box/> have the common ancestor of <App/> but are both at different levels in the component tree.
Btn.js
import React from 'react'
function Btn() {
const handleClick = (e) => {
//...
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
export default Btn
Box.js
import React from 'react'
function Box() {
return (
<h1>
Hello World!
</h1>
);
}
export default Box
I do not want to use prop drilling (with style setting/getting functionality in the <App/> component) to achieve this. I have also deliberately left out component styling as I am open to whichever styling option is best to solve this problem.
What would be the best way to go about this? (I'm open to using Context, Redux or another library if it is appropriate.)
The simplest way of doing this is with Context, as you're using function components not classes the documentation you'll need is useContext https://reactjs.org/docs/hooks-reference.html#usecontext. You still have to define the prop and "setter" function at the app level or at a component called at the app level, but with context you don't have to pass the props all the way down.
To take their example and adapt it to your use case would go something like this. (Working sample: https://codesandbox.io/s/stackoverflow-answer-7hryk)
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
const [stateTheme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme: themes[stateTheme], setTheme: setStateTheme }}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ToggleButtons />
<ThemedButton />
</div>
);
}
function ToggleButtons() {
const { setTheme } = useContext(ThemeContext);
return (
<div>
<button onClick={() => setTheme('light')}>Light Theme</button>
<button onClick={() => setTheme('dark')}>Dark Theme</button>
</div>
);
}
function ThemedButton() {
const { theme } = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}

Resources