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>
)
Related
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();
});
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>
);
}
I'm facing a problem there is a styled component named Breadcrumb but that component depends upon 1 separate styled-components i.e BreadcrumbItem. Both components have different props.
BreadcrumbItem.js:
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
const propTypes = {
/** Active the current BreadcrumbItem. */
active: PropTypes.bool,
/** Additional classes. */
className: PropTypes.string
};
const AbstractBreadcrumbItem = (props) => {
const { className, active, ...attributes } = props;
return <li {...attributes} className={className} />;
};
AbstractBreadcrumbItem.propTypes = propTypes;
const BreadcrumbItem = styled(AbstractBreadcrumbItem)``;
BreadcrumbItem.propTypes = propTypes;
export default BreadcrumbItem;
Breadcrumb.js:
import React from "react";
import styled from "styled-components";
import PropTypes from "prop-types";
const propTypes = {
/** Additional classes. */
className: PropTypes.string,
/** Primary content. */
children: PropTypes.node,
/** Custom separator */
separator: PropTypes.string,
/** Change the look and feel of the BreadcrumbItem. */
scheme: PropTypes.oneOf(["red", "purple"]).isRequired
};
const defaultProps = {
scheme: "red",
separator: "/"
};
const AbstractBreadcrumb = props => {
const { className, children, separator, scheme, ...attributes } = props;
return (
<ul {...attributes} className={className}>
{children}
</ul>
);
};
AbstractBreadcrumb.propTypes = propTypes;
AbstractBreadcrumb.defaultProps = defaultProps;
const Breadcrumb = styled(AbstractBreadcrumb)`
display: flex;
flex-wrap: wrap;
padding: 18px 26px;
margin-bottom: 1rem;
list-style: none;
background-color: #fbfbfb;
border-radius: 4px;
li + li:before {
content: "${props => props.separator}";
}
li + li {
padding-left: 8px;
}
li + li::before {
display: inline-block;
padding-right: 0.5rem;
}
li a {
font-size: 14px;
transition: color .4s linear;
color: ${props => (props.scheme === "red" ? "red" : "purple")};
&:hover {
color: black;
}
}
`;
Breadcrumb.propTypes = propTypes;
Breadcrumb.defaultProps = defaultProps;
export default Breadcrumb;
This is the main markup to create the Breadcrumb.
App.js:
import React from 'react';
import Breadcrumb from './Breadcrumb';
import BreadcrumbItem from './BreadcrumbItem';
export default function App() {
return (
<div className="App">
<Breadcrumb scheme="red">
<BreadcrumbItem>
Home
</BreadcrumbItem>
<BreadcrumbItem>
Shop
</BreadcrumbItem>
<BreadcrumbItem active>
Product
</BreadcrumbItem>
</Breadcrumb>
</div>
);
}
What problem I'm facing is I want to use the active prop of the BreadcrumbItem component inside the parent Breadcrumb component to change the look and feel of the item according to the scheme.
I found the first way which is to add the BreadcrumbItem styles inside the component itself and use something like this ${props => props.active ? css`` : css``}. But Is there a way in styled-component to access the child component prop inside the Parent component?
Please answer the question in the context of styled-components.
Live link: Codesandbox
I'd suggest to move the styling of list item, i.e. <li>, to its own component, i.e. BreadcrumbItem. In this scenario you won't need to access the state of child component instead you'll be handling active state in <li> styles. And it'll look more cleaner and separation of concern (which React recommends) will be there.
[EDIT]: Sample code to access props of children
const List = ({ children }) => {
return (
<ul>
{React.Children.map(children, x => {
console.log(x.props); // get props of children
return x;
})}
</ul>
);
};
const Item = ({ children }) => <li>{children}</li>;
export default function App() {
return (
<List>
<Item>Hello</Item>
<Item active>HI</Item>
</List>
);
}
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]);
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