How to pass props to styled component when using theme? - reactjs

I want to use the color prop I get from Button component to use it for background color using styled component theme.
import React from "react";
import styled from "styled-components";
const StyledButton = styled("button")<{
color: string;
padding: string;
}>`
background: ${(props) => props.theme.colors.primary};
outline: none;
box-shadow: none;
border: none;
padding: ${(props) => props.padding};
border-radius: 5px;
color: white;
font-size: ${(props) => props.theme.fontSizes.small};
margin: 0 10px;
`;
interface Props extends React.ComponentPropsWithoutRef<"button"> {
color?: string;
padding?: string;
}
const Button = ({ children, color, padding }: Props) => {
return (
<StyledButton color={color!} padding={padding!}>
{children}
</StyledButton>
);
};
export default Button;
Theme:
import { DefaultTheme } from "styled-components";
const theme: DefaultTheme = {
colors: {
primary: "#5A8DEE",
boldPrimary: "#274d8c",
lightPrimary: "#dae3f5",
green: "#5CCf4C",
gray: "#F2F4F4",
white: "#FFFFFF",
text: "#5f5f63",
lightText: "#878791",
},
fontSizes: {
extraSmall: "0.75rem",
small: "1rem",
medium: "1.25rem",
large: "1.50rem",
},
};
export default theme;
Like when I get primary from Button props, I want it to get the color codes from the theme context I made.

If I'm understanding your question correctly you've several theme colors and you want to specify which to use from a prop passed to a component.
Given the theme colors:
colors: {
primary: "#5A8DEE",
boldPrimary: "#274d8c",
lightPrimary: "#dae3f5",
green: "#5CCf4C",
gray: "#F2F4F4",
white: "#FFFFFF",
text: "#5f5f63",
lightText: "#878791",
}
You can specify a color prop:
interface Props extends React.ComponentPropsWithoutRef<"button"> {
color?: string;
padding?: string;
}
In the styled component use the passed color prop to access into your theme
const StyledButton = styled("button")<{
color: string;
padding: string;
}>`
background: ${(props) => props.theme.colors[props.color]}; // <-- access by dynamic key
outline: none;
box-shadow: none;
border: none;
padding: ${(props) => props.padding};
border-radius: 5px;
color: white;
font-size: ${(props) => props.theme.fontSizes.small};
margin: 0 10px;
`;
const Button = ({ children, color, padding }: Props) => {
return (
<StyledButton color={color} padding={padding!}>
{children}
</StyledButton>
);
};
Then specify the color you want to use:
<Button color="primary" />
<Button color="boldPrimary" />
...etc...

Related

How to make styled components mixins with props as css property?

I'm building an app with React and Styled components. I'd like to use mixins inside the styles, and pass a CSS property inside this mixin. I tried like this but it's not working:
const TestMixin = ({ color, property }) => css`
${property}: ${color === "blue"
? "blue"
: color === "green"
? "green"
: "red"};
&:hover {
${property}: ${color === "blue"
? "red"
: color === "green"
? "blue"
: "green"};
}
`
const Container = styled.div`
background-color: ${TestMixin({
property: "background-color"
})};
`
How could I make this work? Thanks!
You can't to pass CSS property as an object key. If you want to create a similar scss mixin you can only pass CSS values to the mixin. To set CSS mixin in Container, just use the mixin without CSS property.
import styled, { css } from 'styled-components';
const TestMixin = ({ color }) => css`
background-color: ${color === 'blue' ? 'blue' : color === 'green' ? 'green' : 'red'};
&:hover {
background-color: ${color === 'blue' ? 'red' : color === 'green' ? 'blue' : 'green'};
}
`;
const Container = styled.div`
${TestMixin({ color: 'blue' })};
padding: 1rem 2rem;
`;
export const App = () => {
return <Container>Some text</Container>;
};
Try this solution with Container and container props, it is more flexible and customizable. If you removed the props, the style will not be applied.
import styled, { css } from 'styled-components';
interface Colors {
color: string;
hover: string;
}
interface Props {
bgColor?: Colors;
borderColor?: Colors;
textColor?: Colors;
}
const bgMixin = (color: Colors) => css`
background-color: ${color.color};
&:hover {
background-color: ${color.hover};
}
`;
const borderMixin = (color: Colors) => css`
border-color: ${color.color};
&:hover {
border-color: ${color.hover};
}
`;
const textMixin = (color: Colors) => css`
color: ${color.color};
&:hover {
color: ${color.hover};
}
`;
const Container = styled.div<Props>`
padding: 1rem 2rem;
${({ bgColor }) => bgColor && bgMixin(bgColor)}
${({ textColor }) => textColor && textMixin(textColor)}
border-width: 2px;
border-style: solid;
${({ borderColor }) =>
borderColor
? borderMixin(borderColor)
: css`
border-color: transparent;
`}
`;
export const App = () => {
return (
<Container
bgColor={{ color: 'blue', hover: 'red' }}
borderColor={{ color: 'red', hover: 'blue' }}
textColor={{ color: 'white', hover: 'black' }}
>
Styled Components
</Container>
);
};

