Reputation:
I’m new to react and this original code by Elaine at https://codepen.io/elainehuang/pen/XVNXrJ has helped me create an accordion with expand and collapse all button.
I’d like to modify this solution so that I can fetch not only the title but also the accordion content using a map function. Any help in this regard would be appreciated.
class Accordion extends React.Component {
render() {
const { title, expand, onClick } = this.props;
return (
<div>
<dt className={expand ? 'title is-expanded' : 'title'} onClick={onClick}>
{title}
</dt>
<dd className={expand ? 'content is-expanded' : 'content'} onClick={onClick}>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eu interdum diam. Donec interdum porttitor risus non bibendum. Maecenas sollicitudin eros in quam imperdiet placerat. Cras justo purus, rhoncus nec lobortis ut, iaculis vel ipsum. Donec dignissim arcu nec elit faucibus condimentum. Donec facilisis consectetur enim sit amet varius. Pellentesque justo dui, sodales quis luctus a, iaculis eget mauris.
</p>
</dd>
</div>
);
}
}
class Application extends React.Component {
constructor (props) {
super(props);
this.state = {
block1: true,
block2: false,
block3: false,
};
}
toggle = (index) => () => {
this.setState({ [`block${index}`]: !this.state[`block${index}`] });
}
toggleExpand = (expand = false) => () => {
this.setState({
block1: expand,
block2: expand,
block3: expand,
});
}
render() {
const accordionList = [{ title: 'First Accordion' }, { title: 'Second Accordion' }, { title: 'Third Accordion' }];
return <div className="container">
<h1>React Accordion</h1>
<button type="button" className="btn" onClick={this.toggleExpand(true)}>Expand All</button>
<button type="button" className="btn" onClick={this.toggleExpand()}>Collapse All</button>
<dl className="accordion">
{
accordionList.map((item, index) => (
<Accordion title={item.title} onClick={this.toggle(index + 1)} expand={this.state[`block${index+1}`]} />
))
}
</dl>
</div>;
}
}
React.render(<Application />, document.getElementById('app'));
Upvotes: 3
Views: 13409
Reputation: 1534
Change Accordion
to:
class Accordion extends React.Component {
render() {
const { title, content, expand, onClick } = this.props;
return (
<div>
<dt className={expand ? 'title is-expanded' : 'title'} onClick={onClick}>
{title}
</dt>
<dd className={expand ? 'content is-expanded' : 'content'} onClick={onClick}>
<p>{content}</p>
</dd>
</div>
);
}
}
and Application
to:
class Application extends React.Component {
// ...
render() {
const accordionList = [{ title: 'First Accordion', content: 'Content 1' }, { title: 'Second Accordion', content: 'Content 2' }, { title: 'Third Accordion', content: 'Content 3' }];
// ...
<dl className="accordion">
{
accordionList.map((item, index) => (
<Accordion title={item.title} content={item.content} onClick={this.toggle(index + 1)} expand={this.state[`block${index+1}`]} />
))
}
</dl>
// ...
}
Update 1
I had another doubt. The number of blocks might not always be 3 then how can I dynamically set the block values (block1, block2 etc) to false in this.state and to expand in this.setState?
All of the changes are required in Application
component. To make it work you need:
accordionList
from render()
to the state. I add id
property to each element (and then use it inside map()
). It makes sense to keep accordionList
in the state instead of render()
function.toggle(id)
and toggleExpand(expand)
to use block id
instead of array index.class Application extends React.Component {
constructor (props) {
super(props);
this.state = {
blocks: [
{ id: 1, title: 'First Accordion', content: 'Content 1', expanded: true },
{ id: 2, title: 'Second Accordion', content: 'Content 2', expanded: false },
{ id: 3, title: 'Third Accordion', content: 'Content 3', expanded: false },
{ id: 4, title: 'Fourth Accordion', content: 'Content 4', expanded: false }
]
};
}
toggle(id) {
this.setState((prevState) => {
const index = prevState.blocks.findIndex(item => item.id == id);
prevState.blocks[index].expanded = !prevState.blocks[index].expanded;
return { blocks: prevState.blocks };
});
}
toggleExpand(expand) {
this.setState((prevState) => {
const blocks = prevState.blocks.map(item => {
item.expanded = expand;
return item;
});
return { blocks };
});
}
render() {
return (
<div className="container">
<h1>React Accordion</h1>
<button type="button" className="btn" onClick={() => this.toggleExpand(true)}>Expand All</button>
<button type="button" className="btn" onClick={() => this.toggleExpand(false)}>Collapse All</button>
<dl className="accordion">
{
this.state.blocks.map(item => (
<Accordion key={item.id} title={item.title} content={item.content} expand={item.expanded} onClick={() => this.toggle(item.id)} />
))
}
</dl>
</div>
);
}
}
class Accordion extends React.Component {
render() {
const { title, content, expand, onClick } = this.props;
return (
<div>
<dt className={expand ? 'title is-expanded' : 'title'} onClick={onClick}>
{title}
</dt>
<dd className={expand ? 'content is-expanded' : 'content'} onClick={onClick}>
<p>{content}</p>
</dd>
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
@import url("https://fonts.googleapis.com/css?family=PT+Sans");
html, body, #app {
height: 100%;
}
.container {
min-height: 100%;
padding: 30px;
font-family: 'PT Sans';
text-align: center;
}
.container h1 {
text-align: center;
color: #1569a8;
}
.container .btn {
display: inline-block;
margin-bottom: 20px;
border: 1px solid #1569a8;
background: white;
color: #1569a8;
padding: 5px 10px;
border-radius: 5px;
margin-right: 5px;
font-size: 15px;
cursor: pointer;
outline: none;
}
.container .btn:hover {
background: #1569a8;
color: white;
-webkit-transition: .5s;
transition: .5s;
}
.accordion {
margin: 0 auto;
width: 80%;
}
.accordion .title {
padding: 30px 30px;
cursor: pointer;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
color: white;
position: relative;
font-size: 20px;
background: #1569a8;
margin-bottom: -1px;
border-bottom: 1px solid #0e4671;
text-align: left;
}
.accordion .title::after {
content: "+";
font-size: 18px;
color: white;
-webkit-transition: -webkit-transform .5s ease-in-out;
transition: -webkit-transform .5s ease-in-out;
transition: transform .5s ease-in-out;
transition: transform .5s ease-in-out, -webkit-transform .5s ease-in-out;
position: absolute;
right: 30px;
font-family: monospace;
}
.accordion .title.is-expanded {
-webkit-transition: background .5s;
transition: background .5s;
background: #0e4671;
}
.accordion .title.is-expanded::after {
content: "-";
-webkit-transform: rotate(-360deg);
transform: rotate(-360deg);
}
.accordion .content {
overflow: hidden;
max-height: 0;
-webkit-transition: max-height .5s;
transition: max-height .5s;
margin: 0;
padding: 0 30px;
border: solid 1px #eeeeee;
border-top: 0;
background: #e8f4fc;
}
.accordion .content p {
padding: 30px 0;
margin: 0;
opacity: 0;
-webkit-transition: .5s;
transition: .5s;
}
.accordion .content.is-expanded {
max-height: 500px;
overflow: hidden;
}
.accordion .content.is-expanded p {
opacity: 1;
}
.accordion:after {
width: 100%;
height: 10px;
display: block;
background: #0e4671;
content: '';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Upvotes: 2