Receiving prop in Material UI and conditional rendering - reactjs

This is a styled-component code block, it gets an img prop and based on the prop, it decides to load a specific image.
export const DialogBanner = styled.div`
min-height: 250px;
margin-bottom: 20px;
${({ img }) =>
img
? `background-image: url(${img});`
: `background-image: url("/img/banner.jpg");`}
background-position: center;
background-size: cover;
filter: contrast(85%);
`;
<DialogBanner img={food.img} />
How can I write the same thing using Material UI's useStyles?

EDIT: This is now legacy and was written for version 4
Using the latest version of Material-UI you would use makeStyles which creates a hook for use within your component. https://material-ui.com/styles/basics/#adapting-based-on-props
For Example.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
// Create our styles using passed props
// These are `functors` it seems
const useStyles = makeStyles({
root: {
backgroundImage: ({ img }) =>
img
? `url(${img});`
: `url("/img/banner.jpg");`}
},
});
function DialogBanner(props) {
// Separating out our children
const { children, ...rest } = props
// Prop containing our `img`
const classes = useStyles(rest);
retun <div className={classes.root>{children}</div>
}

Related

Conditional styled components effect all instances when nested

If i use a prop condition on the main component it will work per instance.
So for example if i have:
const Div = styled.div<Props>`
${(props) => {
return css`
${props.test && "border: 5px solid red;"};
`;
}}
`;
only components which have the test prop will have this ugly border :)
But if i use this same condition on a nested css rule like that:
const Div = styled.div<Props>`
${(props) => {
return css`
.tabletScreen & {
${props.test && "border: 5px solid red;"};
}
`;
}}
`;
All instances of this component will have this ugly border if one of the components has this test prop.
When inspect it i see that all instances of the component gets the same class generated, so the this:
.tabletScreen .sc-jcFjpl {
border: 5px solid red;
}
Is implemented to all instances.
But on the first case (when the condition is not nested) the component with the test prop will get another class so it won't override the others.
How can i fix this?
Use && instead of & and it'll be scoped to that stylistic instance of the component. Single ampersand refers to the "static component class".
More info here: https://styled-components.com/docs/basics#pseudoelements-pseudoselectors-and-nesting
import React from "react";
import { render } from "react-dom";
import styled, { css } from "styled-components";
import Wrapper from "./Wrapper";
import Title from "./Title";
// Render these styled components like normal react components.
// They will pass on all props and work
// like normal react components – except they're styled!
const Div = styled.div`
${(props) => {
return css`
.tabletScreen {
border: ${props.test && '5px solid red'};
}
`;
}}
`;
const App = () => (
<Wrapper>
<Div test>
Without Test
<div className="tabletScreen">TabletScreen</div>
</Div>
</Wrapper>
);
render(<App />, document.getElementById("root"));

Access props inside the styled component using MUI

I'm trying to create a custom button component using MUI on a React JS project. I've checked the docs and I discovered that I can do this using styled components. All worked well with the code presented below. The problem is that I want to create a more "customizable" component. I have inside my theme file 2 sets of colors (primary and secondary). The fact is that I want to create a button that is able to take a prop for this set of colors (primary / secondary).
import * as React from 'react';
import ButtonUnstyled, {
buttonUnstyledClasses,
ButtonUnstyledProps,
} from '#mui/core/ButtonUnstyled';
import { styled } from '#mui/system';
import { theme } from '../../theme';
const CustomButtonRoot = styled('button')(`
background-color: ${theme.palette[props.type].main};
padding: 15px 20px;
border-radius: 10px;
color: #fff;
`);
interface TodoButtonProps {
unstyledProps: ButtonUnstyledProps,
type?: 'primary' | 'secondary'
}
function CustomButton(props: TodoButtonProps) {
return <ButtonUnstyled {...props} component={CustomButtonRoot} />;
}
export default CustomButton
The question is: How I can include this prop inside the styled component code?
Pass a callback. In this callback the first argument is the props of the styled component. You can also use the style object instead of template string. More detail in this answer.
const CustomButtonRoot = styled("button")(
({ theme, myColor }) => `
padding: 15px 20px;
border-radius: 10px;
color: ${myColor};
`
);
<CustomButton myColor="red">abc</CustomButton>

What is the best way to typing React Components when they styling with styled-components

