How to create collapse expand list in react

I try to create a collapse and expand side menu in React (v 16.5) with the following criteria -

On page load first item (Circulars) will be in expanded view. Any of one item can expand at a time, like, if user clicks on the second item (Specifications), the first item will collapse. I also want some CSS animation during collapse/expand transaction , like smoothly down/up the body section of each item and change the arrow icons. My approach is to add/remove a CSS class on each item (sidebar-nav-menu-item) dynamically, like -

sidebar-nav-menu-item item-active

So, when a item was in expanded view it should be like above class and remove item-active when its in collapse view. By default, the body divs (sidebar-nav-menu-item-body) should be hidden through CSS when the item in a collapse mode.

import React, { Component } from 'react';

className SidebarNavs extends React.Component{
    constructor(props) {
       super(props);
    }

    render() {
        return(
            <div className="sidebar-nav">
                <div className="sidebar-nav-menu">

                    <div className="sidebar-nav-menu-item" data-id="circulars">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Circulars</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>

                    <div className="sidebar-nav-menu-item" data-id="specifications">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Specifications</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>


                    <div className="sidebar-nav-menu-item"  data-id="wo">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Work Orders</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>

                </div>
            </div>
        )
    }
}

export default SidebarNavs;

CSS:

.sidebar-nav-menu-item{
   display:block;
}
.sidebar-nav-menu-item-body{
   display:none;
}
.sidebar-nav-menu-item.item-active .sidebar-nav-menu-item-body{
   display:block;
}

Answers 1

  • You should use a state variable to show your collapsiable item active / in-active.

    I modified your code a bit to fit it into your requirements.

    class App extends Component {
      constructor() {
        super();
        this.state = {
            activeCollapse: 'circulars'
        };
      }
    
      handleExpandCollaps = (name) => {
        if (this.state.activeCollapse === name) {
            this.setState({ activeCollapse: '' })
        } else {
            this.setState({ activeCollapse: name })
        }
      }
    
      moreInfoClick = (e) => {
        e.stopPropagation();
        console.log("clicked");
      }
      render() {
        return (
          <div>
            <div className="sidebar-nav">
              <div className="sidebar-nav-menu">
    
                <div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "circulars" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("circulars")} data-id="circulars" >
                  <div className="sidebar-nav-menu-item-head">
                    <span className="sidebar-nav-menu-item-head-title">Circulars</span>
                    <span className="sidebar-nav-menu-item-head-help">
                      <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                    </span>
                  </div>
                  <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                </div>
    
                <div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "specifications" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("specifications")} data-id="specifications">
                  <div className="sidebar-nav-menu-item-head">
                    <span className="sidebar-nav-menu-item-head-title">Specifications</span>
                    <span className="sidebar-nav-menu-item-head-help">
                      <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                    </span>
                  </div>
                  <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                </div>
    
    
                <div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "wo" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("wo")} data-id="wo">
                  <div className="sidebar-nav-menu-item-head">
                    <span className="sidebar-nav-menu-item-head-title">Work Orders</span>
                    <span className="sidebar-nav-menu-item-head-help">
                      <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                    </span>
                  </div>
                  <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                </div>
    
              </div>
            </div>
          </div>
        );
      }
    }
    

    Note: I have used CSS for font-awesome icons. Hope you have added font-awesome

    Demo


Related Articles