Skip to content

Instantly share code, notes, and snippets.

@aamirafridi
Created December 10, 2017 11:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aamirafridi/f263630c1efc5c8351385c718f15e50f to your computer and use it in GitHub Desktop.
Save aamirafridi/f263630c1efc5c8351385c718f15e50f to your computer and use it in GitHub Desktop.
React Accordion Component
import "./index.css";
import React, { Component } from "react";
class Accordion extends Component {
static defaultProps = {
onChange: () => {},
statusIconsComponents: {
opened: "▲",
closed: "▼"
},
allowMultipleExpand: true
};
state = {
activeIndexes: []
};
updateIndexes = (index, callback) => {
const { allowMultipleExpand, onChange } = this.props;
this.setState(prevState => {
let activeIndexes;
let isOpen = false;
if (prevState.activeIndexes.includes(index)) {
activeIndexes = allowMultipleExpand
? prevState.activeIndexes.filter(
currentIndex => currentIndex !== index
)
: [];
} else {
isOpen = true;
activeIndexes = allowMultipleExpand
? prevState.activeIndexes.concat(index)
: [index];
}
callback(isOpen);
onChange(activeIndexes);
return { activeIndexes };
});
};
render() {
const children = React.Children.map(this.props.children, (child, index) => {
const isActive = this.state.activeIndexes.includes(index);
return React.cloneElement(child, {
isActive,
statusIcon: this.props.statusIconsComponents[
isActive ? "opened" : "closed"
],
onSelect: onSelectCallback =>
this.updateIndexes(index, onSelectCallback)
});
});
return <div className="accordion">{children}</div>;
}
}
class AccordionItem extends Component {
static defaultProps = {
onChange: () => {},
isOpen: false
};
componentWillMount() {
const { isOpen, onSelect, onChange } = this.props;
if (isOpen) {
onSelect(onChange);
}
}
render() {
const { isActive, statusIcon, onSelect, onChange } = this.props;
const children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
isActive,
statusIcon,
onSelect: () => {
onSelect(onChange);
}
});
});
return <div className="accordion__item">{children}</div>;
}
}
class AccordionHeader extends Component {
render() {
return (
<div onClick={this.props.onSelect} className="accordion__header">
{this.props.children} - {this.props.statusIcon}
</div>
);
}
}
class AccordionPanel extends Component {
render() {
return this.props.isActive ? (
<div className="accordion__panel">{this.props.children}</div>
) : null;
}
}
class App extends Component {
render() {
return (
<div>
<Accordion
onChange={console.log}
statusIconsComponents={{
opened: "😊",
closed: "😞"
}}
allowMultipleExpand={true}
>
<AccordionItem
isOpen
onChange={isOpen => console.log("isOpen = ", isOpen)}
>
<AccordionHeader>Header 1</AccordionHeader>
<AccordionPanel>Panel 1</AccordionPanel>
</AccordionItem>
<AccordionItem>
<AccordionHeader>Header 2</AccordionHeader>
<AccordionPanel>Panel 2</AccordionPanel>
</AccordionItem>
<AccordionItem>
<AccordionHeader>Header 3</AccordionHeader>
<AccordionPanel>Panel 3</AccordionPanel>
</AccordionItem>
</Accordion>
</div>
);
}
}
export default App;
.accordion {
width: 50%;
margin: auto;
background: grey;
padding: 5px;
}
.accordion__item {
background: white;
margin-bottom: 5px;
}
.accordion__item:last-child {
margin-bottom: 0;
}
.accordion__header {
padding: 10px;
background: rgb(169, 169, 169);
line-height: 20px;
}
.accordion__panel {
padding: 10px;
background: white;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment