conditional className convert to styled component - javascript

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.

Related

conditional operator in react not working with css

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' : ''}`}

Specific targeting of CSS classes with Styled Components not being rendered - React

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

Using styled component

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).

How to display some value with styled-components?

I'm new to styled-components and I'm bit confused.
Can we display something or add functionality to styled-compoentns.
OR styled-components is component that we can apply css only
styled-components is primarily intended to apply css.
So typically you would use wrapper components that provide the content and use the components obtained from styled-components for decoration.
Once in a while, I have found it useful to use the .attrs constructor to pass children when
the content is very specific to the component.
const ResourceMissingError= styled.div.attrs({
children: 'This resource could not be found'
})`color: red`;
render(<ResourceMissingError />);
Can we display something or add functionality to styled-components?
Yes, styled components are usable as any native component would be. So just as HTML's <button> can be used to display something, you can use a styled button to do so. See below.
Similarly, you can add functionality as you would in a native component, by listening to click events, for instance. The demo below "adds" functionality to the ColorfulButton by handling its click event.
See also how the color is passed as a prop to the ColorfulButton via mycolor="green":
const ColorfulButton = styled.button`
display: inline-block;
color: ${props => props.mycolor || "blue"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
display: block;
`;
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.state = { text: "Learn JavaScript (click me)", done: true }
}
handleClick = e => {
this.setState({...this.state, done: !this.state.done});
}
render() {
return (
<div>
<ColorfulButton onClick={this.handleClick} mycolor="green">{this.state.text}</ColorfulButton>
<br />
{this.state.done ? 'Yes' : 'No'}
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<div id="app"></div>
You can make a CSS file and import it in your different components like making class in CSS and use that class in your component as className="". Also, you can refer inline CSS like this way style={{}} make sure the properties name like font-size will be written in fontSize in React inline CSS. Every CSS property that have a dash in the middle of the property name, the dash will be removed and the next letter after dash will be capitalized and also the property value will be in double or single quotation.

Styling a nested styled-component from an outer one

Using styled-components, I am trying to style a nested <Input /> component that I have created, which is being used within a different component that has a dropdown appear when typing. I need to add padding-left: 3rem to this nested input but I cannot access it from the component <Dropdown />.
<Dropdown
options={options}
/>
The above is imported where I need it. I need to access the below input from the above <Dropdown />.
<div>
<Input {...props}/> // I need to edit the padding in this component
// rendered input unique to this new component would go here
</div>
The above <Input /> is imported from another component which is used in all instances where I require an input.
export const Dropdown = styled(DropDown)`
padding-left: 3rem !important;
`;
The component works fine but this fails to affect the inner padding of the Input that I need to target.
What do I do?
From what you've said, I'd suggest that the dependency of padding the Input component is with your Dropdown (which you seem to realise already).
Therefore you'd be better off having that "unqiue" styling coupled with your Dropdown component via a wrapping styled component within it.
The following example is crude (and by no means complete or working), but hopefully it illustrates how the ownership of the padding-left should be within the Dropdown and not a sporadic styled component floating some where else in your code base.
./Input/Input.jsx
const Input = ({ value }) => (
<input value={value} />
);
./Dropdown/styled.js
const InputWrapper = styled.div`
position: relative;
padding-left: 3rem !important; /* Your padding */
`;
const Icon = styled.div`
position: absolute;
top: 0;
left: 0;
width: 3rem;
height: 3rem;
background: blue;
`;
const Menu = styled.ul`/* whatever */`;
./Dropdown/Dropdown.jsx
import Input from '...';
import { InputWrapper, Icon, Menu } from './styled';
const Dropdown = ({ options }) => (
<div>
<InputWrapper>
<Icon />
<Input value={'bleh'} />
</InputWrapper>
<Menu>{options}</Menu>
</div>
);
This setup will promote reusable self-contained components.
Figured out the solution below:
export const StyledInput = styled.div`
&& #id-for-input { // specifically on the <Input />
padding-left: 3rem !important;
}
`;
<StyledInput>
<Dropdown />
</StyledInput>

Categories