Styled Components - How to style a component passed as a prop? - css

I am trying to build this simple component which takes title and Icon Component as props and renders them. The icons I use here are third party components like the ones from Material UI.
option.component.jsx
import { Wrapper } from './option.styles';
function Option({ title, Icon }) {
return (
<Wrapper>
{Icon && <Icon />}
<h4>{title}</h4>
</Wrapper>
);
}
option.styles.js
import styled from 'styled-components';
export const Wrapper = styled.div`
display: flex;
color: grey;
&:hover {
color: white;
}
`;
// export const Icon =
I've organized all my styles in a separate file and I intend to keep it that way.
I want to style <Icon /> , but I don't want to do it inside Option Component like this.
import styled from 'styled-components';
import { Wrapper } from './option.styles';
function Option({ title, Icon }) {
const IconStyled = styled(Icon)`
margin-right: 10px;
`;
return (
<Wrapper>
{Icon && <IconStyled />}
<h4>{title}</h4>
</Wrapper>
);
}
What is the best way to style a component passed as a prop while maintaining this file organization?
I've looked through the documentation and I wasn't able find anything related to this. Any help would be appreciated.

You can do this in 2 ways:
1. As a SVG Icon (svg-icon):
option.styles.js as:
import styled from "styled-components";
import SvgIcon from "#material-ui/core/SvgIcon";
export const Wrapper = styled.div`
display: flex;
color: grey;
&:hover {
color: black;
}
`;
export const IconStyled = styled(SvgIcon)`
margin-right: 10px;
`;
And in your component, do like that:
import { Wrapper, IconStyled } from "./option.styles";
function Option({ title, Icon }) {
return (
<Wrapper>
{Icon && <IconStyled component={Icon} />}
<h4>{title}</h4>
</Wrapper>
);
}
const App = () => {
return (
<>
<Option title="title" Icon={HomeIcon}></Option>
<Option title="title" Icon={AccessAlarmIcon}></Option>
</>
);
};
2. As a Font Icon (font-icons):
Import material icons in <head>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
option.styles.js as:
import styled from "styled-components";
import Icon from "#material-ui/core/Icon";
export const Wrapper = styled.div`
display: flex;
color: grey;
&:hover {
color: black;
}
`;
export const IconStyled = styled(Icon)`
margin-right: 10px;
`;
And in your component, do like that:
import { Wrapper, IconStyled } from "./option.styles";
function Option({ title, icon }) {
return (
<Wrapper>
{icon && <IconStyled>{icon}</IconStyled>}
<h4>{title}</h4>
</Wrapper>
);
}
const App = () => {
return (
<>
<Option title="title" icon='star'></Option>
<Option title="title" icon='home'></Option>
</>
);
};

Related

How can I reuse other external styled-components

I want to load an external styled component and use extending styles, but it doesn't work. How can I solve this problem?
common/Button.js
import styled from 'styled-components';
const StyledButton = styled.button`
padding: 1rem 2rem;
border: none;
border-radius: 8px;
`;
const Button = ({ children, onClick }) => {
return <StyledButton onClick={onClick}>{children}</StyledButton>;
};
export default Button;
pages/Login.js
import Button from '../Components/common/Button';
import styled from 'styled-components';
const StyledButton = styled(Button)`
// not working
color: red;
`;
const Login = () => {
return (
<div>
<StyledButton>Submit</StyledButton>
</div>
);
}
export default Login;
enter image description here
Component can be styled only if it passes className property to it's child. So you should make your Button component like this:
const Button = ({ children, onClick, className }) => {
return <StyledButton onClick={onClick} className={className}>{children}</StyledButton>;
};

Generating a grid-container style when mapping through an array

Summary
I have the following chart image (react-vis):
My React code (below) is creating this with a map through an array. How should I modify the code so the charts go across the page and are contained in some kind of fluid wrapper like this:
What have I looked at/tried?
I have basic understanding of HTML and CSS but would not know how to approach this kind of task and modify the code.
Would I need to use something like this and integrate with the code above?
<div class="grid-container">
<div className="grid-item">1</div>
<div className="grid-item">2</div>
<div className="grid-item">3</div>
<div className="grid-item">4</div>
</div>
I would like to understand an effective way to do this please using CSS, bootstrap or whatever would be considered best practice.
Code:
MyComp.js
import React, { useState, useEffect } from "react"
import Example from "./plotBar.js"
function getJson() {
return fetch("http://secstat.info/testthechartdata3.json")
.then(response => response.json())
.catch(error => {
console.error(error)
})
}
const MyComp = () => {
const [list, setList] = useState([])
useEffect(() => {
getJson().then(list => setList(list))
}, [])
return (
<div>
{list.map((data, index) => (
<Example
key={index}
data={data.map(({ id, count }) => ({
x: id,
y: count,
}))}
/>
))}
</div>
)
}
export default MyComp
plotBar.js
import React from "react"
import {
XYPlot,
XAxis,
YAxis,
VerticalGridLines,
HorizontalGridLines,
VerticalBarSeries,
} from "react-vis"
export default function Example({ data }) {
return (
<XYPlot margin={{ bottom: 70 }} xType="ordinal" width={300} height={300}>
<VerticalGridLines />
<HorizontalGridLines />
<XAxis tickLabelAngle={-45} />
<YAxis />
<VerticalBarSeries data={data} />
</XYPlot>
)
}
The data looks like this:
URL for JSON
http://secstat.info/testthechartdata3.json
You should read about flex and flex-flow, after that it just applying minor styling, this is CSS-in-JS example:
const Item = styled(Example)``;
const Container = styled.div`
display: flex;
flex-flow: row wrap;
background: lightgray;
padding: 0.5rem;
${Item} {
margin: 0.5rem;
padding: 1rem;
background: white;
}
`;
const Item = styled(Example)``;
const Container = styled.div`
display: flex;
flex-flow: row wrap;
background: lightgray;
padding: 0.5rem;
${Item} {
margin: 0.5rem;
padding: 1rem;
background: white;
}
`;
export default function Example({ data, className }) {
return (
<XYPlot className={className} xType="ordinal" width={200} height={200}>
<VerticalGridLines />
<HorizontalGridLines />
<XAxis tickLabelAngle={-45} />
<YAxis />
<VerticalBarSeries data={data} />
</XYPlot>
);
}
const list = // fetch on mount
const MyComp = () => {
return (
<Container>
{list.map((data, index) => (
<Item
key={index}
data={data.map(({ id, count }) => ({
x: id,
y: count,
}))}
/>
))}
</Container>
);
};

