Props doesn't exist? Using styled-component and typeScript, already implement `interface` - reactjs

this is my styled component
import React from 'react';
import styled, {css} from 'styled-components';
const CategoryButton = styled(Button).attrs({
variant: 'outlined',
})`
${({ theme }) => css`
display: flex;
align-items: center;
justify-content: center;
padding: ${theme.spacing(2)}px ${theme.spacing(4)}px;
background-color: ${(props) =>
props.selected ? theme.colors.primary.main : theme.colors.background};
color: ${(props) =>
props.selected ? theme.colors.background : theme.colors.primary.main};
border-color: ${(props) =>
props.selected ? theme.colors.primary.main : '#c8c8c8'};
`}
`;
it returns an error that said
Property 'selected' does not exist on type 'ThemeProps<DefaultTheme>'
I've tried to implement the suggestion on this other question to specify the props to pass to component
interface CategoryButton {
selected?: boolean;
}
const CategoryButton = styled(Button).attrs({
variant: 'outlined',
})<CategoryButton>`
${({ theme }) => css`
...
`}
`;
Nevertheless, the same error still appears. How can I get this to work?

You have to pass in selected in CategoryButton like this. It works same as props passing to child:
<CategoryButton selected={true}/>

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};
`;

Custom styled-components breakpoint `Binding element 'theme' implicitly has an 'any' type`

I created "breakpoints" helper functions with styled-components that looks like this:
type Props = Record<string, unknown>
type Interpolators<P extends Props> =
| SimpleInterpolation[]
| Array<Interpolation<ThemedStyledProps<Props, Theme>>>
| Array<Interpolation<ThemedStyledProps<P, Theme>>>
export const xs = <T extends Props>(
strings: TemplateStringsArray,
...values: Interpolators<T>
) => css`
#media screen and (min-width: ${breakpoints.xs}px) {
${css(strings, ...values)}
}`
This used to work just fine, but with TS v4.1x I'm getting the following error when I try to access the theme or any other prop inside the xs function:
Binding element 'theme' implicitly has an 'any' type. TS7031
This is how I'm using it:
export const Container = styled.div`
height: ${NAVBAR_HEIGHT}px;
border-bottom: 2px solid ${({ theme }) => theme.colors.borderSecondary};
padding: 0px 1rem;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
background-color: ${({ theme }) => theme.colors.containerPrimary};
${xs`
background-color: ${({ theme }) => theme.colors.borderSecondary}; // This is where it fails
`}
`

How to EXTEND imported component styled components

I cannot extend my imported component. I was looking into styled components docs and find that in v4+ should works prop "as", but it doesnt.
COMPONENT:
type Props = {
padding: string,
justify: string
}
const FlexContainer = styled.div<Props>`
padding: ${props => props.padding};
display: flex;
flex-wrap: wrap;
justify-content: ${props => props.justify};
`
export const Flexcontainer: React.FC<Props> = props =>{
return (
<FlexContainer padding={props.padding} justify={props.justify}>
{props.children}
</FlexContainer>
)
}
EXTENDED STYLE:
import { Flexcontainer } from '../../reusable/FlexContainer';
const FlexContainerExtended = styled.div`
color: red;
`
USE:
<FlexContainerExtended
padding={null}
justify={"flex-start"}
as={Flexcontainer}>
You just have to add a prop className to your Flexcontainer component like this:
export const Flexcontainer: React.FC<Props> = props =>{
return (
<FlexContainer className={props.className} padding={props.padding} justify={props.justify} >
{props.children}
</FlexContainer>
)}
To override styles, styled-components passes a className as props to the overrided component, it's why
You can just pass the base component to the styled function to override it.
type Props = {
padding: string,
justify: string
}
const FlexContainer = styled.div<Props>`
padding: ${props => props.padding};
display: flex;
flex-wrap: wrap;
justify-content: ${props => props.justify};
`
const FlexContainerExtended = styled(FlexContainer)`
color: red;
`
export const Flexcontainer: React.FC<Props> = props =>{
return (
<FlexContainer padding={props.padding} justify={props.justify}>
{props.children}
</FlexContainer>
)
}
// And use it like this
<FlexContainerExtended
padding={null}
justify={"flex-start"}/>
I know this question was asked a long time ago, but I leave here the solution I found for future visitors.
Base component definition
import React from 'react'
import styled from 'styled-components'
const ContainerWrapper = styled.div`
width: 100%;
max-width: 1200px;
padding-left: 5%;
padding-right: 5%;
margin-left: auto;
margin-right: auto;
`
export default function Container({ children, ...props }) {
return (
<ContainerWrapper {...props}>
{children}
</ContainerWrapper>
)
}
Extended component definition
Obs.: note that the extended component is an article, while the base component is a div, and so far they have no relationship.
Also note that the base component (Container) has been imported but not yet used.
import React from 'react'
import styled from 'styled-components'
import Container from '../../common/Container'
const HeroWrapper = styled.article`
height: 100vh;
padding-top: 74px;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
background-attachment: fixed;
`
Invoking componentes
Now, in the same file where I declared the extended component, I just call the base component, informing the name of the extended component in the as attribute.
export default function PostsWrapper() {
return (
<Container as={HeroWrapper}>
{/* ... */}
</Container>
)
}

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>
);

Nested props in styled-components giving Typescript error

I'm trying to call a prop within a styled-component, but it's giving me an error :
[ts] Property 'color' does not exist on type 'ThemeProps<any>'.
Here's the code with the issue:
const ButtonContainer = styled.button`
border-radius: 4px;
padding: ${theme.s2};
border: 2px solid ${props => props.color};
background: ${props => props.color};
color: ${theme.textDark};
${(props: { secondary?: boolean }) =>
props.secondary &&
css`
background: none;
color: ${props => props.color};
}
`}
`;
With the error showing on the last instance of props.color.
Any ideas how to correctly type this instance? I assume it's something to
${(props: { secondary?: boolean })
Typescript version 3.3.3333 and styled-components 4.1.3
Thanks
Try the following:
color: ${(props: {color: string }) => props.color};
Hope this helps. :)

Resources