Component content not imported when importing styled components - reactjs

I am building a site using React and have decided to use Styled Components. I have a component that is styled with Styled Components and is receiving a prop from my homepage where I am using an instance of this component. I have the styling being pulled into the homepage, but it is not pulling in the content of the component. When if I just pull in the whole component it pulls in the content fine, but it doesn't allow me to pass the prop, but if I import the component styling it doesn't pull in the content of the component, but the prop passes properly. So I need some help with how to pull in the content and styling and get the prop to pass.
I am pretty new to Gatsby and Styled Components so thanks for any help.
My Component Code
import React from 'react'
import styled from "styled-components"
import { useStaticQuery, graphql } from 'gatsby'
export const WhyChooseSection = styled.section`
border-bottom: 1px solid var(--charcoal);
`
export const WhyChooseH2 = styled.h2`
display: ${(props) => props.displayH2};
text-align: center;
`
export const WhyChooseContent = styled.div`
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
width: 100%;
max-width: 1160px;
margin: 0 auto;
padding-top: 40px;
padding-bottom: 40px;
column-gap: 100px;
row-gap: 30px;
#media(min-width: 992px) {
justify-content: space-between;
column-gap: 0;
}
.why-icon {
width: 180px;
height: 145px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
p {
margin-bottom: 0;
font-size: 19px;
font-weight: var(--font-bold);
}
}
`
export function WhyChooseIcons() {
const data = useStaticQuery(graphql`
{
whyChooseIcons: allWp {
nodes {
acfOptionsOfficeSettings {
whyChooseInteriorDetails {
whyChooseIcons {
whyChooseIconGraphic {
altText
id
sourceUrl
}
whyChooseIconTitle
}
}
}
}
}
}
`)
return (
<>
{data.whyChooseIcons.nodes.map((node, index) => (
<WhyChooseSection key={index}>
<WhyChooseH2>Why Choose Interior Details?</WhyChooseH2>
<WhyChooseContent>
{node.acfOptionsOfficeSettings.whyChooseInteriorDetails.whyChooseIcons && node.acfOptionsOfficeSettings.whyChooseInteriorDetails.whyChooseIcons.map((item) => (
<div className='why-icon' key={item.whyChooseIconGraphic.id}>
<img src={item.whyChooseIconGraphic.sourceUrl} alt={item.whyChooseIconGraphic.altText} />
<p>{item.whyChooseIconTitle}</p>
</div>
))}
</WhyChooseContent>
</WhyChooseSection>
))}
</>
)
}
export default WhyChooseIcons
My Homepage Code
import * as React from "react"
// import styled from "styled-components"
import { graphql } from 'gatsby'
import Seo from "../components/seo"
import Hero from '../components/heroComponent'
import { WhyChooseSection } from '../components/whyChooseIcons'
import { WhyChooseH2 } from '../components/whyChooseIcons'
import { WhyChooseContent } from '../components/whyChooseIcons'
import HomeProducts from '../components/homeProducts'
import HomeTestimonials from '../components/testimonials'
import HomeNewHomes from '../components/homeNewHomes'
import HomeFamilyTreatment from '../components/homeFamilyTreatment'
import HomeSolutions from '../components/homeSolutions'
import HomeMotorization from '../components/motorizationComps'
import HomeBrands from '../components/homeBrands'
import CTAOne from '../components/ctaOne'
const IndexPage = (hero) => {
const heroData = hero.data.homeHero
return (
<>
{heroData.edges.map(({ node }, index) => (
<Hero key={index} heroTitle={node.template.homePage.heroH1} heroSubTitle={node.template.homePage.heroParagraph} heroBg={node.template.homePage.heroBackground.gatsbyImage} heroBtnOneText={node.template.homePage.heroButton1Text} heroBtnOneURL={node.template.homePage.heroButton1Url} heroBtnTwoText={node.template.homePage.heroButton2Text} heroBtnTwoURL={node.template.homePage.heroButton2Url} />
))}
<WhyChooseSection>
<WhyChooseH2 displayH2="none"></WhyChooseH2>
<WhyChooseContent></WhyChooseContent>
</WhyChooseSection>
<HomeProducts />
<HomeTestimonials />
<HomeNewHomes />
<HomeFamilyTreatment />
<HomeSolutions />
<HomeMotorization />
<HomeBrands />
<CTAOne />
</>
)
}
export default IndexPage
export const Head = () => <Seo title="Home" />
export const HeroContent = graphql`
{
homeHero: allWpPage(filter: {template: {templateName: {eq: "Home Template"}}}) {
edges {
node {
template {
... on WpHomeTemplate {
homePage {
heroH1
heroParagraph
heroButton1Text
heroButton1Url
heroButton2Text
heroButton2Url
heroBackground {
gatsbyImage(placeholder: BLURRED, width: 1920)
altText
}
}
}
}
}
}
}
}
`

