React styled component child prop selector - javascript

is it possible to make selector for child component prop in styled components?
<Accordion>
<Checkbox checked='false' />
<Text>Text to be hidden when checked is false</Text>
</Accordion>
I would like to access the prop something like this:
const Accordion = styled.div`
& > ${Checkbox}[checked='false'] ~ ${Text} {
display: none;
}
`;
Is it possible and if so, how should I do it?

You are trying to use Attribute selectors, so you need to define valid attributes on Checkbox component like data-*.
If you trying to use component's property, you have to lift the state up (see Text with "State from Parent").
const Checkbox = styled.div``;
const Text = styled.div``;
const Accordion = styled.div`
& > ${Checkbox}[data-checked="true"] ~ ${Text} {
color: palevioletred;
font-weight: 600;
&:last-child {
color: ${prop => (prop.checked ? `blue` : `orange`)};
}
}
& > ${Text}[title="example"]{
border: 1px solid black;
}
`;
const App = () => {
return (
<Accordion checked>
<Checkbox data-checked="true" checked="true">
I'm Check box
</Checkbox>
<Text title="example">With attr gives border</Text>
<Text>Without attr</Text>
<Text>State from Parent</Text>
</Accordion>
);
};

Related

React inline styles not reverting back to more general properties when removing more specific properties

