MUI specificity is different in storybook than in application - css

I noticed that my styles on a component look right in storybook but not in the running application. The component code is at the bottom of this post.
All the components are from MUI, and looking at the "Computed" tab of the Chrome inspector, I can see that the Avatar components are getting their styles differently in the two environments -- the priorities of the classes are swapped:
In the app:
In the storybook
Wrapping the styles in && fixes the problem, and I've used this solution before for MUI style issues, but I've never come across the case where it behaves differently in the app vs storybook.
Here's my main decorator in preview.tsx
export const decorators: ComponentStoryObj<React.FC>['decorators'] = [
story => {
return (
<>
<header>
<link
href='https://fonts.googleapis.com/css?family=Roboto:100,200,300,400,500,600,700'
rel='stylesheet'
/>
<link
href='https://fonts.googleapis.com/css?family=Roboto+Condensed:400,500,600,700'
rel='stylesheet'
/>
</header>
<AppWrapperWithUser>{story()}</AppWrapperWithUser>
</>
);
},
];
Here's the AppWrapper used above, which also wraps the application itself (so that the styles match)
import { ThemeProvider as MuiThemeProvider } from '#mui/material/styles';
import { ThemeProvider as StyledThemeProvider } from 'styled-components';
import { globalStyles, theme } from './theme';
type Props = {
store?: ReturnType<typeof createStore>;
initialState?: RootState;
};
export const AppWrapper: React.FC<Props> = ({ children, store, initialState }) => {
const appStore = store || createStore(initialState);
return (
<MuiThemeProvider theme={theme}>
<StyledThemeProvider theme={theme}>
{globalStyles}
<Provider store={appStore}>{children}</Provider>
</StyledThemeProvider>
</MuiThemeProvider>
);
};
And the component in question:
const S = {
Wrapper: styled(Paper)`
padding: 32px;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;Ø
overflow: hidden;
`,
Pane: styled.div<{ centered?: boolean }>`
display: flex;
flex-direction: column;
gap: 4px;
align-items: ${p => p.centered && 'center'};
`,
Subtitle: styled(Typography).attrs({ variant: 'body2' })`
font-size: 14px;
text-transform: uppercase;
`,
Title: styled(Typography)`
&& {
font-size: 28px;
font-weight: 700;
}
`,
Avatar: styled(Avatar)`
height: 84px;
width: 84px;
box-sizing: content-box;
border: 2px solid rgba(0, 0, 0, 0.1);
`,
TeamLogoWrapper: styled.div`
display: flex;
align-items: center;
height: 100%;
width: 222px;
`,
TeamLogo: styled(Avatar)`
height: 222px;
width: 222px;
box-sizing: content-box;
opacity: 0.3;
border: 2px solid rgba(0, 0, 0, 0.1);
position: absolute;
`,
};
const PlayerDetailHeader = ({ player }: { player: ProcessedPropUIPlayer }) => {
const { ... } = player;
const infoStr = [teamName, `#${uniformNumber}`, position].join(' | ');
return (
<S.Wrapper data-testid={`PlayerDetailHeader-${playerId}`}>
<S.Avatar alt={playerName} src={playerImg} />
<S.Pane>
<S.Title>{playerName}</S.Title>
<S.Subtitle>{infoStr}</S.Subtitle>
</S.Pane>
<S.Pane centered>
<S.Title>{openProps}</S.Title>
<S.Subtitle>Open Props</S.Subtitle>
</S.Pane>
<S.Pane centered>
<S.Title>{dollarFormatter(openAction)}</S.Title>
<S.Subtitle>Open Action</S.Subtitle>
</S.Pane>
<S.TeamLogoWrapper>
<S.TeamLogo alt={teamName} src={teamImg} />
</S.TeamLogoWrapper>
</S.Wrapper>
);
};

Related

react styled-component nested item's props