Since you only want to pass a prop into your <WhyChooseIcons> component and that the component itself renders its own content, there is no need to provide any children node to it in the consuming parent.
Simply update your <WhyChooseIcons> component to accept a prop that it will pass to its inner child. Let's name it displayH2 and we can simply pass it into the <WhyChooseH2> component by doing this: <WhyChooseH2 displayH2={displayH2}>. So your component definition now looks like this:
export function WhyChooseIcons({ displayH2 }) {
// Rest of the component logic here
return (
<>
{data.whyChooseIcons.nodes.map((node, index) => (
<WhyChooseSection key={index}>
<WhyChooseH2 displayH2={displayH2}>Why Choose Interior Details?</WhyChooseH2>
{/* Other content here */}
</WhyChooseSection>
))}
</>
)l
};
Then in the file where you consume this component, it is just a matter of passing "none" to the prop:
<WhyChooseSection displayH2="none">
HOWEVER this feel like a XY problem to me, because all you want is to control the conditional rendering of this component. What you can do is to create a prop on your component that determines whether <WhyChosseH2> should be rendered or not.
export const WhyChooseH2 = styled.h2`
text-align: center;
`
export function WhyChooseIcons({ shouldHideH2 }) {
return (
<>
{data.whyChooseIcons.nodes.map((node, index) => (
<WhyChooseSection key={index}>
{!shouldHideH2 && <WhyChooseH2>Why Choose Interior Details?</WhyChooseH2>}
{/* Other content here */}
</WhyChooseSection>
))}
</>
)
}
Then you can just control H2 rendering with a true/false boolean:
{/* To show it, just don't set the prop */}
<WhyChooseIcons />
{/* To hide it, both ways are valid */}
<WhyChooseIcons shouldHideH2 />
<WhyChooseIcons shouldHideH2={true} />

Related

How to add style to a component with styled component react?