Adding image to button using Emotion css

I want to style two buttons: Up and Down with an image using emotion css but unable to do so. Currently, I am normally styling my elements within a function. How can I achieve this using emotion css?
I followed https://emotion.sh/docs/introduction but I am unable to implement it the right way.
import up from "../img/up.png";
function PostButton(props) {
let style = {
backgroundRepeat: 'no-repeat',
background: `url(${up})`,
paddingRight: 24,
paddingTop: 26,
paddingLeft: 26,
paddingBottom: 26.6
};
return (
<button style={style} onClick={() => props.handleClick()}>{props.background}</button>
);
}
//I have written similar code for PostButton2
function Post(props) {
return (
<div>
<Up >
<PostButton src={"../images/up.png"} handleClick= .
{props.incrementScore} />
</Up> >
<Down >
<PostButton2 src={"../images/down.png"}
handleClick{props.decrementScore} />
</Down>
</Col>
</Row>
</Container>
</div>
);
}
Assuming that src property holds the background image, I think you need to update background to use the prop like so:
background: `url(${props.src})`,
Use src prop as path to image.
// First way, with css from emotion/react
/** #jsx jsx */
import { jsx, css } from '#emotion/react'
const PostButton = ({ background, handleClick, src }) => (
<button css={css`
background: ${`no-repeat url(${src})`};
padding: 26px 24px 26.6px 26px;
`}
onClick={handleClick}>{background}</button>
)
// Second way, with css from emotion/css
import React from 'react'
import { css } from '#emotion/css'
const PostButton = ({ background, handleClick, src }) => (
<button className={css`
background: ${`no-repeat url(${src})`};
padding: 26px 24px 26.6px 26px;
`}
onClick={handleClick}>{background}</button>
)
// Third way, with css from emotion/css, but pass styles as object
import React from 'react'
import { css } from '#emotion/css'
const PostButton = ({ background, handleClick, src }) => (
<button className={css({
background: `no-repeat url(${src})`,
padding: '26px 24px 26.6px 26px'
})}
onClick={handleClick}>{background}</button>
)

CSS to React components

I started rewriting CSS into React Components but i've encountered a problem, I do know how to do this (using styled-components btw ) :
You have 5 ways to styling component in React.
Every approach have pros & cons(personally I use 4-th ways.)
1.Inline Css
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
2. CSS in JS
const Button = (props) => (
<button className={ 'large' in props && 'large' }>
{ props.children }
<style jsx>{`
button {
padding: 20px;
background: #eee;
color: #999
}
.large {
padding: 50px
}
`}</style>
</button>
)
/* Creates a regular button */
<Button>Hi</Button>
/* Creates a large button */
<Button large>Big</Button>
3. Styled Components
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
render(
<Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>
);
4. Css Modules (scss || sass)
#width: 10px;
#height: #width + 10px;
#header {
width: #width;
height: #height;
}
5. Stylable - https://github.com/wix/stylable
import React from 'react';
import styled, {css} from 'styled-components';
const Info = styled.div`
display: flex;
// etc.
${props => props.title && css`font-size: 15px`}
`
const Box = styled.div`
&:first-of-type {
// some more css
}
`
// usage
<Info title>some info</Info>
I recommend you follow the official docs as stated in comments by #Mikkel

Extending styles with styled-components not working

I'm trying to extend styles for a react component using styled-components but is not working.
AFAIK, I'm doing it the right way, but perhaps I'm missing something...
Here is what I have:
import React from "react";
import styled from "styled-components";
const TextContainer = ({ text }) => {
return <p dangerouslySetInnerHTML={{ __html: text }} />;
};
const Paragraph = styled(TextContainer)`
background: red;
`;
class Home extends React.Component {
render() {
const { t } = this.props;
return <Paragraph text="This is a test" />;
}
}
export default Home;
Of course, the expected result is to have a red background on p, but right now the output looks like this:
Any idea on how to solve this? Probably I'm missing something, but I can't realize what.
Thanks is advance!
As stated in documentation:
The styled method works perfectly on all of your own or any
third-party components, as long as they attach the passed className
prop to a DOM element.
Example
// This could be react-router-dom's Link for example, or any custom component
const Link = ({ className, children }) => (
<a className={className}>
{children}
</a>
);
const StyledLink = styled(Link)`
color: palevioletred;
font-weight: bold;
`;
render(
<div>
<Link>Unstyled, boring Link</Link>
<br />
<StyledLink>Styled, exciting Link</StyledLink>
</div>
);
Ref: https://www.styled-components.com/docs/basics#styling-any-component
I didn't know that was a way to do it.
I would do:
const Link = styled.a`
..put you css styles here (className styles)
`
const StyledLink = styled(Link) `
color: palevioletred;
font-weight: bold;
`
render(){
return(
<div>
<Link>Unstyled, boring Link</Link>
<br />
<StyledLink>Styled, exciting Link</StyledLink>
</div>
)
}

Resources