My problem is about props. I want to use nested components with props in styled-components. For example:
const MenuItem = ({ item }) => {
const router = useRouter();
const [isOpen, setIsOpen] = useState(false);
const isActive = router?.asPath === item?.path;
return (
<MenuItemWrapper key={item?.slug} onClick={() => setIsOpen(!isOpen)}>
<Link href={item?.path}>
<InnerMenuItem isActive={isActive}>
{item?.prefix && <Prefix>{item?.prefix}</Prefix>}
{item?.label}
{item?.children && <RightArrow isOpen={isOpen} />}
</InnerMenuItem>
</Link>
<Children>
{
item?.children?.map((child) => <MenuItem item={child} />)
}
</Children>
</MenuItemWrapper>
);
};
export default MenuItem;
this is MenuItem component. I use MenuItem component as a recursion component.
in styled-component i tried this but it doesnt work. I want to apply different style in Children > InnerMenuItem but it not working
export const Children = styled.div`
display: flex;
flex-direction: column;
margin-left: 65px;
${MenuItemWrapper} {
font-size: 16px;
padding: 9px 0;
&:not(:first-child) {
border-top:none;
}
}
${InnerMenuItem} {
${({ isActive }) => // HOW CAN I USE THIS PROPS HERE
isActive &&
css`
color: orange
`};
}
`;
from styled components official documentations:
"If the styled target is a simple element (e.g. styled.div), styled-components passes through any known HTML attribute to the DOM. If it is a custom React component (e.g. styled(MyComponent)), styled-components passes through all props."
example :
const Input = styled.input`
color: ${props => props.inputColor || "palevioletred"};
`;
return(
<Input inputColor="rebeccapurple" />
)
another way is by Extending Styles, example :
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
return(
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);
more about styled-components read here
have you tried
${InnerMenuItem} {
color: ${({isActive})=> isActive ? 'orange' : undefined}
};

How to make container expand up instead of down when component added?

I have a React component that asks user to upload profile photo. In the same view, the user can see a greyed out 'next' button. When the photo is uploaded and displayed, the user sees a horizontal scrolling bar to adjust photo size that comes between the photo and next button. My goal is to keep the next button in view at all times but also keep all elements of the component in the same order. Right now, a user has to scroll down to see the 'next' once the image is loaded. I've been experimenting with flex and position and even a ternary in the JSX to add paddingBottom when !isLoading. Can someone give a good direction so that the user doesn't have to scroll down to see the next' button?
Here is my component:
import React, { useContext, useState, useEffect } from 'react';
import styled from 'styled-components';
import Typography from 'components/Typography';
// import { useMediaQuery } from 'beautiful-react-hooks';
import { medium } from 'constants/mediaQueries';
import AppButton from 'components/AppButton';
import useFetcher from 'hooks/useFetcher';
import { getMeApi } from 'utils/apiRoutes';
import Loader from 'components/Loader';
import routePaths from 'containers/Router/routePaths';
import { useHistory, useLocation } from 'react-router-dom';
import { UserContext } from 'containers/context/UserContext';
import checkmarkImage from 'assets/images/checkmark.svg';
import { useDispatch } from 'react-redux';
import BackArrowGrid from 'components/Auth/BackArrowGrid';
import UploadImageInput from 'components/UploadImageInput';
import { setPlainLogo, setInitialLogo } from '../../store/header/actions';
// const MOBILE = 250;
// const DESKTOP = 356;
const Container = styled.div`
width: 100%;
position: relative;
align-items: center;
justify-content: flex-end;
display: flex;
flex-direction: column;
border: pink solid 2px;
`;
const ButtonContainer = styled.div`
margin-top: 32px;
display: flex;
align-items: center;
flex-direction: row;
div {
margin-bottom: 16px;
}
img {
margin-left: 8px;
}
`;
const Title = styled(Typography)`
font-size: 40px;
text-transform: uppercase;
margin-bottom: 64px;
#media (min-width: ${medium}) {
margin-bottom: 64px;
}
`;
const ButtonContent = styled.span`
display: flex;
align-items: center;
`;
const Form = styled.div`
width: 100%;
display: flex;
flex-direction: column;
align-self: flex-end;
border: yellow solid 2px;
.empty {
display: none;
}
#media (min-width: ${medium}) {
flex-direction: column;
align-items: center;
min-height: auto;
.empty {
display: block;
width: 118px;
}
}
`;
const UploadPicture = () => {
const { fetcher, error, isLoading } = useFetcher();
const { setUserFromLogin } = useContext(UserContext);
const history = useHistory();
const location = useLocation();
// const isDesktop = useMediaQuery(`(min-width: ${medium})`);
const dispatch = useDispatch();
const [image, setImage] = useState({});
const uploadImage = async () => {
const result = await fetcher({
url: getMeApi,
method: 'POST',
body: {
photo: image.croppedImage.source,
},
});
if (result.errors) {
return;
}
setUserFromLogin(result.user);
if (location.profile) {
history.push(routePaths.Profile);
} else {
history.push(routePaths.Welcome);
}
};
useEffect(() => {
dispatch(setPlainLogo());
return () => dispatch(setInitialLogo());
}, []);
const onPictureChange = newImage => {
setImage(prevImage => ({
...prevImage,
...newImage,
}));
};
return (
<>
<BackArrowGrid>
<Container>
<Title h1>Profile photo</Title>
<Form>
<div className="empty"> </div>
<UploadImageInput
onChange={onPictureChange}
image={image}
placeholder={
"Upload a profile photo. This is how you'll appear to other Color TV users."
}
height={356}
width={356}
horizontalPadding={60}
aspect={1}
round
/>
<ButtonContainer>
{error && (
<Typography error>
Error while uploading the image, please try again.
</Typography>
)}
<AppButton
primary={!!image.croppedImage}
disabled={isLoading || !image.croppedImage}
onClick={uploadImage}
noBorder
>
<ButtonContent>
{isLoading ? (
<Loader className="center" size={30} />
) : (
'Next'
)}
{!isLoading && !!image.croppedImage && (
<img src={checkmarkImage} alt="checkmark_icon" />
)}
</ButtonContent>
</AppButton>
</ButtonContainer>
</Form>
</Container>
</BackArrowGrid>
</>
);
};
export default UploadPicture;
Since this component is part of a larger code base, I'm not sure how to run a snippet of it but here are 2 photos - before and after. Once the photo is uploaded, the next button drops out of view because the slider component is added in. I want to next button to stay in view no matter how the browser size.

