how to pass multiple classNames to inner children with emotion js - reactjs

I want to split the components into baseUI one and styled one:
eg.
MyComponent.jsx
export default class MyComponent extends React.Component {
...
...
render() {
const { wrapperClassName, className, childClassName } = this.props;
return (
<div className={wrapperClassName>
<div className={className />
<div className={childClassName} />
</div>
)
}
}
StyledMyComponent.jsx
import styled from 'react-emotion'
const StyledMyComponent = styled(MyComponent)(
...
...
)
export default StyledMyComponent
however anything I put to the styled function's argument they will go to the className only, is there a way I specify which props goes to which className?
also can I do something like sass/less with children selector?
hypothetically something like this:
const classes = css`
color: red;
span { // this works
color: black;
}
.childClassName { // this doesn't work
color: green;
}
`
<MyComponent className={classes} />

No you can't.
What you can do, is create specific components for the underlying div. This is how I make my components:
const MyComponentStyle = styled('div')....;
const MySecondComponentStyle = styled('div')...;
const MyThirdStyle = styled('div')...;
const MyComponent = ({ wrapperClassName, childClassName, className }) =>
<MyComponentStyle className={wrapperClassName}>
<MySecondComponentStyle className={className} />
<MyThirdStyle className={childClassName} />
</MyComponentStyle>
)
}
}
Conditionally styling the element and its children based on class names
You can conditionally change the styling of stuff below the main component based on its classes.
Taking your example:
const Something = () => (
<MyComponent className={classes}>
<div className="childClassName">child</div>
<div className="otherChildClassName">child</div>
</MyComponent>
You can style the children like so:
const classes = css`
color: red;
span {
color: black;
}
& .childClassName {
color: green;
}
`
note the & character. It essentially means "this class". So & .childClassName means "childrens of this element with class childClassName.
You could also use &.someClassName (note the lack of space), which would mean: "this element when it also has a class named someClassName.

Related

How to use props variable for css-modules className?

Is it possible to use a props variable for a css-modules className?
// Component.js
import styles from "./Component.module.scss"
const Component = ({ color }) =>
<div className={`${styles.component}` `${styles.color}`>
Component
</div>
// Component.module.scss
.component { border: 1px black solid; }
.red { color: red; }
.green { color: green; }
Then I could use the Component like so:
// App.js
<Component color="red" />
<Component color="green" />
And have the two Components be red and green respectively.
I think you've missed a bracket
const Component = ({ color }) => {
const cssColor = color;
return (
<div className={`${styles.component}` `${styles[cssColor]}`}>
Component
</div>
)
}
To use Component level CSS you can get it loaded in your webpack as using a loader (Reference)
When using webpack, you can add the loader and also include the module to your webpack.config.js in other to make CSS modules work with Webpack.
test: /\.css$/,
loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
}
Alternatively, you could use a library called classnames
Following works:
const Component = ({ color }) => {
const cssColor = color;
return (
<div className={`${styles.component}` `${styles[cssColor]}`>
Component
</div>
)
}

styled-components: style based on non-styled class parent

Let's say I have this:
const Child = styled.button`
background-color: grey;
`;
<div className="some-non-styled-class">
<Child>Hello World</Child>
</div>;
<div className="some-other-non-styled-class">
<Child>Hello World</Child>
</div>;
How can I style Child so it's "red" when under some-non-styled-class and "blue" when it's under some-other-non-styled-class?
I'm aware of the ability to refer to other styled-components, but I have a use case where I need to change styling based on an existing library with static class names.
You could try something like this then:
export const Child = () => {
const [parentClass, setParentClass] = useState("")
useEffect(() => {
const currentClass = document.querySelector(".childComp").parentElement.className
setParentClass(currentClass)
},[])
return (<MyStyledComponent className="childComp" parentClassName={parentClass}>
{children}
</MyStyledComponent>)
}

React styled component not passing props from attribute

I'm trying (unsuccessfully) to pass a single color value from a parent to a child using styled-components.
If the color is passed correctly, the background should be the color that is passed. Else green.
No matter what I do, the background-color is green
What am I missing here?
The parent:
function App() {
return (
<div>
<Article color="red"/>
</div>
);
}
The child:
const MainContetnt = styled.div`
background-color: ${props => props.color ?? "green"};
flex: 1;
`;
const Article = (props) => {
return (
<MainContetnt>
Main content
</MainContetnt>
)
};
Props will not be passed automatically to a styled component, you still have to do it the usual way:
const Article = (props) => {
return (
<MainContetnt color="props.color">
Main content
</MainContetnt>
)
};

Passing custom props to each styled component through Provider

I would like to pass a custom prop (exactly: theme name as string) to each passed styled component through Provider, so it was available throughout the css definition.
ThemeProvider almost does it, but it expects object, not the string. I do not want to pass whole object with theme settings, just the name of my theme.
I do not want to use special theme prop or similar, because then I would have to it manually every single time I create new styled component. Provider seems like the best option if only it cooperated with string.
Is there any possibility to pass a string through Provider to Consumer builded in styled components?
EDIT:
[PARTIAL SOLUTION]
I found what I was looking for when I realized styled-components exports their inner context. That was it. Having access to pure react context gives you original Provider, without any 'only objects' restriction ('only objects' is a styled-components custom provider restriction).
Now I can push to each styled component exactly what I want and if I want.
import styled, { ThemeContext } from 'styled-components';
const StyledComponent = styled.div`
color: ${props => props.theme == 'dark' ? 'white' : 'black'};
`;
const Component = props => {
const theme = 'dark';
return (
<ThemeContext.Provider value={theme}>
<NextLevelComponent>
<StyledComponent />
</NextLevelComponent>
</ThemeContext.Provider>
);
};
Hope I have this correct, from what I've been able to glean. I haven't tried this out but it seems it might work for you. This is lifted directly from the reactjs.org docs regarding context. It passed the string name of the theme down.
const ThemeContext = React.createContext('green');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="blue">
<SomeComponent />
</ThemeContext.Provider>
);
}
}
function SomeComponent(props) {
return (
<div>
<OtherComponent />
</div>
);
}
class OtherComponent extends React.Component {
static contextType = ThemeContext;
render() {
return <ThirdComponent theme={this.context} />
}
}
I hope this helps you understand the idea behind ThemeContext from styled-components. I've passed string "blue" to ThemeContext just to show, that it should not be object and you can use just string.
import React, { useContext } from "react";
import ReactDOM from "react-dom";
import styled, { ThemeContext } from "styled-components";
// Define styled button
const Button = styled.button`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
color: ${props => props.theme};
border: 2px solid ${props => props.theme};
`;
// Define the name of the theme / color (string)
const themeName = "blue";
const ThemedButton = () => {
// Get the name from context
const themeName = useContext(ThemeContext);
return <Button theme={themeName}>Themed color: {themeName}</Button>;
};
function App() {
return (
<div className="App">
<ThemeContext.Provider value={themeName}>
<ThemedButton />
</ThemeContext.Provider>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Demo: https://codesandbox.io/s/styled-components-example-with-themecontext-cso55

How to use the styled-component property innerRef with a React stateless component?

I have the following styled component:
const Component = styled.div`
...
`;
const Button = (props) => {
return (
<Component>
...
</Component>
);
};
export default styled(Button)``;
I want to get a reference to the underlying div of Component. When I do the following I get null:
import Button from './Button.js';
class Foo extends React.Component {
getRef = () => {
console.log(this.btn);
}
render() {
return (
<Button innerRef={elem => this.btn = elem} />
);
}
}
Any ideas why I am getting null and any suggestions on how to access the underlying div?
Note: The reason I am doing this export default styled(Button)``; is so that the export styled component can be easily extended.
I managed to accomplish this by passing a function down as a prop to the styled-component that I was targeting, then passing the ref back as an argument of the function:
const Component = styled.div`
...
`;
const Button = (props) => {
return (
<Component innerRef={elem => props.getRef(elem)}>
...
</Component>
);
};
export default styled(Button)``;
...
import Button from './Button.js';
class Foo extends React.Component {
getRef = (ref) => {
console.log(ref);
}
render() {
return (
<Button getRef={this.getRef} />
);
}
}
Passing a ref prop to a styled component will give you an instance of the StyledComponent wrapper, but not to the underlying DOM node. This is due to how refs work. So it's not possible to call DOM methods, like focus, on styled components wrappers directly.
Thus to get a ref to the actual, wrapped inner DOM node, callback is passed to the innerRef prop as shown in example below to focus the 'input' element wrapped inside Styled component 'Input' on hover.
const Input = styled.input`
padding: 0.5em;
margin: 0.5em;
color: palevioletred;
background: papayawhip;
border: none;
border-radius: 3px;
`;
class Form extends React.Component {
render() {
return (
<Input
placeholder="Hover here..."
innerRef={x => { this.input = x }}
onMouseEnter={() => this.input.focus()}
/>
);
}
}
render(
<Form />
);
NOTE:-
String refs not supported (i.e. innerRef="node"), since they're already deprecated in React.

Resources