React Accordion UI Component
I wanted to flex my react muscles by building a little component. It all began with this egghead tutorial.
Great way to get started and practice some ES6, however at the end of this brief guide I was left with only one list item, so I had some fun digging around to figure out how to render a full list of question and answers.
I also wanted to do a fun folding animation for the accordion, I drew from this really sweet widget! I'm not totally satisfied with the animation result, if anyone has thoughts on how to finesses the fold I'm all ears! :)
Made with
Html
Css/SCSS
Javascript
Html
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<pattern id="pattern"
x="0" y="0" width="24" height="24"
patternUnits="userSpaceOnUse" >
<rect fill="rgba(159, 188, 191, 0.15)" x="0" width="20" height="20" y="0"/>
<rect fill="rgba(159, 188, 191, 0.15)" x="20" width="20" height="20" y="20"/>
</pattern>
<rect fill="url(#pattern)" x="0" y="0" width="100%" height="100%"/>
</svg>
<div id="accordion"></div>
Css
@import url(https://fonts.googleapis.com/css?family=Josefin+Slab:400,700);
@import url(https://fonts.googleapis.com/css?family=Maven+Pro);
$bodyBg: #3a3042;
$underLine: #ff784f;
body {
background: $bodyBg;
font-family: 'Maven Pro', sans-serif;
margin: 0;
}
svg {
position: absolute;
width: 100%;
height: 100%;
z-index: -1;
top: 0;
}
h1 {
color: #fff;
text-align: center;
text-transform: uppercase;
font-family: 'Josefin Slab', serif;
font-size: 66px;
margin-top: 10vh;
letter-spacing: 10px;
}
.accordion-container {
width: 60%;
min-width: 500px;
margin: 5vh auto;
border-radius: 5px;
background-color: #fff;
box-shadow:-2px 1px 2px 2px #212121;
div {
border-bottom: 3px solid;
border-color: $underLine;
transition: border-color 0.5s ease-in;
}
}
.answer, .summary {
display: inline-block;
}
.answer {
background: $bodyBg;
color: #fff;
}
.summary {
font-family: 'Josefin Slab', serif;
text-transform: uppercase;
cursor: pointer;
line-height: 25px;
padding: 15px;
&:before {
content: '>\0000a0';
}
}
.folding-pannel {
display: block;
transition: all 0.2s ease-in;
line-height: 40px;
border-top: 2px solid $bodyBg;
}
.active .summary {
border-color: transparent;
transition: border-color 0.2s ease-out;
}
.inactive .folding-pannel {
transform-origin: 50% 0%;
transform: perspective(250px) rotateX(-90deg);
transition: all 0.4s ease-in-out;
height: 0;
}
.active .folding-pannel {
transform: perspective(350px) rotateX(0deg);
transition: all 0.4s ease-in-out;
height: 50px;
line-height: 50px;
text-indent: 40px;
}
Javascript
class AccordionItem extends React.Component {
constructor() {
super();
this.state = {
active: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
active: !this.state.active,
className: "active"
});
}
render() {
const activeClass = this.state.active ? "active" : "inactive";
const question = this.props.details;
return (
<div className={activeClass} onClick={this.toggle}>
<span className="summary">{question.summary}</span>
<span className="folding-pannel answer">{question.answer}</span>
</div>
);
}
}
class Accordion extends React.Component {
constructor() {
super();
this.state = {
questions: sampleQuestions,
};
this.renderQuestion = this.renderQuestion.bind(this);
}
renderQuestion(key) {
return <AccordionItem key={key} index={key} details={this.state.questions[key]} />
}
render() {
return(
<div>
<h1>What is...</h1>
<div className="accordion-container">{Object.keys(this.state.questions).map(this.renderQuestion)} </div>
</div>
)
}
}
const sampleQuestions = {
question1: {summary:'the capital of Canada?', answer:'Ottawa baby!!'},
question2: {summary:'the life span of a bowhead whale?', answer:'Over 200 years!!'},
question3: {summary:'the most visited city in the world?', answer:'London, groovy baby!!'},
question4: {summary:'the warmest ocean?', answer:'Indian Ocean, it\'s a hottie!'},
question5: {summary:'the one thing ron swanson hates more than lying?', answer:'Skim milk, which is water that\'s lying about being milk'}
};
ReactDOM.render(
<Accordion />,
document.getElementById('accordion')
);
Author
Tobi Weinstock
Demo
See the Pen React Accordion UI Component by Tobi Weinstock (@tvweinstock) on CodePen.