React - styled components constructor from variable - reactjs

Is there a way to pass the return of another variable to the styled constructor "styled(xx)"?
I get an error: Objects are not valid as a React child (found: object with keys {$$typeof, render, propTypes}).
import styled from 'styled-components/macro';
import { Link as LinkR } from 'react-router-dom';
import { Link as LinkS } from 'react-scroll';
const Link = props => (props.scroll ? LinkS : LinkR);
export const BtnContainer = styled(Link)`
margin: 16px 0;
padding: 16px 22px;
`
If I wrap the imported Link objects into `` or '' in the Link variable, it only outputs the return to the DOM. Ok, I expected that. :) But it shows me that something can be passed on to the constructor.
Wrapping in {} or $ {} do not work either.

Have you tried returning the rendered link components?
const Link = (props) => props.scroll ? <LinkS {...props} /> : <LinkR {...props} />;
This makes Link an actual component and styled-components can handle them

Related

How to use sx prop in styled components without mui

Instead of using multiple props for each css attribute, I want to make an sx prop like material-ui has. I'm trying to implement this with styled-components. The idea is that I can pass an object as a prop, and reference a value from the object in the styled component.
things to note:
material-ui is not added as a dependancy. I want to "clone" sx from it.
currently looking into prop-types to add some value in finding bug.
Button Component
import styled from "styled-components";
export const Button = styled.button`
background-color: ${(sx) => sx.backgroundColor};
color: ${(sx) => sx.color};
`;
Rendering Button
import React from "react";
import styled from "styled-components";
import { Button } from "./Button/Button";
function App() {
return (
<>
<Button sx={{ backgroundColor: "red", color: "white" }} />
<>
);
}
export default App;
Update:
After adding prop-types to maybe find the issue, I get an error when I pass the prop to the Button component when I added this code:
// typechecking prop types passed
Button.propTypes = {
sx: PropTypes.string,
};
And the error I got back:
Warning: Failed prop type: Invalid prop `sx` of type `object` supplied to `styled.button`, expected `string`
Im not sure exactly where its going wrong. I've tried doing a console.log to see the value passed, which seems to be a string. Therefore dot notation should work?
Any help would be appreciated, thanks very much :)
After adding prop-types and finding the passed types, I was able to find this answer:
// required prop to be passed as an object
Button.propTypes = {
sx: PropTypes.object,
};
// updating the props for Button component
export const Button = styled.button`
background-color: ${(props) => props.sx.backgroundColor};
color: ${(props) => props.sx.color};
`;
then using sx as normal...
import React from "react";
import styled from "styled-components";
import { Button } from "./Button/Button";
function App() {
return (
<>
<Button sx={{ backgroundColor: "red", color: "white" }} />
</>
);
}
export default App;

How to focus trap with styled-components? How to access classname from styled-components?