React button select change color

I am trying to create a selectable button, so that it is easy, medium and difficult to choose when creating a task in the form
In this case, when he clicks on the easy, changing to the color is choosing, as for example:
easy: green
medium: yellow
high: red
my tsx component looks like this
interface IButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
name: string;
}
const Priority: React.FC<IButtonProps> = ({ name, ...props }) => {
const inputRef = useRef<HTMLButtonElement>(null);
const [priority, setPriorite] = useState("");
const { fieldName, defaultValue, error, registerField } = useField(name);
useEffect(() => {
registerField({
name: fieldName,
ref: inputRef.current,
path: "value",
});
}, [fieldName, registerField]);
return (
<Container
ref={inputRef}
isActive={priority === "Low"}
activeColor="red"
defaultValue={defaultValue}
{...props}
/>
);
};
And my styled-component looks like this
interface RadioBoxProps {
isActive: boolean;
activeColor: string;
}
const colors: { [key: string]: any } = {
red: "red",
green: "green",
blue: "blue",
};
export const Container = styled.button<RadioBoxProps>`
display: flex;
align-items: center;
justify-content: center;
height: 2.5rem;
border: 2px solid;
font: 1rem Poppins;
background: transparent;
border-radius: 0.25rem;
transition: border-color 0.2s;
background: ${(props) =>
props.isActive ? colors[props.activeColor] : "transparent"};
`;
I would like to know the bug that when I select it, it doesn't have the color assigned to it to be sent to the backend

React - styled-components, props, and TypeScript

