Sibling component styling with styled-components - styled-components

This is quite a simple case which I can't, for some reason, get to work. I have a styled-components Container, and I'd like to define the styles of p inside that container like so:
const Container = styled.div`
& p {
margin: 0;
& + & {
margin-top: 10px;
}
}
`
So, I would expect whenever there's more that on p inside the Container, that the second p would get a top-margin, but this doesn't happen.
Here's a codesandbox.
Any ideas?

I just inverted the condition.
Is this want you want?
CodeSandbox
import styled from "styled-components";
const Container = styled.div`
background: grey;
& p:nth-child(1) {
margin: 0px;
}
& p {
margin-top: 10px;
}
`;
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Container>
<p>Line One</p>
<p>Line Two</p>
<p>Line Two</p>
</Container>
</div>
);
}

Related

jest testing on react-hook-form

I have below react code and jest code but I am having trouble targeting the name input field using jest. I have set (data-testid="name") on the FormModal component but jest couldn't find it and returned with error ( TestingLibraryElementError: Unable to find an element by: [data-testid="name"]). I keep on debugging but couldn't find the problem.
FormModal.tsx
import { FunctionComponent } from "react";
import Modal from "react-modal";
import { useForm } from "react-hook-form";
export const InputField = styled.input`
width: 70%;
height: 25px;
border-radius: 5px;
&:focus {
border: 3px solid #89cff0;
outline: none;
}
`;
export const InputSpan = styled.div`
width: 30%;
`;
export const InputContainer = styled.div`
width: 100%;
display: flex;
margin: 15px 0px;
height: 30px;
align-items: center;
`;
return (
<form onSubmit={handleSubmit(onSubmit)}>
<InputContainer>
<InputSpan>Name:</InputSpan>
<InputField data-testid="name" {...register("name")}></InputField>
</InputContainer>
<ModalFooter>
<FormSubmit type="submit">Submit</FormSubmit>
<FormCancel onClick={props.openModal}>Cancel</FormCancel>
</ModalFooter>
</form>
);
};
Here is the jest test
FormModal.test.tsx
import { fireEvent, render, screen } from "#testing-library/react";
import FormModal from "../FormModal";
it("calls the onSubmit function without valid inputs", () => {
render(<FormModal />);
const createButton = screen.getByTestId("name");
expect(createButton).not.toBeNull();
});

Style-components - inherit the styles and dynamic props from a styled component but not the tag

Essentially, I am looking for a way to share a spacing styling to all components like this.
const Spacing = styled.div`
padding: ${props => props.p || 0};
padding-top: ${props => props.pt || 0};
`;
const Button = styled(Spacing)`
background: white;
`;
With this example, I can inherit the dynamic padding of Spacing but the problem is it will inherit the tag. so if I use <Button as='button' pt='2rem' > Click </Button> I have to manually add as='' attribute to all inheriting component.
Is there a way to get rid of the as='' attribute?
I got the solution.
First create the styles to be inherited:
spacing.js
import { css } from "styled-components";
export const Spacing = css`
margin-top: ${props => props.mt + "rem" || 0};
margin-bottom: ${props => props.mb + "rem" || 0};
margin-left: ${props => props.ml + "rem" || 0};
margin-right: ${props => props.mr + "rem" || 0};
`;
styled.button.js
import styled from "styled-components";
import { Spacing } from "./path-to-spacing.js"
const Button = styled.button`
${Spacing};
// your styles
`;
usage
<Button mt={4}> Click me </Button>

React: Map does not give expected result