I'm trying to add style to a component.
It works fine with a component imported from the material-ui library, but it doesn't work when it comes to my original component(that is not used in any library).
Here is the code for style with styled-component
import styled from "styled-components";
import List from "./List";
import AcUnitIcon from "#mui/icons-material/AcUnit";
export const ListCotainer = styled(List)` // doesn't apply style
margin: 100px;
color: pink;
`;
export const Container = styled.div` // applies style
font-family: sans-serif;
text-align: center;
background-color: red;
`;
export const Icon = styled(AcUnitIcon)` // applies style
background-color: blue;
margin: 100px;
padding: 20px;
${Container}:hover & {
background-color: green;
}
`;
App.js
import { ListCotainer, Container, Icon } from "./styles";
export default function App() {
return (
<Container>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ListCotainer />
<Icon />
</Container>
);
}
List.js (regular component with no library)
import React from "react";
const List = () => {
return (
<div>
<h1>Hello styled component</h1>
</div>
);
};
export default List;
Is there any way I can add style to the List component?
you need to create styled CSS same as you have created for Container in your App.js. i have update your code it should work for you.
styles.js
import styled from "styled-components";
import AcUnitIcon from "#mui/icons-material/AcUnit";
export const ListCotainer = styled.div`
margin: 100px;
width: 200px;
color: pink;
`;
export const Container = styled.div`
font-family: sans-serif;
text-align: center;
background-color: red;
`;
export const Icon = styled(AcUnitIcon)`
background-color: blue;
margin: 100px;
padding: 20px;
${Container}:hover & {
background-color: green;
}
`;
List.js
import React from "react";
import { ListCotainer } from "./styles";
const List = () => {
return (
<ListCotainer>
<h1>Hello styled component</h1>
</ListCotainer>
);
};
export default List;
App.js
import { Container, Icon } from "./styles";
import List from "./List";
export default function App() {
return (
<Container>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<List />
<Icon />
</Container>
);
You may not be able to style like this component and what you are applying to the style material component is a svg/icon that you can apply to icons and img with style components.
style.js
import styled from "styled-components";
import AcUnitIcon from "#mui/icons-material/AcUnit";
export const ListCotainer = styled.div`
margin: 100px;
width: 200px;
color: pink;
`;
export const Container = styled.div`
font-family: sans-serif;
text-align: center;
background-color: red;
`;
export const Icon = styled(AcUnitIcon)`
background-color: blue;
margin: 100px;
padding: 20px;
${Container}:hover & {
background-color: green;
}
`;
App.js
import { Container, Icon } from "./styles";
import List from "./List";
export default function App() {
return (
<Container>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<List />
<Icon />
</Container>
);
}
List.js
import React from "react";
import { ListCotainer } from "./styles";
const List = () => {
return (
<ListCotainer>
<h1>Hello styled component</h1>
</ListCotainer>
);
};
export default List;

Unable to view custom sidebar component created with react router in storybook

I am quite new to React and have been trying to create a custom sidebar component. Based on my understanding by going through different tutorials, this is what I have so far:
react-router-dom version: ^6.3.0
storybook: ^6.1.17
These are my components
sidebar.jsx
const SidebarNav = styled.nav`
background: #15171c;
width: 250px;
height: 100vh;
display: flex;
justify-content: center;
position: fixed;
top: 0;
right: ${({ sidebar }) => (sidebar ? "0" : "-100%")};
transition: 350ms;
z-index: 10;
`;
const SidebarWrap = styled.div`
width: 100%;
`;
const Sidebar = () => {
const [sidebar, setSidebar] = useState(true);
return (
<>
<IconContext.Provider value={{ color: "#fff" }}>
<SidebarNav sidebar={sidebar}>
<SidebarWrap>
{sideBarData.map((item, index) => {
return <SubMenu item={item} key={index} />;
})}
</SidebarWrap>
</SidebarNav>
</IconContext.Provider>
</>
);
};
export default Sidebar;
sidebardata.jsx
export const sideBarData = [ {
title: "Question 4",
path: "/test"
},
// {
// title: "Question 5",
// path: "/testing"
// }
];
sidebarsubmenu.jsx
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const SidebarLink = styled(Link)`
display: flex;
color: #e1e9fc;
justify-content: space-between;
align-items: center;
padding: 20px;
list-style: none;
height: 60px;
text-decoration: none;
font-size: 18px;
&:hover {
background: #252831;
border-left: 4px solid green;
cursor: pointer;
}
`;
const SidebarLabel = styled.span`
margin-left: 16px;
`;
const DropdownLink = styled(Link)`
background: #252831;
height: 60px;
padding-left: 3rem;
display: flex;
align-items: center;
text-decoration: none;
color: #f5f5f5;
font-size: 18px;
&:hover {
background: green;
cursor: pointer;
}
`;
const SubMenu = ({ item }) => {
const [subnav, setSubnav] = useState(false);
const showSubnav = () => setSubnav(!subnav);
return (
<>
<SidebarLink to={item.path}
onClick={item.subNav && showSubnav}>
<div>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</div>
<div>
{item.subNav && subnav
? item.iconOpened
: item.subNav
? item.iconClosed
: null}
</div>
</SidebarLink>
{subnav &&
item.subNav.map((item, index) => {
return (
<DropdownLink to={item.path} key={index}>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</DropdownLink>
);
})}
</>
);
};
export default SubMenu;
BookSideBarLayout.jsx
import React from 'react';
import Sidebar from './BookSideBar';
function Layout(props) {
return (
<div>
<div style={{display: "flex"}}>
<Sidebar/>
</div>
</div>
);
}
export default Layout;
BookSidebarRoutes.jsx
import React from "react";
import {BrowserRouter, Route,Routes} from "react-router-dom";
import Support from "./Support";
import Layout from "./BookSideBarLayout";
function MyRoutes() {
return (
<BrowserRouter>
<Layout/>
<Routes>
{/* <Route path="/" element={<Sidebar/>}/> */}
<Route path="/test" element={<Support/>}/>
</Routes>
</BrowserRouter>
);
}
export default MyRoutes;
The story that I created was as below
import { storiesOf } from '#storybook/react';
import StoryRouter from 'storybook-react-router';
import MyRoutes from '../../src/components/BookSidebarRoutes';
// export default {
// title: 'Side Bar',
// //decorators : [(Story) => (<MemoryRouter><Story/></MemoryRouter>)]
// };
storiesOf('Params', module)
.addDecorator(StoryRouter())
.add('params', () => (
<MyRoutes/>
));
export const Default = () => <MyRoutes />;
After I run my story above, I keep getting the error as below:
A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.**
I also tried Switch in the MyRoutes component but that too didn't work. Any guidance in this will be really helpful. Thank you in advance

Strange stuff with useState, useEffect and fetch

I'm trying to fetch some data inside useEffect and when the data is received I want to set a certain state with useState. Data correctly returns from the server. However this doesn't work. Here's the code:
const [sharingLink, setSharingLink] = React.useState(null);
React.useEffect(() => {
client.query({
query: queryGetReferalData
}).then(result => {
console.warn(result); // correct response
setSharingLink(result.data.referralsystem.shareUrl);
console.warn(sharingLink); // null
});
}, []);
Here's the whole component:
import React from 'react';
import styled from 'styled-components';
import { i18n } from 'Helpers';
import { Button } from './Button';
import { ButtonLink } from './ButtonLink';
import { Heading } from './Heading';
import { Input } from './Input';
import Copy from './icons/Copy';
import Facebook from './icons/Facebook';
import Twitter from './icons/Twitter';
import WhatsApp from './icons/WhatsApp';
import client from '#client/apollo';
import queryGetReferalData from './schemas/queryGetReferalData.graphql';
const Root = styled.div`
padding: 48px;
padding-top: 32px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0px 10px 30px rgba(27, 50, 85, 0.1);
border-radius: 4px;
background-color: #FFFFFF;
`;
const Pane = styled.div`
`;
const Row = styled.div`
display: flex;
& > * + * {
margin-left: 10px;
}
`;
export const Form = () => {
const [sharingLink, setSharingLink] = React.useState(null);
const facebookSharingLink =
sharingLink && `https://www.facebook.com/sharer/sharer.php?${encodeURIComponent(sharingLink)}`;
const twitterSharingLink =
sharingLink && `http://www.twitter.com/share?url=${encodeURIComponent(sharingLink)}`;
const whatsAppSharingLink =
sharingLink && `whatsapp://send?text=${encodeURIComponent(sharingLink)}`;
React.useEffect(() => {
client.query({
query: queryGetReferalData
}).then(result => {
console.warn(result);
setSharingLink(result.data.referralsystem.shareUrl);
console.warn(sharingLink);
});
}, []);
return (
<Root>
<Pane>
<Heading>
{ i18n._('Your invitational link') }
</Heading>
<Row>
<Input disabled={sharingLink === null} value={sharingLink} />
<Button icon={<Copy />}>
{ i18n._('COPY') }
</Button>
</Row>
</Pane>
<Pane>
<Heading>
{ i18n._('Or share via social') }
</Heading>
<Row>
<ButtonLink
backgroundColor='#5A79B5'
icon={<Facebook />}
href={facebookSharingLink}
>
{ i18n._('Facebook') }
</ButtonLink>
<ButtonLink
backgroundColor='#52A6DB'
icon={<Twitter />}
href={twitterSharingLink}
>
{ i18n._('Twitter') }
</ButtonLink>
<ButtonLink
backgroundColor='#0DC455'
icon={<WhatsApp />}
href={whatsAppSharingLink}
>
{ i18n._('WhatsApp') }
</ButtonLink>
</Row>
</Pane>
</Root>
);
};
The component also renders like sharingLink is null.
Why is this happening?
What do I do to make this work?
I had some code that was adding to the DOM in the parent component. When I remove it, everything works.
Adding to the DOM in the component, doesn't matter if it's into useEffect or not, somehow messes up hooks even though you add HTML to something completely unrelated to react I guess.
I had this structure:
<body>
<div id="page">
<div id="root">
<CustomReactElement />
</div>
</div>
<body>
The code inside CustomReactElement was adding markup to the 'page'. I changed the code from setting an innerHTML to appendChild() and everything worked.

How to EXTEND imported component styled components

I cannot extend my imported component. I was looking into styled components docs and find that in v4+ should works prop "as", but it doesnt.
COMPONENT:
type Props = {
padding: string,
justify: string
}
const FlexContainer = styled.div<Props>`
padding: ${props => props.padding};
display: flex;
flex-wrap: wrap;
justify-content: ${props => props.justify};
`
export const Flexcontainer: React.FC<Props> = props =>{
return (
<FlexContainer padding={props.padding} justify={props.justify}>
{props.children}
</FlexContainer>
)
}
EXTENDED STYLE:
import { Flexcontainer } from '../../reusable/FlexContainer';
const FlexContainerExtended = styled.div`
color: red;
`
USE:
<FlexContainerExtended
padding={null}
justify={"flex-start"}
as={Flexcontainer}>
You just have to add a prop className to your Flexcontainer component like this:
export const Flexcontainer: React.FC<Props> = props =>{
return (
<FlexContainer className={props.className} padding={props.padding} justify={props.justify} >
{props.children}
</FlexContainer>
)}
To override styles, styled-components passes a className as props to the overrided component, it's why
You can just pass the base component to the styled function to override it.
type Props = {
padding: string,
justify: string
}
const FlexContainer = styled.div<Props>`
padding: ${props => props.padding};
display: flex;
flex-wrap: wrap;
justify-content: ${props => props.justify};
`
const FlexContainerExtended = styled(FlexContainer)`
color: red;
`
export const Flexcontainer: React.FC<Props> = props =>{
return (
<FlexContainer padding={props.padding} justify={props.justify}>
{props.children}
</FlexContainer>
)
}
// And use it like this
<FlexContainerExtended
padding={null}
justify={"flex-start"}/>
I know this question was asked a long time ago, but I leave here the solution I found for future visitors.
Base component definition
import React from 'react'
import styled from 'styled-components'
const ContainerWrapper = styled.div`
width: 100%;
max-width: 1200px;
padding-left: 5%;
padding-right: 5%;
margin-left: auto;
margin-right: auto;
`
export default function Container({ children, ...props }) {
return (
<ContainerWrapper {...props}>
{children}
</ContainerWrapper>
)
}
Extended component definition
Obs.: note that the extended component is an article, while the base component is a div, and so far they have no relationship.
Also note that the base component (Container) has been imported but not yet used.
import React from 'react'
import styled from 'styled-components'
import Container from '../../common/Container'
const HeroWrapper = styled.article`
height: 100vh;
padding-top: 74px;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
background-attachment: fixed;
`
Invoking componentes
Now, in the same file where I declared the extended component, I just call the base component, informing the name of the extended component in the as attribute.
export default function PostsWrapper() {
return (
<Container as={HeroWrapper}>
{/* ... */}
</Container>
)
}

hide imported child component on hover using styled-components

Like the title says, this works with styled components sitting within the same js file (provided they are procedural-ordered above). But with imported child components I can't get it to work.
import React from "react";
import styled from "styled-components";
// Components
import Bars from "../path/Bars.js";
import BarsHover from "../path/BarsHover.js";
// Variables
import { colors } from "../../path/index.js";
//============================================ styles =============================================
const DivBars = styled(Bars)``;
const DivBarsHover = styled(BarsHover)``;
const DivWrapper = styled.div`
display: flex;
width: 20rem;
margin-bottom: 3rem;
&:hover {
cursor: pointer;
}
&:hover ${DivBars} {
display: none;
}
&:hover ${DivBarsHover} {
display: block;
}
`;
//=========================================== component ===========================================
const ParentComponent = props => {
return (
<DivContainer>
<DivBars fullBarWidth={"100%"} fractionBarWidth={"70%"} barColor={colors.niagara} />
<DivBarsHover fullBarWidth={"100%"} fractionBarWidth={"70%"} barColor={colors.gallery2} />
</DivContainer>
);
};
export default ParentComponent;
I think this caveat is the cause:
...wrapping A in a styled() factory makes it eligible for
interpolation -- just make sure the wrapped component passes along
className.
class A extends React.Component {
render() {
return <div className={this.props.className} />
}
}
const StyledA = styled(A)``
const B = styled.div`
${StyledA} {
}
`
NOTE: Ensure the className prop is propagated all the way to the component being referenced in the case that it isn't a direct descendent.

Resources