I looking into styled-components and trying the examples from the website but I also wanted to use TypeScript.
I have this simple example here
import React from 'react';
import './App.css';
import styled from 'styled-components';
interface IProps{
primary: boolean
}
const App:React.FC<IProps> = ({primary}) => {
return (
<div className="App">
<h1>Styled Components</h1>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
}
const Button = styled.button`
background: ${props => props.primary ? 'red' : 'white'};
color: ${props => props.primary ? 'white' : 'red'};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 1px solid green;
border-radius: 3px;
`
export default App;
but I'm getting errors on the primary prop
I getting the error
Property 'primary' does not exist on type 'ThemedStyledProps<Pick<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "form" | ... 264 more ... | "onTransitionEndCapture"> & { ...; }, any>'
Ho can I use styled-components with TypeScript?
Use styled-components with typescript:
const Button = styled.button<{ primary?: boolean }>`
Full code:
import * as React from 'react';
import styled from 'styled-components';
interface IProps{
primary?: boolean
}
const App:React.FC<IProps> = () => {
return (
<div className="App">
<h1>Styled Components</h1>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
}
const Button = styled.button<{ primary?: boolean }>`
background: ${props => props.primary ? 'red' : 'white'};
color: ${props => props.primary ? 'white' : 'red'};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 1px solid green;
border-radius: 3px;
`
export default App;
I prepare a example from my project
Wrapper.ts(styled-component)
import styled from 'styled-components';
interface Props {
height: number;
}
export const Wrapper = styled.div<Props>`
padding: 5%;
height: ${(props) => props.height}%;
`;
index.tsx
import React, { FunctionComponent } from 'react';
import { Wrapper } from './Wrapper';
interface Props {
className?: string;
title: string;
height: number;
}
export const MainBoardList: FunctionComponent<Props> = ({ className, title, height }) => (
<Wrapper height={height} className={className}>
{title}
</Wrapper>
);
you can import interface/type from 'index.tsx' to 'Wrapper.ts' to reuse.
other wise you can specify type like this
export const Wrapper = styled.div<{height:number}>`

Give diferent className to MenuProps and Parent Select Componens with styled-components

I'm trying to customize a MUI TextField Select component with styled components.
The ideia is styled-compoents provide diferent classes to Select field and Menu, so i can have their styled separated.
const StyledSelect = styled(({ className, ...props }) => {
return (
<TextField {...props}
classes={{ root: className }}
SelectProps={{
MenuProps: {
classes: { paper: className, list: className },
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
},
transformOrigin: {
vertical: "top",
horizontal: "left"
},
getContentAnchorEl: null
},
}}
/>
)
})`
& {
background-color: #454D5D;
border-radius: 10px;
margin-top: 5px;
}
& li {
color: #FFF;
}
&.MuiFormControl-root {
background-color: transparent;
}
& .MuiListItem-root {
font-size: 18px;
}
& .MuiListItem-root.Mui-selected {
background-color: #1A2233;
}
& .MuiFormLabel-root {
font-family: 'Roboto';
font-weight: 300;
}
& .MuiInputLabel-shrink {
color: ${props => props.color};
font-weight: normal;
}
& .MuiInput-underline:after {
border-bottom: 2px solid ${props => props.errors[props.field.name] && props.touched[props.field.name]
? CASABLANCA : props.color};
transition: none;
transform: none;
}
& .MuiInput-underline:before {
border-bottom: 1px solid ${props => props.color}
}
& .MuiSelect-roo {
color: black;
font-family: 'Roboto';
font-weight: 300;
}
& .MuiSelect-select:focus {
background: transparent;
}
`;
I wish my TextField class would be different from MenuProps class
One way to solve this is to have one wrapper component per class name you need to generate. In my example below, StyledTextField takes care of the root class name for TextField (the className property is equivalent to classes.root) and then MenuPaperClass provides an additional class name.
import React from "react";
import ReactDOM from "react-dom";
import TextField from "#material-ui/core/TextField";
import MenuItem from "#material-ui/core/MenuItem";
import styled from "styled-components";
const StyledTextField = styled(TextField)`
/* && to add specificity */
&& {
border: 1px solid green;
}
`;
const MenuPaperClass = styled(({ className, ...props }) => {
return (
<StyledTextField
SelectProps={{ MenuProps: { classes: { paper: className } } }}
value="1"
select
{...props}
>
<MenuItem value="1">One</MenuItem>
<MenuItem value="2">Two</MenuItem>
<MenuItem value="3">Three</MenuItem>
</StyledTextField>
);
})`
&& {
background-color: lightblue;
}
`;
function App() {
return (
<div className="App">
<MenuPaperClass />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
This isn't a particularly elegant solution and gets pretty tedious if you have 3 or more separate classes you want to use, so I may come back to this later to consider alternative approaches/syntax, but this does work.

TypeError: Cannot read property 'color' of undefined with Styled-Components

I created any component and in it I use a property called "style" and inside this property that is an object I use css styling properties.
The error occurs because I am not entering the same amount of properties within style in the other corresponding components.
I already tried to check if the props is undefined, inside my styled, but it always returns me false and the error continues ...
I would like to know how do I enter properties within the style, but is it mandatory to enter the same amount of properties for all components, or do they have the same properties?
Example 1:
<SpotifyIcon
name="books"
style={{
margin: "0 1rem 0 0",
padding: "0.3rem",
verticalAlign: "middle",
fontSize: "1.4rem",
color: "#0A0A09",
bgColor: "#b3b3b3",
hover: { color: "#1DB954", bgColor: "white" }
}}
/>
Example 2:
<SpotifyIcon
name="books"
style={{ fontSize: "1.5rem", color: "#b3b3b3" }}
/>
MY STYLES.JS:
import styled from "styled-components";
const Icon = styled.i`
font-family: "spotify" !important;
font-style: normal;
font-weight: normal;
font-variant: normal;
font-size: ${props => props.style.fontSize};
margin: ${props => props.style.margin};
padding: ${props => props.style.padding};
vertical-align: ${props => props.style.verticalAlign};
color: ${props => props.style.color};
background-color: ${props => props.style.backgroundColor};
text-transform: none;
line-height: 1;
:before {
content: ${props => `"\\${props.theme.default[props.name]}"`};
}
:hover {
color: ${props => props.style.hover.color};
background-color: ${props => props.style.hover.bgColor};
}
`;
export default Icon;
MY COMPONENTE INDEX:
import React from "react";
import PropTypes from "prop-types";
import { ThemeProvider } from "styled-components";
// STYLE AND THEMES
import Icon from "./styles";
import Themes from "./themes";
const SpotifyIcon = ({ name, style }) => {
return (
<ThemeProvider theme={Themes}>
<Icon name={name} style={style} />
</ThemeProvider>
);
};
SpotifyIcon.propTypes = {
name: PropTypes.string.isRequired,
style: PropTypes.shape({
margin: PropTypes.string,
padding: PropTypes.string,
verticalAlign: PropTypes.string,
bgColor: PropTypes.string,
fontSize: PropTypes.string,
color: PropTypes.string,
hover: PropTypes.shape({
color: PropTypes.string,
bgColor: PropTypes.string
})
}),
checkIconName: (propObjValue, funcName, componentName) => {
if (Themes.default[propObjValue.name] === undefined) {
return new Error(
`Invalid name ${
propObjValue.name
}, supplied to ${componentName}. Validation Failed!`
);
}
}
};
export default SpotifyIcon;
My friends, I found the answer to my problems, so come on ..
To begin with I did something wrong, which inserted properties into the style property, where it is the inline css reserved property.
So to solve the first problem I removed the properties from inside the style and inserted it straight into the component.
The second problem complements the first, because undefined was inserted because it does not have the property, because it is inside the style property. So to solve the problem beyond the first step I entered defaultProps and that's it!

Resources