Pass props to styled-components - javascript

I am querying data for my react site using graphql from my CMS (prismic.io) in order to produce color themed pages. I want to pass a variable or props into my styled component to change the background color based on what is sent back from the CMS.
In the below example, my graphql query will return a HEX that has been inputted by the user, this would then be applied to buttons etc to theme that page.
The colour can and will change from page to page as the user will be selecting it within the CMS.
Any help would be appreciated. Code example below:
Props
props.data.case_study_color
Component
const ContactButton = styled.button `
background: #004655;
color: #fff;
font-size: 2rem;
padding: 10px;
`;

You could do the following.
const ContactButton = styled.button`
background: #004655;
color: ${props => props.color || '#fff'};
font-size: 2rem;
padding: 10px;
`;
See codesandbox example here.
This would be the component code:
.....component
const [color, setColor] = React.useState("#fff");
React.useEffect(() => {
fetch(URL).then(data => {
setColor(data.response);
});
}, []);
return (
<div className="App">
<ContactButton color={color}>White</ContactButton>
</div>
);

const ContactButton = styled.button `
background: ${props => props.caseStudyColor};
color: #fff;
font-size: 2rem;
padding: 10px;
`;
<ContactButton caseStudyColor={'#004655'} />

As my solution was slightly different but based on Paul's answer it might be useful for someone else.
Button Component
const ContactButton = styled.button`
background: ${props => props.themeColor || '#004655'};`
Color Component
const themeColor = props.data.case_study_color;
Button
<ContactButton themeColor={themeColor}>Get in touch</ContactButton>

Related

hover testing not working on react component