I am using React-typescript for my app. For styling I am using styled-components. I have created one global breadcrumbs components. When I am using the breadcrumbs components to parent components there will be couple links. after mapping the links it should display single links but I am getting combined links. This is how it looks like:
My expected result is:
This is my parent component where I am importing my Breadcrumbs
<BreadCrumb>
check
this
out
</BreadCrumb>
This is my global Breadcrumb component
import React from "react";
import styled from 'styled-components'
export interface IBreadCrumb {
direction?: "";
children: JSX.Element[];
onClick?: () => void;
}
export const BreadCrumb = ({ direction, children, onClick }: IBreadCrumb) => {
return (
<div>
<Breadcrumbs>
{
children.map(i => //In here I am doing my mapping.
<Crumb>
<a href="#" onClick={onClick}>{children}</a>
</Crumb>
)
}
</Breadcrumbs>
</div >
)
}
const Direction = {
right: "\\2192",
left: "\\2190 ",
slash: "/"
}
const Crumb = styled.li`
display: inline-block;
&:last-of-type:after {
content: "";
padding: 0;
}
a {
color: grey;
text-decoration: none;
&:hover,
&:active {
text-decoration: underline;
}
}
`
const Breadcrumbs = styled.ul`
list-style: none;
padding: 0;
& > li:after {
content: "${Direction.right}";
padding: 0 8px;
color: grey
}
`;
Should it not be e.g.:
children.map((child, index) =>
<Crumb>
<a key="{index}" href="#" onClick={onClick}>{child}</a>
</Crumb>
)

Incorrect rendering of a component when using Math.random in setState in React Component

The intention is to display an item from a list of objects but on every page refresh, the item should be randomly chosen from the list. Here, Testimonials is the list and I want to display any random item from this list. If I use a constant, it works fine. When I use, the random function, it does not display proper image with its associated item message.
I use React 16, Next.js, styled components as the tech.
The problem is in rendering of Employees section. The console displays a warning as
warning.js?6327:33 Warning: Propsrcdid not match. Server: "/static/images/testimonials/2.jpg" Client: "/static/images/testimonials/5.png"
Here is the piece of my code
import {Component} from 'react';
import Row from '../../../common-util/row';
import Col from '../../../common-util/col';
import {Container, Content, Image, StyledCol, Statement, Title, Designation, Heading, Arrow} from './styles';
const Testimonials = [{
name: 'ACX',
role: 'XYZ',
image: '/static/images/testimonials/1.jpeg',
message: 'KL DSAD E'
}, {
name: 'HJK',
role: 'Growth Hacker',
image: '/static/images/testimonials/2.jpg',
message: 'JKLASD ASDA'
}, {
name: 'ZXCV',
role: 'Product Manager',
image: '/static/images/testimonials/3.jpg',
message: 'KKK'
}, {
name: 'UIP',
role: 'Data Integrity',
image: '/static/images/testimonials/4.JPG',
message: 'LOPO'
}, {
name: 'NMa',
role: 'Sales Evangelist',
image: '/static/images/testimonials/5.png',
message: 'KK D D D'
}];
export default class Employees extends Component {
constructor(props) {
super(props);
this.state = {
currentIndex: parseInt((Math.random()*10))%5,
};
}
render() {
let {currentIndex} = this.state;
return (
<Container>
<Content>
<Title>Our employees say...</Title>
</Content>
<Row>
<Col xs={1} sm={1} md={2} lg={2}>
<Arrow className={currentIndex === 0 ? 'disabled' : ''} onClick={this.handleClick.bind(this, 'DECREMENT')}>{'<'}</Arrow>
</Col>
<Col xs={10} sm={10} md={8} lg={8}>{this.currentItem(currentIndex)}</Col>
<Col xs={1} sm={1} md={2} lg={2}>
<Arrow className={currentIndex === Testimonials.length - 1 ? 'disabled' : ''} onClick={this.handleClick.bind(this, 'INCREMENT')}>{'>'}</Arrow>
</Col>
</Row>
</Container>
);
}
currentItem(currentIndex) {
const item = Testimonials[currentIndex];
return (
<Row>
<StyledCol xs={4} md={4} lg={3}>
<Image src={item.image} alt={item.name} />
</StyledCol>
<Col xs={8} md={8} lg={9}>
<Statement>{item.message}</Statement>
<Heading className='font-Bold'>{item.name},</Heading>
<Designation className='font-DemiBold'>{item.role}</Designation>
</Col>
</Row>
);
}
handleClick(type: string) {
let {currentIndex} = this.state;
switch (type) {
case 'DECREMENT':
this.setState({
currentIndex: currentIndex - 1
});
break;
case 'INCREMENT':
this.setState({
currentIndex: currentIndex + 1
});
break;
default:
}
}
}
The corresponding style is
import styled from 'styled-components';
import Col from '../../../common-util/col';
import Grid from '../../../common-util/grid';
import H4 from '../../../common-util/headers/h4';
import H5 from '../../../common-util/headers/h5';
export const Container = styled(Grid)`
padding-bottom: 20px;
`;
export const Content = styled.div`
display: flex;
flex-flow: row wrap;
width: 100%;
justify-content: center;
margin-bottom: 20px;
`;
export const Title = styled.h1`
font-size: 42px;
line-height: 1.0;
letter-spacing: -0.3px;
text-align: justify;
font-weight: 500;
`;
export const Image = styled.img`
width: 100%;
`;
export const Statement = styled.p`
padding: 15px 20px;
background: url(/static/images/svg/top-left-bg.svg) top left no-repeat, url(/static/images/svg/bottom-right-bg.svg) bottom right no-repeat;
background-size: 20px;
line-height: 2.3;
letter-spacing: -0.2px;
font-size: 16px;
margin: 0
`;
export const Heading = H4.extend`
color: #4990e2;
text-align: left;
font-weight: bold;
line-height: 1.2;
margin-bottom: 5px;
margin-left: 20px;
`;
export const Designation = H5.extend`
text-align: left;
font-weight: 600;
line-height: 1.22;
letter-spacing: -0.2px;
margin-top: 0;
margin-left: 20px;
`;
export const Arrow = styled.div`
margin: auto;
color: grey;
font-size: 20px;
font-weight:lighter;
cursor: pointer;
&:hover {
font-size: 22px;
}
&.disabled {
pointer-events: none;
&:hover {
font-size: 20px;
}
}
`;
export const StyledCol = Col.extend`
margin: auto;
`;
That's the problem.
this.state = {
currentIndex: parseInt((Math.random()*10))%5,
};
This will be invoked on a server and in the browser causing a mismatch in rendered markup.
You could fix that by making sure random will only be called in a browser:
this.state = {
currentIndex: 0,
};
componentDidMount(){
this.setState({ currentIndex: parseInt((Math.random()*10))%5 })
}
Using React's useState and useEffect hook introduced in React 16.8.0:
const [selectedImage, setImage] = useState(<defaultImage>);
useEffect(() => {
setImage(randomImage());
}, [selectedImage]);