I am migrating a project to Typescript. I have a component styled with styled-components and I need to override it with styled-components on some other component. My component looks like this.
This component aims to render an image and if image not exist, returns a Fallback element styled with styled-components.
import React, { useState } from 'react'
import styled from 'styled-components'
export const Fallback = styled.div`
padding-bottom: 56%; /* 16/9 ratio */
background-color: #e5e5e5;
`
export const Image = styled.img`
image-rendering: pixelated;
image-rendering: crisp-edges;
`
interface ImageWithFallbackProps {
src: string
alt: string
fallback: typeof Fallback
}
const ImageWithFallback = ({
src,
alt,
fallback = Fallback,
...props
}: ImageWithFallbackProps): React.ReactNode => {
const [error, setError] = useState(false)
const FallbackElement: React.FC = fallback
if (error) return <FallbackElement />
return <Image src={src} alt={alt} {...props} onError={() => setError(true)} />
}
export default ImageWithFallback
And, I use it on another component.
const Content: AnyStyledComponent = styled.div`
/* styles */
`
Content.Text = styled.div`
font-weight: bold;
flex: 1;
text-align: left;
`
Content.Image = styled(ImageWithFallback)`
flex: 1;
max-width: 80px;
align-self: baseline;
`
Content.Fallback = styled(Fallback)`
width: 80px;
padding-bottom: 54px;
`
But I get an error where I define Content.Image like this Content.Image = styled(ImageWithFallback). Error code is ts(2769). How can I solve this issue?

hide imported child component on hover using styled-components

Like the title says, this works with styled components sitting within the same js file (provided they are procedural-ordered above). But with imported child components I can't get it to work.
import React from "react";
import styled from "styled-components";
// Components
import Bars from "../path/Bars.js";
import BarsHover from "../path/BarsHover.js";
// Variables
import { colors } from "../../path/index.js";
//============================================ styles =============================================
const DivBars = styled(Bars)``;
const DivBarsHover = styled(BarsHover)``;
const DivWrapper = styled.div`
display: flex;
width: 20rem;
margin-bottom: 3rem;
&:hover {
cursor: pointer;
}
&:hover ${DivBars} {
display: none;
}
&:hover ${DivBarsHover} {
display: block;
}
`;
//=========================================== component ===========================================
const ParentComponent = props => {
return (
<DivContainer>
<DivBars fullBarWidth={"100%"} fractionBarWidth={"70%"} barColor={colors.niagara} />
<DivBarsHover fullBarWidth={"100%"} fractionBarWidth={"70%"} barColor={colors.gallery2} />
</DivContainer>
);
};
export default ParentComponent;
I think this caveat is the cause:
...wrapping A in a styled() factory makes it eligible for
interpolation -- just make sure the wrapped component passes along
className.
class A extends React.Component {
render() {
return <div className={this.props.className} />
}
}
const StyledA = styled(A)``
const B = styled.div`
${StyledA} {
}
`
NOTE: Ensure the className prop is propagated all the way to the component being referenced in the case that it isn't a direct descendent.

How to inherit style from another component?

I want to take styling from the component 'Tab' which is a react-router-dom Navlink and apply it to my new component 'DropdownTab' which is a 'div'. How can I inherit the styling without making it a NavLink? I want to do this to avoid duplicate lines of code.
const Tab = styled(NavLink)`
padding: 0 20px;
height: ${variables.height};
line-height: ${variables.height};
`;
// Should be the component 'Menu' and is now the component NavLink
const DropdownTab = styled(Tab)`
`;
Perhaps you can use as?
const DropdownTab = styled(props => <Tab as="div" {...props} />)`
/* additional styles */
`;
That's ugly though, so maybe you can just extract the common styles into a variable:
const commonStyles = `
/* common styles */
`
const Tab = styled(NavLink)`${commonStyles}`
const DropdownTab = styled('div')`${commonStyles}`
If you have prop-based style in your component (i.e color: ${props => props.color}, you'd need to use css helper:
import styled, { css } from 'styled-components'
const commonStyles = css`
color: ${({ isActive }) => isActive ? 'red' : 'blue' };
`
...
Why is DropdownTab a div?
Based on your code, DropdownTab is a Tab. You've used the extending styles concept of styled-components and you've done it right.

Resources