Testing opacity while hover button is not working. Tried with both userEvent.hover(hoverButton) and fireEvent.mouseOver(hoverButton) unfortunately the result is same. At the same time the other properties are working, for example 'cursor:not-allowed'
Button.js
import styled from 'styled-components';
const ButtonWrapper = styled.button`
color: #fff;
background-color: tomato;
font-size: 14px;
padding: 6px 30px;
height: 35px;
&:not(:disabled):hover {
opacity: 0.8;
background: red;
}
${({ disabled }) =>
disabled && `
opacity: 0.4;
cursor: not-allowed;
`}
`;
// background-color: ${(props) => props.theme.buttonColors[props.kind]};
const Button = ({ children, className, disabled, size = 'medium' }) => {
return <ButtonWrapper
className={className}
disabled={disabled}
onClick={() => console.log('Hello mate')}
size={size}
>{children}</ButtonWrapper>;
}
export default Button;
Button.test.js
import { fireEvent, render, screen } from '#testing-library/react';
import userEvent from "#testing-library/user-event";
import Button from './index';
test('should render button', () => {
render(<Button>Click on me</Button>);
expect(screen.getByText(/Click on me/i)).toBeInTheDocument();
});
test('should render disabled button', () => {
render(<Button disabled>hover on me</Button>);
const hoverButton = screen.getByRole('button');
expect(hoverButton).toBeDisabled();
expect(hoverButton).toHaveStyle('opacity: 0.4')
expect(hoverButton).toHaveStyle('cursor:not-allowed')
});
test('should hover button', () => {
render(<Button>hover on me</Button>);
// const hoverButton = screen.getByRole('button');
const hoverButton = screen.getByRole('button', { name: 'hover on me' });
userEvent.hover(hoverButton);
// fireEvent.mouseOver(hoverButton)
expect(hoverButton).toHaveStyle('opacity: 0.8')
expect(hoverButton).toHaveStyle(`background-color: #000`);
});
test('toMatchSnapshot', () => {
const { asFragment } = render(<Button>Click on me</Button>);
expect(asFragment()).toMatchSnapshot();
});
Problem
I found a few obstacles that you're running into:
There's a mistake in the test, where the background-color was expected to be #000 when it's set as background: redu in the Button.js file.
I'm pretty confident that the testing-library's hover interaction doesn't update the computed styles. I verified this using getComputedStyle in the DOM versus in the test. That said, you can use jest-styled-components' toHaveStyleRule, to check for pseudo styles however that's not really not going to cover what you're attempting to test.
Currently, there's really no way for you to test a button's current appearance based upon a browser interaction, unless that interaction add/removes styles dynamically using some sort of React state or prop (similar to what you've done with disabled). As a result, there's no way to accurately determine it's appearance since every Button has the same pseudo styles (the snapshots showcase this, but also looking at the DOM will showcase the same thing):
exports[`Button disabled snapshot 1`] = `
<DocumentFragment>
.c0 {
color: #fff;
background-color: tomato;
font-size: 14px;
padding: 6px 30px;
height: 35px;
opacity: 0.4;
cursor: not-allowed;
}
.c0:not(:disabled):hover {
opacity: 0.8;
background: red;
}
<button
class="c0"
disabled=""
>
Click on me
</button>
</DocumentFragment>
`;
exports[`Button enabled snapshot 1`] = `
<DocumentFragment>
.c0 {
color: #fff;
background-color: tomato;
font-size: 14px;
padding: 6px 30px;
height: 35px;
}
.c0:not(:disabled):hover {
opacity: 0.8;
background: red;
}
<button
class="c0"
>
Click on me
</button>
</DocumentFragment>
`;
Recommendation:
You don't need to test against native interactions/style changes with the DOM. Instead, test against what you do control. In this case, you're controlling whether or not a button can be disabled and appending styles based upon that prop. Other than testing that (which you've already done), in this case, any other tests should be considered ancillary/superfluous.
Taking a step back, what exactly are you trying to test? It seems like you are trying to test if a browser can apply a style via a specific CSS selector to a button? This seems rather unnecessary, no?
Not recommended:
This will test if an element contains a particular pseudo style, but again, it's shared and there's really no way to determine when its styles are active (even if you added a disabled button prop, this test will still pass because it still contains the pseudo styles!):
test("should contain button hover styles", () => {
render(<Button>hover on me</Button>);
const hoverButton = screen.getByRole("button");
expect(hoverButton).toHaveStyleRule("opacity", "0.8", {
modifier: ":not(:disabled):hover",
});
expect(hoverButton).toHaveStyleRule("background", "red", {
modifier: ":not(:disabled):hover",
});
});

How can I remove whitespace from a string in react-native?

I am using react-native. However, when a long string is input, the line breaks are not consistent in the right screen.
like this
However, I would like the string to wrap consistently as in the image below.
this is my code how can i fix or add code??
import React from 'react';
import styled from 'styled-components/native';
const Container = styled.View`
background: lightskyblue;
width: 100%;
height: 100%;
`;
const PolicyContainer = styled.View`
background: lightyellow;
`;
const Label = styled.Text`
font-size: 13px;
`;
const Policy = () => {
return (
<Container>
<PolicyContainer>
<Label>
{'<'}example{'>'}('https://example'이하 'example')은(는) 「개인정보
보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한
고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이
개인정보 처리방침을 수립·공개합니다.
</Label>
</PolicyContainer>
</Container>
);
};
export default Policy;
Try using text-align: justify; in the style of Label. I believe it will solve your problem.
const Label = styled.Text`
font-size: 13px;
text-align: justify;
`;

how do you share styles in react?

I have a component in which I got:
const Title = styled(Typography)({
fontFamily: 'Manrope',
fontStyle: 'normal',
fontWeight: 600,
fontSize: 28,
lineHeight: '38px',
color: '#20232C',
marginTop: 17,
height: 50,
display: 'block',
});
and in this component i use it like this..
<Title>Title here</Title>
As you can imagine, Title is something that could be used in many places in the app. So I took the above Title and put it in components/Titles/styles.tsx. and then export it from there.
Now, wherever I need this, I import Title and just use it.
The problem: In some other places, this Title needs to have different fontWeight and marginTop, and so on. Who knows, in the future, maybe in one place, it could need 4-5 fields different then in the current Title styles.
How can we workaround this ?
Way 1:
export const Title = styled(Typography)(
({ fontWeight }: { fontWeight?: number }) => ({
width: 'fit-content',
fontFamily: 'Manrope',
fontStyle: 'normal',
fontWeight: fontWeight || 'normal',
fontSize: 28,
lineHeight: '25px',
color: '#20232C',
marginTop: '17px',
}),
);
As I made fontWeight optional, We could do this for all fields.
What do you think of this way and how do you handle situations like this ? it's really almost the first time I am dealing with styles in js. I come from a vue.js world.
Making the fontWeight as an attribute is of course fine, but might come too complex when extending it further.
The other option would be inherit the Title into another styled component (always best when you find meaningful use-cases for naming), e.g.:
export const Subtitle = styled(Title)`
font-size: 24px;
font-weight: bold;
`
This is specific to styled-components.
There are two common ways, both covered in the documentation.
Props
const Button = styled.button`
/* Adapt the colors based on primary prop */
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
Extending
// The Button from the last section without the interpolations
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// A new component based on Button, but with some override styles
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
render(
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);
You should be able to use props in the Title tag and pass them to the individual component like this:
<Title thisTitlesHight="80px" thisTitlesWidth="50px">Title Here<Title>
Then using these props inside your child component:
export const Title = styled(Typography)(
({ fontWeight }: { fontWeight?: number }) => ({
height: this.props.thisTitlesHeight,
width: this.props.thistitlesWidth
}),
);
you also have to define props in the function.
I created a codeSandbox so you can see the code for it: https://codesandbox.io/s/stupefied-wildflower-wmw26?file=/src/App.js

How to access child's props from parent's styled-component?

I have kind of this construction:
<Wrapper activeTextColor="red">
<Text active={true}>Text 1</Text>
<Text active={false}>Text 2</Text>
</Wrapper>
Styled-components should look like this:
const Text = styled.p``;
const Wrapper = styled.div`
${Text} {
${props =>
props.activeTextColor &&
css`
/* How to make color red based on "active" attribute of Text element? */
`}
}
`;
How to access child's props from parent's styled-component here?
Here is a live example
You can't (as far as I can tell). But you can access the parent's props from the child component (the other way around). That seems to accomplish what you are trying to do.
Short answer:
You have to pass the parent prop to the child component.
In a parent component <Wrapper /> you would have to clone your children and pass the activeTextColor to the children:
const StyledWrapper = styled.div``;
class Wrapper extends React.Component {
render() {
const { children, activeTextColor } = this.props;
return (
<StyledWrapper activeTextColor={activeTextColor}>
{React.Children.map(children, child =>
React.cloneElement(child, {
activeTextColor: activeTextColor
})
)}
</StyledWrapper>
);
}
}
Both activeTextColor and active are now accessible from the Text component.
const Text = styled.p`
${props => css`
color: ${props.active ? activeTextColor : "#000"};
`}
`;
Another Option:
In the case above, it might make more sense to go with a ThemeProvider/ThemeConsumer. If you know an activeTextColor is going to be red (maybe you are dealing with design tokens), then access the active colors with:
${props => css`
background: ${props.active ? props.theme.activeTextColor : '#000'};
`}
Detailed Answer (and why someone would want to do this):
This extends the Short Answer above. At some point you might need to access the parent props in the parent component, and both the child and parent props in the child component.
A real world example would be something like Tabs. I have two different styles / variants of Tabs, with both the Tabs container component and Tab needing its own styles depending its own props. It is one component styled two different ways.
Nesting the styled-components won't work. So you end up with something like this.
const StyledTabs = styled.div`
display: flex;
justify-content: flex-start;
${props =>
props.variant === "wizard" &&
css`
justify-content: space-between;
`}
`;
const StyledTab = styled.p`
font-size: 14px;
white-space: nowrap;
font-family: sans-serif;
border: 1px solid #ddd;
padding: 15px;
${props => css`
background: ${props.active ? "#fff" : "#f6f6f6"};
`}
${props =>
props.variant === "box" &&
css`
& {
border-right: 0 none;
&:last-child {
border-right: 1px solid #ddd;
}
border-top: ${props.active
? "2px solid lightseagreen"
: "1px solid #ddd"};
border-bottom: ${props.active ? "none" : "1px solid #ddd"};
}
`}
${props =>
props.variant === "wizard" &&
css`
& {
margin-right: 20px;
text-align: center;
line-height: 40px;
height: 40px;
width: 40px;
border-radius: 50%;
color: ${props.active ? "#fff" : "#000"};
${props.active && "background: lightseagreen"};
}
`}
`;
class Tabs extends React.Component {
render() {
const { children, variant } = this.props;
return (
<StyledTabs variant={variant}>
{React.Children.map(children, child =>
React.cloneElement(child, {
variant: variant
})
)}
</StyledTabs>
);
}
}
class Tab extends React.Component {
render() {
const { children, variant, active } = this.props;
return (
<StyledTab variant={variant} active={active}>
{children}
</StyledTab>
);
}
}
const App = () => (
<div>
<Tabs variant="box">
<Tab active={true}>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</Tabs>
<Tabs variant="wizard">
<Tab active={true}>Step 1</Tab>
<Tab>Step 2</Tab>
<Tab>Step 3</Tab>
</Tabs>
</div>
);
render(<App />, document.getElementById("root"));
Full example:
https://codesandbox.io/s/upbeat-thunder-wfo2m
Related issue on styled-component's GitHub:
https://github.com/styled-components/styled-components/issues/1193
There are quite a few related questions on StackOverflow, but I don't think there are many clear answers:
react-native with styled-components parent prop
How to style a child component from a parent styled component, considering passed props
const Text = styled.p`color: ${props => props.active ? "red" : "palevioletred"};`;

Create new component and inherit styles from styled-component

const Button = styled.button`
display: inline-block;
width: 300px;
background-color: black;
`
const ButtonHref = styled.a`
${Button}
`
So I have two styled-components. I want to inherit 'Button' styles but create another tag. I use react-emotion. How can I do this?
There are a few options here, using composition, styled components, or with props. The second option is probably what you want, but I've provided two other options as well.
1. Using composition
const baseButton = css`
color: white;
background-color: black;
`
const fancyButton = css`
background-color: red;
`
render() {
return (
<div>
<button css={baseButton}></button>
<button css={[baseButton, fancyButton]}></button>
</div>
)
}
The second button will have the baseButton and specialButton styles.
Or...
const baseButton = css`
color: white;
background-color: black;
`
const fancyButton = css`
${baseButton};
background-color: red;
`
render() {
return (
<div>
<button css={baseButton}></button>
<button css={fancyButton}></button>
</div>
)
}
2. Using styled components
const Button = styled.button`
color: white;
background-color: black;
`
const Fancy = styled(Button)`
background-color: red;
`
render() {
return (
<div>
<Button>Button</Button>
<Fancy>Fancy</Fancy>
</div>
)
}
This works for any component that accepts the className prop, which button does.
3. Using props
const Button = styled.button`
color: white;
background-color: ${props => props.fancy ? 'red' : 'black'};
`
render() {
return (
<div>
<Button>Button</Button>
<Button fancy>Fancy</Button>
</div>
)
)
If you just want an a that has the exact styles as your Button then you can do <Button as=“a” />
You could also add some custom css like this:
const ButtonBase = styled.button`
// some css here
`
const SmallButtonCustom = `
// some css here
`
const SmallButton = styled(ButtonBase)`
${SmallButtonCustom};
`

Categories