Multiple Conditions not working in Styled-Component - reactjs

I am creating Form Buttons where some would be Primary and some would be Secondary. On them, I would apply the UX color codes. The first one works, the second one doesn't.
Am I doing something wrong?
Here is the code:
const FormActionBtns = styled.span`
height: 50px;
max-width: 100px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
background-color: red;
color: white;
${props => props.theme && props.theme.ACCENT && `
font-size: ${props.theme.FONT_SIZE.TOPIC_HEADER};
color: ${props.theme.PRIMARY_STAGE.FORM_COMPONENTS.FONT.REVERSE_BRIGHT};
${props.isPrimary && `
background-color: ${props.theme.ACCENT.IDLE};
`}
${props.isSecondary && `
background-color: ${props.theme.COMPLEMENTARY.IDLE};
`}
`}
`;
export const SubmitBtn = (props) => {
console.log(props);
return(
<FormActionBtns isPrimary={true}>
{props.submitTxt}
</FormActionBtns>
)
}
export const RevertBtn = (props) => {
return (
<FormActionBtns isSecondary={true}>
{props.revertTxt}
</FormActionBtns>
)
}
The Submit button works brilliantly as the correct accent color gets applied based on the theme. The Reset button is not working properly having passed isSecondary=true.
Note: When I remove the condition isPrimary from the style, Reset then gives proper result

In order to implement CSS dynamically in React, I'd just use the style attribute, similarly to this code:
const FormActionBtns = (props) => {
const styleProp = { height: '50px', maxWidth: '100px' }; // etc.
if (props.isPrimary) {
styleProp.backgroundColor = props.theme.ACCENT.IDLE;
} else if (props.isSecondary) {
styleProp.backgroundColor = props.theme.COMPLEMENTARY.IDLE;
}
return (
<span style={styleProp}>
{props.children}
</span>
);
}

You can also use .attrs.
styled.span.attrs(({ isPrimary, theme }) => ({
background-color: isPrimary ? theme.ACCENT.IDLE : theme.COMPLEMENTARY.IDLE;
}))
`
// other styles
`

You're missing a semi-colon:
${props.isPrimary && `
background-color: ${props.theme.ACCENT.IDLE};
`}; // here
${props.isSecondary && `
background-color: ${props.theme.COMPLEMENTARY.IDLE};
`}

Related

React Styled Components styles do not apply properly

I have problem with understanding how cascade style sheet algorithm works with React library styled-component. I have created Todo element with 3 fields, and I gave each its own class , like TodoTitle, TodoDescription and TodoAction - so that div with different class will have diferent width. Yet width property does not apply to class, even though it is flex. Other properties like red backgound did aplied.
import React from 'react';
import { Todo } from '../../types/Todo';
import { StyledTodoItem } from './TodoItem.styles';
type Props = {
todo: Todo;
index: number;
};
export const TodoItem: React.FC<Props> = ({ todo, index }) => {
return (
<StyledTodoItem bg={index % 2 === 0 ? 'even' : 'odd'}>
<div className="todoTitle">
<h1>{todo.title}</h1>
</div>
<div className="todoDescription">
<h1>{todo.text}</h1>
</div>
<div className="todoAction">
<button type="button">Hello</button>
</div>
</StyledTodoItem>
);
};
import styled from 'styled-components';
type Props = {
bg: 'even' | 'odd';
};
export const StyledTodoItem = styled.div<Props>`
width: 100%;
display: flex;
background: ${({ bg }) => (bg === 'odd' ? 'white' : '#ededed')};
height: ${({ theme }) => theme.todoTable.elementMinHeight};
border-right: 1px solid ${({ theme }) => theme.palette.borderColor};
.todoTitle {
display: flex;
justify-content: center;
padding: 10px 20px;
width: 150px;
background: red;
}
`;
If , instead of .todoTitle I paste div , width will be applied, but for every element.
Please, explain how to fix this and any similar problem like this that might arise.
Thanks in advance.
Thanks for comments. So I checked out few tutorials and it seems that best practice is to avaid as possible html tags and to change them for custom styled components as provided in example. Also nested components do lead to lots of bugs, or so it seems.
return (
<>
<TodoItemWrapper bg={index % 2 === 0 ? 'even' : 'odd'}>
<TodoTitle>
<TextTitle>{todo.title}</TextTitle>
</TodoTitle>
<TodoDescr>
<TextDescr>{todo.text}</TextDescr>
</TodoDescr>
<TodoAction>
<Button>View</Button>
<Button>Edit</Button>
<Button>Delete</Button>
</TodoAction>
</TodoItemWrapper>
)
export const TodoItemWrapper = styled.div`
width: 100%;
display: flex;
background: ${({ bg }) => (bg === 'odd' ? 'white' : '#ecfcff')};
height: fit-content;
`;
export const TodoText = styled.div`
display: flex;
justify-content: flex-start;
align-items: center;
padding: 0 20px;
`;
export const TodoTitle = styled(TodoText)`
width: ${({ theme }) => theme.todoTable.title};
border-right: 1px solid ${({ theme }) => theme.palette.borderColor};
`;