props is not applied although passed it to styled component

What I want to do
Changing colors depending on props passed from a component using styled component.
Problem
SmallButton component definitely gets props but it doesn't change like props indicates.
I would like to change styles of SmallButton depending on props that a component gives to the button component.
SmallButton has these props but it doesn't change at all.
I would like you to teach me how to solve it.
Thank you very much.
=== ==== === ===
My code is like this.
Header.jsx
render() {
return (
<>
<Wrapper>
{/* CSS Grid( 1 : 1 : 1) 左 */}
<Image src={Logo} alt="" />
{/* CSS Grid( 1 : 1 : 1) 中央 */}
<SearchBox />
{/* CSS Grid( 1 : 1 : 1) 右 */}
{this.props.isAuthenticated ? (
<>
<div>
<MessageToUserDiv>
<span>Hello {this.state.loginUser.username}</span> 
<LogoutButton onClick={this.handleLogout}>Logout</LogoutButton>
</MessageToUserDiv>
<AuthButtonDiv>
<SmallButton
btn_border="#466A80"
btn_back="#466A80"
btn_text_color="#D9F1FF"
btn_name="Post"
btn_click={this.jumpToPostGive}
/>
<SmallButton
btn_border="#466A80"
btn_back="#8DD6FF"
btn_text_color="#466A80"
btn_name="Info"
btn_click=""
/>
</AuthButtonDiv>
</div>
</>
) : (
<>
<div>
<p>Hello Guest</p>
<AuthButtonDiv>
<SmallButton btn_name="Register" btn_click={this.jumpToRegister} />
<SmallButton btn_name="Login" btn_click={this.jumpToLogin} />
</AuthButtonDiv>
</div>
</>
)}
</Wrapper>
</>
);
}
}
const Wrapper = styled.div`
background-color: #8dd6ff;
width: 100%;
display: grid;
grid-template-columns: 1fr 2.3fr 1fr;
padding: 10px 5px 5px 5px;
`;
const Image = styled.img`
width: 230px;
margin-top: 5px;
`;
const MessageToUserDiv = styled.div`
font-size: 13px;
text-align: right;
height: 20%;
`;
const LogoutButton = styled.button`
color: #6e787f;
width: 30%;
`;
const AuthButtonDiv = styled.div`
display: flex;
justify-content: space-around;
align-items: flex-end;
height: 80%;
`;
SmallButton.jsx
class SmallButton extends Component {
constructor(props) {
super(props);
}
render() {
return (
<StyledButton
type={this.props.btn_type}
onClick={this.props.btn_click}
onSubmit={this.props.btn_submit}
disabled={this.props.btn_disable}
>
{this.props.btn_name}
</StyledButton>
);
}
}
const Colors = {
main: '#8DD6FF',
characters: '#6C7880',
subcolor1: '#D9F1FF',
accent1: '#70AACC',
accent2: '#466A80',
};
const StyledButton = styled.button`
font-size: 1.18em;
border-radius: 7px;
height: 45px;
width: 100px;
padding: 2px 3.5px;
border: solid 2.5px;
border-color: ${(props) => props.btn_border};
background: ${(props) => props.btn_back};
color: ${(props) => props.btn_text_color};
`;
spread the rest for your props to StyledButton like this:
<StyledButton
type={this.props.btn_type}
onClick={this.props.btn_click}
onSubmit={this.props.btn_submit}
disabled={this.props.btn_disable}
{...this.props}
>
{this.props.btn_name}
</StyledButton>

