App.js
import React from "react";
import Accordion from "./components/Accordion";
import faqData from "./data";
export default function App() {
return (
<Accordion>
<Accordion.Title>Frequently Asked Questions</Accordion.Title>
<Accordion.Wrapper>
{faqData.map((item) => (
<Accordion.Item key={item.id}>
<Accordion.ItemHeader>{item.header}</Accordion.ItemHeader>
<Accordion.Body>{item.body}</Accordion.Body>
</Accordion.Item>
))}
</Accordion.Wrapper>
</Accordion>
);
}
Accordion.js
import React, { useState, useContext, createContext } from "react";
import {
Container,
Inner,
Item,
Body,
Wrapper,
Title,
Header
} from "./Accordion.styles";
const ToggleContext = createContext();
export default function Accordion({ children, ...restProps }) {
return (
<Container {...restProps}>
<Inner>{children}</Inner>
</Container>
);
}
Accordion.Title = function AccordionTitle({ children, ...restProps }) {
return <Title {...restProps}>{children}</Title>;
};
Accordion.Wrapper = function AccordionFrame({ children, ...restProps }) {
return <Wrapper {...restProps}>{children}</Wrapper>;
};
Accordion.Item = function AccordionItem({ children, ...restProps }) {
const [toggleShow, setToggleShow] = useState(true);
const toggleIsShown = (isShown) => setToggleShow(!isShown);
return (
<ToggleContext.Provider value={{ toggleShow, toggleIsShown }}>
<Item {...restProps}>{children}</Item>
</ToggleContext.Provider>
);
};
Accordion.ItemHeader = function AccordionHeader({ children, ...restProps }) {
const { toggleShow, toggleIsShown } = useContext(ToggleContext);
return (
<Header onClick={() => toggleIsShown(toggleShow)} {...restProps}>
{children}
</Header>
);
};
Accordion.Body = function AccordionBody({ children, ...restProps }) {
const { toggleShow } = useContext(ToggleContext);
return (
<Body className={toggleShow ? "open" : ""} {...restProps}>
<span>{children}</span>
</Body>
);
};
Accordion.style.js
import styled from "styled-components";
export const Container = styled.div`
display: flex;
background: #6867ac;
border-bottom: 8px solid #ffbcd1;
font-family: "Inter", sans-serif;
`;
export const Wrapper = styled.div`
margin-bottom: 40px;
`;
export const Inner = styled.div`
display: flex;
padding: 70px 45px;
flex-direction: column;
max-width: 815px;
margin: auto;
`;
export const Title = styled.h1`
font-size: 33px;
line-height: 1.1;
margin-top: 0;
margin-bottom: 8px;
color: white;
text-align: center;
`;
export const Item = styled.div`
color: white;
margin: auto;
margin-bottom: 10px;
max-width: 728px;
width: 100%;
&:first-of-type {
margin-top: 3em;
}
&:last-of-type {
margin-bottom: 0;
}
`;
export const Header = styled.div`
display: flex;
flex-direction: space-between;
cursor: pointer;
border: 1px solid #ce7bb0;
border-radius: 8px;
box-shadow: #ce7bb0;
margin-bottom: 1px;
font-size: 22px;
font-weight: normal;
background: #ce7bb0;
padding: 0.8em 1.2em 0.8em 1.2em;
user-select: none;
align-items: center;
`;
export const Body = styled.div`
font-size: 18px;
font-weight: normal;
line-height: normal;
background: #ce7bb0;
margin: 0.5rem;
border-radius: 8px;
box-shadow: #ce7bb0;
white-space: pre-wrap;
user-select: none;
overflow: hidden;
&.open {
max-height: 0;
overflow: hidden;
}
span {
display: block;
padding: 0.8em 2.2em 0.8em 1.2em;
}
`;
Live mode https://codesandbox.io/embed/xenodochial-lovelace-i9fcw?fontsize=14&hidenavigation=1&theme=dark