flex row exceeding page width, text-overflow: ellipsis not working

my apps flex capabilities have changed since adding some code.
In my MovieContainer ideally I want the flex-direction to be row (it's currently set to column) and only the width of the page and then to start again on the next column ideally 6 across.
But when set to row it goes outside the scope of my page.
Also since adding this new code the text-overflow on my MovieName is now exceeding its width and overflow is no longer hidden.
I feel like I've effected these styled components somehow although when I change the font-size it works fine.
Styled Components:
const MovieContainer = styled.div`
display: flex;
flex-direction: column;
padding: 10px;
width: 280px;
box-shadow: 0 3px 10px 0 #aaa;
cursor: pointer;
`;
const CoverImage = styled.img`
height: 362px;
`;
const MovieName = styled.span`
font-size: 18px;
font-weight: 600;
color: black;
margin: 15px 0;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
`;
const InfoColumn = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
`;
const MovieInfo = styled.span`
font-size: 12px;
font-weight: 500;
color: black;
text-transform: capitalize;
`;
Rest of the code:
const MovieComponent = () => {
const [filmWorld, setFilmWorld] = useState([]);
const [cinemaWorld, setCinemaWorld] = useState([]);
useEffect(() => {
const theaters = ["cinema", "film"];
theaters.forEach((theater) => {
async function fetchCinema() {
const data = await api.getFilm(theater);
if (data.Provider === "Film World") {
console.log("FILM WORLD: ", data.Provider);
console.log(`Data ${theater}World: `, data);
setFilmWorld(data);
} else {
console.log("CINEMAWORLD: ", data.Provider);
console.log(`Data ${theater}World: `, data);
setCinemaWorld(data);
}
}
fetchCinema();
});
}, []);
const movies = useMemo(() => {
if (!filmWorld.Provider) return [];
return filmWorld.Movies.map((movie, index) => ({
...movie,
cinemaWorldPrice:
cinemaWorld.Provider && cinemaWorld.Movies[index]?.Price,
}));
}, [filmWorld, cinemaWorld]);
Since making changes to the below code the styling seems to go out of wack. This is where the styled components are called:
return (
<MovieContainer>
{movies.map((movie, index) => (
<div className="movies" key={index}>
<MovieName>{movie.Title}</MovieName>
<CoverImage src={movie.Poster} alt={movie.Title} />
<InfoColumn>
<MovieInfo>Filmworld Price: ${movie.Price}</MovieInfo>
<MovieInfo>Cinemaworld Price: ${movie.cinemaWorldPrice}</MovieInfo>
</InfoColumn>
</div>
))}
</MovieContainer>
);
};
export default MovieComponent;
If I understood you correctly this is what you trying to achieve:
If that is the case all you have to do is add this line:
const MovieContainer = styled.div`
.
.
.
flex-direction: row;
flex-wrap: wrap;
`;

How to interpolate a CSS property in styled-components?

I am trying to create a Styled Component which can receive a prop for the type of border. The Section can either have border-top or border-bottom based on the prop passed to it.
Here is the code
type TSectionBorder = 'top' | 'bottom';
<Section edge="top">
{children}
</Section>
How can I achieve this in styled components, something along these lines -
const Section = styled.section<{edge: TSectionBorder}>`
border-${edge}: 1px solid black;
`;
const Section = styled.section<{edge: TSectionBorder}>`
${({ edge }) => `border-${edge}: 1px solid black`};
`;
I deconstructed the props just to keep your syntax, however, this is one way to do so. They also have a css helper to look into.
Documentation:
https://styled-components.com/docs/basics#passed-props
https://styled-components.com/docs/api#css
First you will import:
import styled from 'styled-components';
Here is the function ButtonBlock:
function ButtonBlock(props) {
return (
<BtnBlock type="primary" {...props}>
{props.children}
</BtnBlock>
);
}
const handleColorType = (color) => {
switch (color) {
case 'grey':
return '#D4D5D6';
default:
return '#6633CC';
}
};
const hoverColorType = (color) => {
switch (color) {
case 'grey':
return '#6a7581';
default:
return '#99CC00';
}
};
Styled Component:
const BtnBlock = styled(Button)`
&& {
max-width: none;
width: 100%;
text-align: center;
display: block;
font-size: 14px;
font-weight: 600;
line-height: 50px;
height: 50px;
text-transform: uppercase;
border-radius: 0;
border: 0px;
padding: 0;
background: ${({ color }) => handleColorType(color)};
&[disabled] {
background-color: #c6c6c6;
}
:hover {
background-color: ${({ color }) => hoverColorType(color)};
}
&[disabled]:hover {
background-color: #c6c6c6;
}
}
`;

Styled-components: Styles are not applied when trying to style already styled component

I'm trying to style my component which was styled already. But styles in the new rules are not applied in output CSS.
Can I style component that I already styled?
Thanks you in advance for your help.
EDIT: Add rest of LanugageChooser definition
// COMPONENT THAT I'M TRYING TO STYLE
const LanguageChooser = () => {
const Container = styled.div`
display: flex;
align-items: center;
height: 36px;
& > div:not(:last-child) {
margin-right: 5px;
}
`;
return (
<Container>
<Flag { ...languages.pl }/>
<Flag { ...languages.en }/>
</Container>
)
}
const Flag = ({ flag, language }) => {
const { i18n } = useTranslation();
const Button = styled.div`
cursor: pointer;
font-size: 24px;
transition: .2s all;
&:hover {
font-size: 36px;
}
`;
return (
<Button onClick={ () => i18n.changeLanguage(language) }>{ flag }</Button>
)
}
// TRYING ADD MARGIN LEFT, BUT THERE IS NO RESULT.
// ANY OTHER CSS PROPERTY ARE NOT APPLYING
const Navbar = ({ color }) => {
...
const StyledLanguageChooser = styled(LanguageChooser)`
margin-left: auto;
`;
const Nav = styled.nav`
display: flex;
align-content:center;
background: ${ color };
padding: 2px 3px;
`;
return (
<Nav className="navbar">
<StyledNavLink to="/home">...</StyledNavLink>
<StyledNavLink to="/maps">...</StyledNavLink>
<StyledNavLink to="/charts">...</StyledNavLink>
<StyledLanguageChooser/>
</Nav>
)
}
First, move the styled component outside of function scope or your style will reset on every render and you will get heavy performance issues.
Secondly, in order to apply styles, you need to pass className property.
See Styling normal React components
If you use the styled(MyComponent) notation and MyComponent does not render the passed-in className prop, then no styles will be applied.
const Container = styled.div`
display: flex;
align-items: center;
height: 36px;
& > div:not(:last-child) {
margin-right: 5px;
}
`;
const LanguageChooser = ({ className }) => {
return (
<Container className={className}>
<Flag {...languages.pl} />
<Flag {...languages.en} />
</Container>
);
};
const Button = styled.div`
cursor: pointer;
font-size: 24px;
transition: 0.2s all;
&:hover {
font-size: 36px;
}
`;
const Flag = ({ flag, language }) => {
const { i18n } = useTranslation();
return <Button onClick={() => i18n.changeLanguage(language)}>{flag}</Button>;
};

React Styled-Components passing props issue

Trying to create a nested component that accepts multiple arguments, eg. styles for the button, styles for text and a possible icon. Everything (passing props) works fine if I directly render the button.
Below is the code I've written
import React from "react";
import styled from "styled-components";
import _ from "lodash";
import { hexToRgb } from "../styles/helpers";
import * as MaterialIcons from "styled-icons/material";
const StyledButton = styled.button`
text-align: center;
border: ${props =>
props.outline ? `1px solid ${props.outlineColor}` : "none"};
background: ${props => (props.background ? props.background : "#000")};
border-color: ${props =>
props.outlineColor ? props.outlineColor : "transparent"};
min-width: 120px;
width: ${props => (props.width ? props.width : "auto")};
min-height: 40px;
border-radius: 25px;
color: ${props => (props.color ? props.color : "#FFF")};
transition: all ease 0.5s;
&:hover {
cursor: pointer;
background: ${props =>
props.background ? hexToRgb(props.background) : "#FFF"};
}
`;
const StyledText = styled.span`
font-size: 16px;
font-weight: ${props => (props.fontWeight ? props.fontWeight : 400)};
`;
const StyledIcon = styled(MaterialIcons.Lock)`
font-size: 15;
padding: 10px;
`;
const Button = props => {
let _icon = null;
const { children, icon } = props;
console.log("props", props);
if (icon) {
_icon = <StyledIcon size="48" title="Test icon" />;
}
console.log("StyledButton", StyledButton);
return (
<StyledButton>
<StyledText>{children}</StyledText>
{_icon}
</StyledButton>
);
};
export default Button;
If I directly export default StyledButton it works fine.
For some odd reason props were not passed onto the StyledComponent, however directly stating what I want works.
return (
<StyledButton
outlineColor={outlineColor}
background={background}
width={width}
color={color}
outline={outline}
>
<StyledText>{children}</StyledText>
{_icon}
</StyledButton>
);

Resources