I have a button which need to be styled using styled component. As I am new to react I need your help in getting button color change to green on success and blue on failure. Also I have class card-preview which need to have box shadow when I hover on it while using styled component .The below code will give you an idea what I am trying to do.
import styled from "styled-components";
const Button = styled.button`
border: none;
border-radius: 10px;
padding: 7px 10px;
font-size: 1.3rem;
background: ${(props) => (props.success ? "green" : "blue")};
color: white;
`;
const Cardlist = ({ cards, title }) => {
return (
<div className="card-list">
<h2>{title}</h2>
{cards.map((card) => (
<div className="card-preview" key={card.id}>
<h2>{card.name}</h2>
if({card.status}==='success')
<Button success>{card.status}</Button>
else
<Button pending>{card.status}</Button>
</div>
))}
</div>
);
};
export default Cardlist;
For the button and CSS I think what you have should be working, though it could be more DRY. The if-else isn't valid in JSX anyway. I'm also not sure what you do with the pending prop, but it appears to be the antithesis of the success "state".
const Button = styled.button`
border: none;
border-radius: 10px;
padding: 7px 10px;
font-size: 1.3rem;
background: ${(props) => (props.success ? "green" : "blue")};
color: white;
`;
...
{cards.map((card) => (
<div className="card-preview" key={card.id}>
<h2>{card.name}</h2>
<Button
success={card.status === 'success'}
pending={card.status !== 'success'} // <-- may not be necessary
>
{card.status}
</Button>
</div>
))}
To apply some style to the card-preview div you just need to convert that to a styled-component as well.
const CardPreview = styled.div.attrs(() => ({
className: "card-preview",
}))`
:hover { // <-- sometimes you need &:hover to get the parent node
// apply box-shadow and any other CSS rules here
}
`;
Then replace the div with CardPreview component.
{cards.map((card) => (
<CardPreview key={card.id}>
<h2>{card.name}</h2>
<Button success={card.status === 'success'} >
{card.status}
</Button>
</CardPreview>
))}
See Pseudoselectors & Nesting
Update: Reagarding .attrs
This is a chainable method that attaches some props to a styled
component.
It simply allows you to specify prop values when the component is created versus when it's being used. The className prop looks to be a static "card-preview" so may as well set it once and forget it (in this specific case).
Related
I'm pretty new with React (and JS in general for that matter), but I'm trying to get a dropdown menu (that will eventually work like a query selector for a search bar) to work with some custom styling.
I found a perfect solution at w3schools with inline javascript. The problem is I can't seem to get it to work in a react application. Maybe it's as simple as some syntax changes between vanilla JS and React, but so far it doesn't work. Any advice would be appreciated.
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_custom_select
Blake.
So React components are typically built in a much different way to how this example would be built. React requires use of State to update the interface and we don't use tags.
I would recommend, instead of spending much time converting this example, finding an existing React drop-down package that works the same way.
This is a great existing package for creating React drop-downs and I have used it myself in the past. Follow the guide on there and you'll have it implemented in no time.
Hope this helped explain it and welcome to the React community!
Of course, we can use any available select item library out there. One of my favourites is headles.ui.
But if we want to master react, it is better to learn to make it from scratch.
So, Let's do it with React Way to make it works.
First of all, we need to keep in mind that React deals with state. So, we will use state to convert your example into React app.
Here, we will not use plain <select> & <option> tag since this is a Custom Select. The data we put inside an selectItems array:
const selectItems = [
"Audi",
"BMW",
"Citroen",
"Ford",
"Honda",
"Jaguar",
"Land Rover",
"Mercedes",
"Mini",
"Nissan",
"Toyota",
"Volvo",
];
Next, we define two state in our component which are active state and selected state.
// this state will be used in conditional rendering of our list items
// the value is boolean and the default is false (not active)
const [active, setActive] = useState(false);
// this state will keep our selected item
const [selected, setSelected] = useState("Select car:");
To open and close the List Items, we need to define a toggle method, that will toggle the active state by negating the previous state:
const toggle = () => setActive((prevState) => !prevState);
And whenever the user click a List Item, the element will set the selected state with the element item value, and hide the List Items by calling the toggle method:
const handleItemClick = (item) => {
setSelected(item);
toggle();
};
In order to render the selected item and the list items, we need to create two sections:
return (
<div>
...
{/* at onClick listener we pass toggle method */}
<div className="select-selected" onClick={toggle}>
// Selected Item section
</div>
<div className="select-items">
// Select Items section
</div>
</div>
)
At Selected Item, we define conditional class name and add selected state. The className will have value select-selected select-arrow-active if state active value is true and will be select-selected only if otherwise.
return (
...
<div className={`select-selected ${active ? 'select-arrow-active' : ''}`} onClick={toggle}>
{selected}
</div>
)
And lastly, we render the List items by using javascript map method:
return (
...
<div className="select-items">
{/* we render list items only when active state is true */}
{active && selectItems.map((item,i) => {
return (
<div
key={i}
onClick={() => handleItemClick(item)}
{/* when the selected item equal to the item then use "same-as-selected" className */}
className={`${item === selected ? "same-as-selected": ""}`}
>
{item}
</div>
)
})}
</div>
...
)
And finally, this is the complete code. Minimal example but at least have followed the given vanilla javascript example.
You can see our select item component in action by clicking the blue Run code snippet button below.
const { useState } = React;
const selectItems = [
"Audi",
"BMW",
"Citroen",
"Ford",
"Honda",
"Jaguar",
"Land Rover",
"Mercedes",
"Mini",
"Nissan",
"Toyota",
"Volvo",
];
function App() {
const [active, setActive] = useState(false);
const [selected, setSelected] = useState("Select car:");
const toggle = () => setActive((prevState) => !prevState);
const handleItemClick = (item) => {
setSelected(item);
toggle();
};
return (
<div>
<h2>Custom Select</h2>
<div className="custom-select">
<div className={`select-selected ${active ? 'select-arrow-active' : ''}`} onClick={toggle}>
{selected}
</div>
<div className="select-items">
{active && selectItems.map((item,i) => {
return (
<div
key={i}
onClick={() => handleItemClick(item)}
className={`${item === selected ? "same-as-selected": ""}`}
>
{item}
</div>
)
})}
</div>
</div>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('.react'));
.custom-select {
position: relative;
font-family: Arial;
color: #ffffff;
width: 200px;
}
.select-selected {
background-color: DodgerBlue;
padding: 8px 16px;
cursor: pointer;
}
.select-selected:after {
position: absolute;
content: "";
top: 14px;
right: 10px;
width: 0;
height: 0;
border: 6px solid transparent;
border-color: #fff transparent transparent transparent;
}
.select-selected.select-arrow-active:after {
border-color: transparent transparent #fff transparent;
top: 7px;
}
.select-items div,.select-selected {
color: #ffffff;
padding: 8px 16px;
border: 1px solid transparent;
border-color: transparent transparent rgba(0, 0, 0, 0.1) transparent;
cursor: pointer;
user-select: none;
}
.select-items {
position: absolute;
background-color: DodgerBlue;
top: 100%;
left: 0;
right: 0;
z-index: 99;
}
.select-items div:hover, .same-as-selected {
background-color: rgba(0, 0, 0, 0.1);
}
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-table#7.8.0/dist/react-table.development.js"></script>
<div class='react'></div>
Im trying to learn and convert my project from css to styled component(https://styled-components.com/), at the moment i have converted all my other components except one component where i am stuck, checked others examples from stackoverflow but it was not same kind.
I have conditional class names
My question is how to convert InfoBox component to use styled component ? do i need to inject the styles through some kind of styled-component wrapper or thats not needed ?
english is not my mother language so could be mistaked
my code:
import React from 'react'
import "./InfoBox.css"
function InfoBox({ isRed, active, activetored, ...props }) {
return (
<div onClick={props.onClick}
className={`infoBox ${active && "infoBox--selected"}
${activetored && "infoBox--selectedtored"}
${isRed && "infoBox--red"} `} >
</div>
)
}
export default InfoBox
<div className="app__stats">
<InfoBox
isRed
active={typeofCase === "cases"}
onClick={(e) => setTypeofCase('cases')}
/>
<InfoBox
isGreen
active={typeofCase === "recovered"}
onClick={(e) => setTypeofCase('recovered')}
/>
<InfoBox
isRed
activetored={typeofCase === "deaths"}
onClick={(e) => setTypeofCase('deaths')}
/>
</div>
css is like this (you can put whatever):
. infoBox--selected {
border-top: 10px solid greenyellow;
}
. infoBox--selectedtored {
border-top: 10px solid red;
}
. infoBox--red {
border-color: darkblue;
}
One of the ideas behind styled-component is to avoid classnames.
Instead of setting the css by class, you have few options. the easiest one will probably be to use your props inside the css code, and change the style by it:
const InfoBox = styeld.div`
border-color: ${props => props.isRed ? 'darkblue' : 'black'};
border-top: ${props => props.active ? '10px solid greenyellow' : 'red'};
...
`;
this way, you don't need classnames (although it can be done with it too, obviously).
Now, instead of the div inside the component, use the InfoBox styled component we just wrote and you good to go.
In my task component on the parent div I am trying to make it so when reminder is set to true it adds a "reminder" class to the div, which just adds a border left. If reminder is set to false the "reminder" class will not be added.
I have checked and on double click the reminder does toggle between true and false so I know that works.
const Task = ({ task, onDelete, onToggle }) => {
return (
<div
className={`task ${task.reminder ?
'reminder' : ''}`}
onDoubleClick={() => onToggle(task.id)} className="task">
<h3>{task.text} <FaTimes
onClick={() => onDelete(task.id)}
style={{
color: "red", cursor: "pointer"
}} />
</h3>
<p>{task.day}</p>
</div>
)
}
export default Task
.task {
background: #f4f4f4;
margin: 5px;
padding: 10px 20px;
cursor: pointer;
}
.task.reminder {
border-left: 5px solid green !important;
}
onToggle , you are not passing task.remainder , how will task.remainder changes ?
Please pass task.remainder in onToggle function
Your code must look like this .
<div className={task ${task.reminder ?'reminder' : ''}} onDoubleClick={() => onToggle(task.remainder)} className="task">
I think you are adding className 2 times in div element remove 2nd one then you will get your result. Actually, your logic is correct but you did that small mistake adding className 2 times.
Btw, there is 2 ways to implement that logic-
className={`${task.reminder ? 'task reminder' : 'task'}`}
className={`task ${task.reminder ? 'reminder' : ''}`}
I've been struggling with a styling issue for a while, but the basic issue is that I want to style every instance of <StyledButton> differently after the first. To do this, I'm targeting the wrapping element (attributeset-row className) and the remove-btn className (for <StyledButton>) like so:
const StyledHorizontalAttributesTable = styled(StyledHorizontalAttributes)`
& .attributeset-row:not(:first-child) .remove-btn {
background-color: lightblue;
}
`;
I have discovered the issue is that the CSS styles are not being applied to the components, due to my targeting of classNames - as you can see below I am passing in classNames to the relevant components (and they are showing in the browser), but along with a lot of other what looks to be jargon:
Can anyone explain where I might be going wrong with this in terms of applying specific CSS styles to StyledComponents (usually this type of styling isn't needed) but I need to style all <StyledButton>'s after the first differently.
Here's my code:
const StyledButton = styled(Button)`
margin-top: 14px;
`;
const StyledHorizontalAttributesTable = styled(StyledHorizontalAttributes)`
& .attributeset-row:not(:first-child) .remove-btn {
background-color: lightblue;
}
`;
return (
<div className={className}>
{objects.map((enteredObject, index) => (
<RepeatableAttributeSetContextProvider
form={form}
object={enteredObject}
key={`${enteredObject.key}-${enteredObject.repeatIndex}`}
>
<StyledHorizontalAttributesTable className="attributeset-row">
{enteredObject.attributeCollection.questions
.filter(filterRepeatAttributes)
.map((attribute) => (
<Fragment key={attribute.key}>
{renderAttribute(enteredObject, attribute, formLayout)}
</Fragment>
))}
<StyledButton
className="remove-btn"
type="link"
buttonStyle="LINK"
name="delete"
dataId={`delete-${enteredObject.key}-${index}`}
isOberonIcon
isIconButton
icon="bin"
onClick={() => onRemove(enteredObject)}
>
<Message id="Form.Button.Remove" defaultMessage="Remove" />
</StyledButton>
</StyledHorizontalAttributesTable>
</RepeatableAttributeSetContextProvider>
))}
</div>
);
I would use the adjacent sibling combinator. This will target all .attributeset-rows but the first one.
const StyledHorizontalAttributesTable = styled(StyledHorizontalAttributes)`
& + & {
.remove-btn {
background-color: lightblue;
}
}
`;
Here's a simple version of this example over at CodeSandbox. https://codesandbox.io/s/serene-swanson-frcb4?file=/src/App.js
Is it possible to style Text or Paragraphs with styled-components? If yes, how can I pass text into the component?
For example, I had this Footer component:
const Footer = () => (
<footer className="site-footer">
<p className="text"> HELLO</p>
<Copyright
link={'https://hotmail.com'}
text={'HELOO'}></Copyright>
</footer>
);
export default Footer;
I wanted to switch from global classes to css in js, which is why I thought of using styled-components. Now I tried this:
export const StyledText = styled.text`
text: Hellooo
color: white;
text-align: center;
`
But if I comment out the paragraph and use StyledText component, nothing shows up. If I try passing Styled Component text={'HELLO'} my app crashes. How can I convert my footer in such a way that it uses styled-components?
You can update your component to look like this:
import styled from 'styled-components';
const StyledText = styled.p`
color: white;
text-align: center;
`;
export default function Footer({text}){
return <footer className="site-footer">
<StyledText>{text}</StyledText>
<Copyright
link={'https://hotmail.com'}
text={text}/>
</footer>;
}
You will be able to call your Footer component like:
<Footer text="HELLO"/>
Hope this helps,
styled-components just deals with the styles of a component and not the content. All children will be preserved, so you can do something like this:
// JSX
<StyledText>Hello</StyledText>
// StyledText.js
export default styled.p`
color: white;
text-align: center;
`;
Yes, you can style every html element and every custom component as long its passes className.
This example covers pretty much the basic API:
const Text = styled.p`
color: blue;
text-align: center;
`;
const Copyright = ({ className, link, text }) => (
<div className={className}>
<Text>{link}</Text>
<Text>{text}</Text>
</div>
);
const StyledCopyright = styled(Copyright)`
border: 1px black solid;
${Text} {
color: palevioletred;
}
`;
const Footer = () => {
return (
<footer>
<Text>HELLO</Text>
<Copyright link="https://hotmail.com" text="World" />
<StyledCopyright link="https://hotmail.com" text="World" />
</footer>
);
};