CSS to React components

I started rewriting CSS into React Components but i've encountered a problem, I do know how to do this (using styled-components btw ) :
You have 5 ways to styling component in React.
Every approach have pros & cons(personally I use 4-th ways.)
1.Inline Css
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
2. CSS in JS
const Button = (props) => (
<button className={ 'large' in props && 'large' }>
{ props.children }
<style jsx>{`
button {
padding: 20px;
background: #eee;
color: #999
}
.large {
padding: 50px
}
`}</style>
</button>
)
/* Creates a regular button */
<Button>Hi</Button>
/* Creates a large button */
<Button large>Big</Button>
3. Styled Components
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
render(
<Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>
);
4. Css Modules (scss || sass)
#width: 10px;
#height: #width + 10px;
#header {
width: #width;
height: #height;
}
5. Stylable - https://github.com/wix/stylable
import React from 'react';
import styled, {css} from 'styled-components';
const Info = styled.div`
display: flex;
// etc.
${props => props.title && css`font-size: 15px`}
`
const Box = styled.div`
&:first-of-type {
// some more css
}
`
// usage
<Info title>some info</Info>
I recommend you follow the official docs as stated in comments by #Mikkel

Styled component does not inherit styles when using "as" attribute

I'm using styled-system with styled components and have a basic case like this:
const buttonFont = {
fontFamily: "Chilanka"
};
// style a boilerplate for text
const Text = styled.div`
${typography}
${color}
`;
// button blueprint
const Button = ({ children, ...rest }) => {
return (
<Text as="button" {...buttonFont } {...rest}>
{children}
</Text>
);
};
// styled button using button
const StyledButton = styled(Button)`
color: white;
background-color: gray;
padding: 10px 20px;
border: 2px solid black;
`;
// When using "as" this component does not includes buttonFont styles
const StyledLabel = styled(StyledButton).attrs({
as: "label"
})``;
I want to create a StyledLabel which will inherit all styles from StyledButton, but change tag to label. But StyledLabel does not get the buttonFont styles when using "as" attribute.
Please see live example here: demo
I'm not sure what your end goal is, but these 2 examples worked in terms of inheritance. However, they might not help with your plan for composition:
import React from "react";
import styled, {css} from "styled-components";
import { typography, color } from "styled-system";
import ReactDOM from "react-dom";
import "./styles.css";
const buttonFont = {
fontFamily: "Chilanka"
};
const Text = styled.div`
${typography}
${color}
margin: 24px;
`;
const StyledButton = styled(Text)`
color: white;
background-color: gray;
padding: 10px 20px;
border: 2px solid black;
`;
const StyledLabel = styled(StyledButton)`
color: yellow;
`;
const __Text = styled.div`
${typography(buttonFont)}
${color}
margin: 24px;
`;
const __StyledButton = styled(__Text)`
color: white;
background-color: gray;
padding: 10px 20px;
border: 2px solid black;
`;
const __StyledLabel = styled(__StyledButton)`
color: yellow;
`;
function App() {
return (
<div className="App">
<StyledButton as="button" {...buttonFont}>styled button</StyledButton>
<StyledLabel as="label" {...buttonFont}>Does inherit styled font with "as"</StyledLabel>
<br />
<br />
<br />
<__StyledButton as="button">styled button</__StyledButton>
<__StyledLabel as="label">Does inherit styled font with "as"</__StyledLabel>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Resources