How can a parent pass overriding style rules to a child in styled-components fashion?

I have a Button and a few other versions of the button that would like to reuse Button but override a few specific rules. Like width, height, color, or hovered state color etc.
Is this possible without having to manually pass in every single property as a prop?
Here is the example in webpackbin http://www.webpackbin.com/VyjrMGJ9f
import React from 'react';
import styled from 'styled-components';
const Button = styled.button`
text-align: center;
font: bold 15px helvetica;
padding: 10px;
border: 0;
pointer-events: ${props => props.disabled ? 'none' : 'auto'};
color: ${props => props.disabled ? '#848484' : '#fff'};
background-color: ${props => props.disabled ? '#bebebe' : '#07314d'};
&:hover {
background-color: ${props => props.disabled ? '#bebebe' : '#336086'};
}
`
const EnhancedButton = styled.button`
background-color: ${props => props.disabled ? '#bebebe' : '#ec4800'};
&:hover {
background-color: ${props => props.disabled ? '#bebebe' : '#f98d00'};
}
`
export default function HelloWorld() {
return (
<div>
<p>
<Button disabled>disabled button</Button>
and
<Button>button</Button>
</p>
<p>
How can EnhancedButton in a different component re-use all of Button's rules and override some of them (in this case the background color and hovered background color?
<EnhancedButton disabled>disabled enhanced button</EnhancedButton>
and
<EnhancedButton>enhanced button</EnhancedButton>
</p>
</div>
);
}
I just figured this out, so simple.
styled
can either take a tag or a component as an arguemnt, so in this case all I had to do was to change
const EnhancedButton = styled.button`
to
const EnhancedButton = styled(Button)`
Here's the working version on Webpackbin http://www.webpackbin.com/VyjrMGJ9f

Resources