I have a react component which uses inline styles and allows additional override styles to be passed as props.
function Input(props) {
return (
<input
style={{
borderRadius: 10,
border: '2px solid white',
...props.style,
}}
/>
);
}
In one specific use-case of the component, I want to remove the bottom border and get rid of the border radius on the two bottom corners of the component when some condition is met:
<Input
style={!condition ? {} : {
borderBottom: 0,
borderBottomRightRadius: 0,
borderBottomLeftRadius: 0,
}}
/>
This works as expected both on initial render, and once the condition is updated to true, however, once the condition is reverted back to false, the bottom border radius stays 0 rather than reverting back to the 10px specified by the borderRadius.
What seems to be happening, is once the bottom border radii are set to 0, instead of keeping borderRadius and overriding it with the more specific properties I set in the conditional style, react is instead replacing borderRadius with borderTopLeftRadius and borderTopRightRadius (i.e. the ones that weren't set in the conditional style).
After condition is toggled back to false this persists, rather than borderRadius being set on the element again even though the conditional style is now empty.
Is there any way around this other than having to reset the initial values in the falsy branch of the condition? I don't want the initial values of the Input style to need to be known outside of the Input function.
Thanks
Instead of passing the style to the child component, you can pass a condition and change the style depending on its value, like this:
function Input({ condition }) {
return (
<input
style={{
background: "red",
borderRadius: `${condition ? "10px" : "10px 10px 0 0px"}`,
border: "2px solid white",
borderBottom: `${condition ? "0" : "2"}px solid white`,
}}
/>
);
}
function App() {
const [condition, setCondition] = useState(false);
return (
<div>
<button onClick={() => setCondition(prev => !prev)}>Change</button>
<Input condition={condition} />
</div>
);
}
Edit:
The anothere way I could think of is by using custom libraly styled-compenents where you could do somethin like this:
import React, { useState } from "react";
import styled, { css } from "styled-components";
const CustomInput = styled.input`
background: red;
border-radius: 10px;
border: 2px solid white;
${(props) => props.additionalStyles}
`;
function Input({ additionalStyles }) {
return <CustomInput additionalStyles={additionalStyles} />;
}
function App() {
const [condition, setCondition] = useState(false);
return (
<div>
<button onClick={() => setCondition((prev) => !prev)}>Change</button>
<Input
additionalStyles={
!condition ? ""
: css`
border-bottom: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
`
}
/>
</div>
);
}
Here you can find more about css function from styled-components: Link

I want to change display of a div when clicked -React Styled Component

I am creating a div using styled component. I want to change the visibility of the div on button clicked,
const Category = () => {
const [showCategory, setShowCategory] = useState(false)
useEffect(() => {
setShowCategory(false)
}, [])
return (
<button onClick={() => { setShowCategory(true)}}>
New Category
</button>
<AdminInputStyle>
<form>
<form-group>
<label>Add Category</label>
<input type='text' />
</form-group>
<button>Submit</button>
</form>
</AdminInputStyle>
)
}
Here's the styled component
const AdminInputStyle = styled.div`
display: ${(d) => (d.showCategory ? 'show' : 'hidden')};
`
You can try something like this too, show when you need to show the add category when you press add category
return (
<>
<button
onClick={() => {
setShowCategory(true);
}}
>
New Category
</button>
{showCategory && (
<AdminInputStyle>
<form>
<form-group>
<label>Add Category</label>
<input type="text" />
</form-group>
<button>Submit</button>
</form>
</AdminInputStyle>
)}
</>
);
I have an example, but in the case we will use a Button. Clicking it will alter the visibility.
You must pass a property to the styled component if you want it to be visible based on that prop. In your example, you don't pass a prop to the styled component in this scenario, which is why the component cannot detect if it should be visible or not.
You will need to / can use the css function from the styled-components library. This can help you return styles based on the properties your styled-component will have. In this example, our property that we pass to the button will be called visible.
import React from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components/macro';
const StyledButton = styled.button`
border-radius: 3px;
color: white;
background-color: green;
cursor: pointer;
width: 100px;
height: 50px;
${({ visible }) => {
return css`
visibility: ${visible ? 'visible' : 'hidden'};
`;
}}
`;
export default function Button({ children, visible, onClick }) {
return (
<StyledButton visible={visible} onClick={onClick}>
{children}
</StyledButton>
);
}
Button.propTypes = {
children: PropTypes.node,
visible: PropTypes.bool,
onClick: PropTypes.func,
};
You can see that passing the visible prop will enable the button to alter its' styles based on whether that property is true or false. We utilize a function within the component that returns the css function and this will control the visibility css property.
Here is how we utilize the button and pass props to it from another component; in this example just the App.js file:
import React, { useState } from 'react';
import './App.css';
import Button from './components/Button';
function App() {
const [visible, setVisible] = useState(true);
function handleClick() {
setVisible(!visible);
}
return (
<div className="App">
<Button visible={visible} onClick={handleClick}>
Click
</Button>
</div>
);
}
export default App;
FYI: For the css; you don't want display: hidden;. hidden is an invalid value for the display prop. You'd want display: none; if you don't want the element to be in the DOM. visibility: hidden; will add the element to the DOM, but it won't be visible. You can use whichever works best for your case 👍🏿

Toggle class on button click in react js using state without re rendering component

I am trying to change the class of a button in react js on click using hooks. The problem is when the class toggle happens through a state change, the result of the class toggle is seen but the page re-renders, rendering the initial class (not the one that is toggled to). Please help
Buttons
<button className={buttonColor1?styles.priceButtonWhite:styles.priceButtonGreen} onClick={() => changeColor(1)}>100</button>
<button className={buttonColor2?styles.priceButtonWhite:styles.priceButtonGreen} onClick={() => changeColor(2)}>200</button>
<button className={buttonColor3?styles.priceButtonWhite:styles.priceButtonGreen} onClick={() => changeColor(3)}>300</button>
Change Color Function
const changeColor = (n) => {
if (n==1){
setButtonColor1(!buttonColor1);
}
else if (n==2){
setButtonColor2(!buttonColor2);
}
else{
setButtonColor3(!buttonColor3 );
}
}
Hooks
const [buttonColor1, setButtonColor1] = useState(true);
const [buttonColor2, setButtonColor2] = useState(true);
const [buttonColor3, setButtonColor3] = useState(true);
SCSS
.priceButton{
border-radius: 10px;
padding: 10px;
border: none;
}
.priceButtonWhite{
#extend .priceButton;
background: white;
border: 1px solid #166C6C;
}
.priceButtonGreen{
#extend .priceButton;
background: #166C6C;
}
On button click, the class gets toggled to priceButtonGreen, then the entire page re-renders leaving the class priceButtonWhite on the buttons
When you have button element inside form element then clicking on a button will cause the page to reload (button element has type attribute set to submit by default).
You can either change the type of a button to button or use event.preventDefault() in the button listener.
I created a working example here: https://codesandbox.io/s/suspicious-curie-90t4c?file=/src/App.js
Since the style object is missing in your example It's hard to see what is the mistake in your code.
function Toggler() {
const [buttonColor1, setButtonColor1] = useState(true);
const [buttonColor2, setButtonColor2] = useState(true);
const [buttonColor3, setButtonColor3] = useState(true);
return (
<div>
<button
className={`priceButton ${
buttonColor1 ? "priceButtonWhite" : "priceButtonGreen"
}`}
onClick={() => setButtonColor1(!buttonColor1)}
>
100
</button>
<button
className={`priceButton ${
buttonColor2 ? "priceButtonWhite" : "priceButtonGreen"
}`}
onClick={() => setButtonColor2(!buttonColor2)}
>
200
</button>
<button
className={`priceButton ${
buttonColor3 ? "priceButtonWhite" : "priceButtonGreen"
}`}
onClick={() => setButtonColor3(!buttonColor3)}
>
300
</button>
</div>
);
}
.priceButton {
border-radius: 10px;
padding: 10px;
border: none;
}
.priceButtonWhite {
background: white;
border: 1px solid #166c6c;
}
.priceButtonGreen {
background: #166c6c;
}
Within the structure you proposed this is a working solution.

Add Classes to Styled Component

I am trying to add class names to a React Component to make it easier for me to customize that component using Styled Components. Here is a simplified outline of the component:
const SignupForm = props => (
<form>
<Input className="input" />
<Button className="button" />
</form>
)
And here is how I would like to use it:
import { SignupForm } from '../path/to/signup-form'
<Form />
...
const Form = styled(SignupForm)`
.input {
/* Custom Styles */
}
.button {
/* Custom Styles */
}
`
However, this does not work. Only if I create a wrapper Component will it work - like this:
import { SignupForm } from '../path/to/signup-form'
<FormWrapper>
<SignupForm/>
<FormWrapper>
...
const FormWrapper = styled.div`
.input {
/* Custom Styles */
}
.button {
/* Custom Styles */
}
`
I'm wondering whether or not there is a way to access the .input and .button classes without having to create a wrapper class, i.e. via the actual imported class itself? If so, how?
You need to provide className for the wrapper/container as styled-component injecting the styles through it:
const SignupForm = ({ className }) => (
<form className={className}>
<input className="input" />
<button className="button">Button</button>
</form>
);
const Form = styled(SignupForm)`
.input {
background-color: palegreen;
}
.button {
background-color: palevioletred;
}
`;
Just add extra atrribute className by using attrs to existing styled component.
const FormWrapper = styled.div.attrs({
className: 'SignupForm',
})`
.input {
/* Custom Styles */
}
.button {
/* Custom Styles */
}
`

Styled component: Herencia

I am writing a React application with styled components, creating a library of reusable components for my application, but I encounter the problem of inheritance between sister components when trying to give a property to a label that is next to my input when the input is required, but it does not work. I have tried with:
// Form.js
import { StyledLabel, StyledInput } from './styles.js'
<StyledLabel>Mi Label 1</StyledLabel>
<StyledInput required/>
// ./styles.js
import styled from 'styled-components'
export const StyledInput = styled.input`
border: 1px #dddd solid;
`
export const StyledLabel = styled.label`
font-size: 10px;
${StyledInput}['required'] + & {
&::after {
content: '*'
}
}
`
The result only returns the form without the *
Does anyone know how I can detect from Styled Components when an input has the required HTML attribute, and show the *
Pseudo-selectors in styled-components work just like they do in CSS. (or rather, Sass).
So you can do what you want this way:
const Wrapper = styled.div`
label {
&:after {
content: "${p => p.required && "*"}";
color: red;
}
}`;
const Form = () => {
return (
<Wrapper required>
<label>Mi Label 1</label>
<input />
</Wrapper>
);
};
But if you want don't want to pass props of the input element to its parent you can
do it this way:
const Wrapper = styled.div`
display: flex;
flex-direction: row-reverse;
align-items: center;
`;
const Example = styled.input`
+ label {
&:after {
content: "${p => p.required && "*"}";
color: red;
}
}
`;
const Form = () => {
return (
<Wrapper required>
<Example />
<label>My Label 1</label>
</Wrapper>
);
};
more info in resources:
Before and After pseudo classes used with styled-components
https://github.com/styled-components/styled-components/issues/388
https://github.com/styled-components/styled-components/issues/74

Categories

Resources