I'm currently using styled-components 5.0.1 with React. https://styled-components.com/
For context, my goal is to focus trap within a modal.
My problem is that classnames are randomly generated from styled-components so I'm not able to access these DOM nodes with querySelector. My other problem is that I'm not able to use React ref forwarding since I would have to do a lot of ref forwarding between component trees.
Is there a way to access the classname that is generated from styled-components? If so, I can use querySelector and go about the usual way of focus trapping by accessing the DOM nodes through querySelector.
Firstly, your component should be able to process the passed-in css:
import styled from "styled-components";
const Box = styled.div`
color: red;
${props => props.addCSS}
`;
const DatePicker = () => <Box>DatePicker</Box>;
Secondly, declare the css style.
import { css } from "styled-components";
const myCSS = css`
font-size: 60px;
`;
Lastly, pass it down to the child component.
<DatePicker addCSS={myCSS} />
I was able to solve this issue by accessing the DOM nodes with using a data- attribute.
For example,
const myComponent = styled.div`
// ...styles here
`;
const foo = () => (
<myComponent
data-foo-bar="foobar"
/>
);
console.log(document.querySelector('[data-a11y-id="HeaderNavBar-SearchButton"]');
// returns the DOM element for myComponent

Conditional Rendering in Styled Component based on Parents' Props

How would I go about applying conditional styles to a Styled Component based on its parent component's props?
A high-level sample setup of my current code is below:
// File 1: GrandParent.js
import React, { useState } from 'react';
import Parent from './parent';
export default function GrandParent() {
const [ isExpanded, setIsExpanded ] = useState(false);
render (
<>
<Parent isExpanded={isExpanded} />
...other Stuff
</>
);
}
// File 2: Parent.js
import React from 'react';
import styled from 'styled-components';
import mediaQueries from './settings';
export default function Parent(props) {
render (
<div className={props.isExpanded ? `expanded` : ``}>
... other stuff
<Child>I'm a child.</Child>
</>
);
}
const Child = styled.div`
background-color: "red";
.expanded & {
background-color: "blue";
${mediaQueries.desktop`
background-color: "navy";
`}
}
`;
Essentially, what I'd like is for my <Child> styled component to have awareness of it's <Parent>'s props so that I can style it conditionally. Currently, I am just conditionally applying a class name to a div just inside the <Parent>, and then I'm using that class name to apply styling to the <Child>. But doesn't feel like a very Styled Components or React way of doing things.
What I'd like to be able to do is something more like this:
const Child = styled.div`
background-color: ${props => props.isExpanded ? 'blue' : 'red'};
`;
But is there a way for me to do this without having to pass in 'isExpanded' as a prop on the <Child>? In my actual code, there are many more children of <Parent> who will also need to know the status of 'isExpanded' for styling purposes, and I really don't want to have to pass that prop to all of them individually.
I think Context API might be the key here, but I'm already using a context several levels higher up above GrandParent in the React tree for global style settings, and I'm afraid that if I try to nest a 2nd context (perhaps wrapping it around <Parent> to let <Parent> and all of its children know about the status of 'isExpanded'), that then both <Parent> and <Child> might lose access to the outer context... or is that not how context nesting works?
I just know that I still need both <Parent> and <Child> to have access to some theme settings from the outer context. And I don't know if it's overkill to introduce a 2nd context at the <Parent> level just for keeping track of the status of that single 'isExpanded' prop.
The other requirement here is that I need <Child> to be able to handle media queries within its conditional styling as well (eg. only applying certain styles if 'isExpanded' is true and if it's a desktop viewport).
What's the proper way to set this up?
You are correct. Context is the way to go.
Context is designed to be a lightweight cross-component data store and using 2 contexts in this case is definitely not overkill; In fact, this use case is almost exactly what Context was created for. (Anecdotally, at work we use dozens of nested contexts).
export const ExpandedContext = createContext();
export default function GrandParent() {
const [ isExpanded, setIsExpanded ] = useState(false);
render (
<ExpandedContext.Provider value={{ isExpanded }}>
<Parent />
...other Stuff
<ExpandedContext.Provider/>
);
}
Unfortunately, there is no way to access context inside of a styled-component, so you'll have to pass in the value via props instead.
export default function Parent(props) {
const { isExpanded } = useContext(ExpandedContext);
render (
<div>
... other stuff
<Child isExpanded={isExpanded}>I'm a child.</Child>
</div>
);
}
You could make the parent's container div a styled component and refer to Child. Something like:
export default function Parent(props) {
render (
<ParentContainer isExpanded={props.isExpanded}>
... other stuff
<Child>I'm a child.</Child>
</ParentContainer>
);
}
const Child = styled.div`
// ...
`;
const ParentContainer = styled.div`
> ${Child} {
background-color: ${props => props.isExpanded ? 'blue' : 'red'};
}
`;

How i can add style of a existent component in styled components?

I'm trying to use styled components to personalize a header component from semantic-ui-react.
I try:
header.jsx:
import React from 'react';
import { Header } from 'semantic-ui-react';
import TipografiaHeader from './cabecalho.css'
const HeaderPages = () => (
<div>
<TipografiaHeader as='h2'
textAlign='center'
>
Workout Log
</TipografiaHeader>
</div>
)
export default HeaderPages
cabecalho.jss.js:
import styled from "styled-components";
import { Header } from 'semantic-ui-react';
const TipografiaHeader = styled.Header`
background: red;
`;
export { TipografiaHeader };
But in the console i'm receiving:
Uncaught TypeError: _styledComponents2.default.Header is not a
function
The syntax for styled.element can only be used for HTML elements. For eg:
styled.h1``
For styling a custom component, the syntax to be used is:
styled(Header)``
Note that this custom component requires the className prop to be passed into the DOM element underneath for this to work.
Docs

Styled-components: Override component style inside a new component

I am trying to override the style of a component inside another component.
So, I have a component A, with some div's inside(Wrapper, Header).
In a new component, I am trying to override component A. Inside that override I want some new styling to the Header component. I know I can reference a component inside the same component but I can't find any info about referencing inside a new component.
// Component A
import React from "react";
export default ({
className,
title
}) => (
<Wrapper className={className}>
<Header>{title}</Header>
</Wrapper>
)
);
const Header = styled.h2`
padding-left: 0;
`;
// Component B
import React from "react";
export default () => (
<CustomA>
/* content */
</CustomA>
)
);
const CustomA = styled(<A />)`
${Header} {
padding-left: 20px;
}
`;
I expect Header to be changed but I get "Header is not defined".
There are a couple of issues to address.
You can follow along on CodeSandbox.
1. Export Header component from Component A
You need to make Header component available outside Component A so that it can be referenced within Component B.
import React from "react";
import styled from "styled-components";
export const Header = styled.h2`
padding-left: 0;
`;
export default ({ className = "", title }) => (
<div className={className}>
<Header>{title}</Header>
</div>
);
2. Errors in Component B
There are three issues here.
You need to pass the component name, not the instance to styled() function.
Instead of const CustomA = styled(<A />) where <A /> is an instance,
Do const CustomA = styled(A).
You need to import Header component exported from Component A.
Now you can reference is within styled(A) as ${Header}.
import styled from "styled-components";
import A, { Header } from "./CustomA";
const CustomA = styled(A)`
${Header} {
padding-left: 20px;
}
`;
export default () => <CustomA title="Component B Content" />;
The last issue is that, you aren't passing the title (I also did className = "" in Component A to make it optional).
First of all you need to use styled like below:
const CustomA = styled(A)``;
instead of
const CustomA = styled(<A/>)``;
Secondly, try the following code:
const CustomA = styled(A)`
h2{
padding-left: 20px;
}
`;
try
const CustomA = styled(A)`
padding-left: 20px;
`;

Resources