Search code examples
reactjsreact-bootstrap

How to design collapsible list with in reactjs component with plus minus toggle along with the title text


I have created a react component for sidebar menu. I do not want to use bootstrap accordian for the purpose. I want to create a menu list with plus minus sign toggle. In the code below, if list items under Technical analysis are open it should show "Technical Analysis -". How can I achieve similar list as pic attached?enter image description here

import React, {Fragment, useState} from "react";
import {Col, Collapse, Container, ListGroup, ListGroupItem, Row} from "react-bootstrap";
import {Link, useRouteMatch} from "react-router-dom";
import ListItem from "@material-ui/core/ListItem";
import List from "@material-ui/core/List";

const SidebarL = () => {

const [open, setOpen] = useState(false);
const [open1, setOpen1] = useState(false);
const [open2, setOpen2] = useState(false);
let {url} = useRouteMatch();


return (
    <Container>
      <Row>
        <Col>
          <div onClick={() => setOpen(!open)}
                 aria-controls="collapse-text"
                 aria-expanded={open}
            >
              <Link style={{textDecoration: "none", color: "black"}}>
                <div>
                  Technical Analysis
                </div>
              </Link>
            </div>
            <Collapse in={open}>
                <div id="collapse-text">
                    <ul className="list-unstyled border-left p-2 ml-2">
                        <li>
                          <Link to={`${url}/thedowtheory`}
                                style={{textDecoration: "none"}}
                          >
                            The Dow Theory
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/charts`}
                                style={{textDecoration: "none"}}>
                            Chart & Chart Patterns
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/trendlines`}
                                style={{textDecoration: "none"}}>
                            Trend & Trend Lines
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}>
                            Support & Resistance
                          </Link>
                        </li>
                    </ul>
                </div>
            </Collapse>
            <hr className="mt-2 mb-2" />
            <div onClick={() => setOpen1(!open1)}
                 aria-controls="collapse-text1"
                 aria-expanded={open1}
            >
                <Link style={{textDecoration: "none", color: "black"}}>
                  <div>Fundamental Analysis </div>
                </Link>
            </div>
            <Collapse in={open1}>
                <div id="collapse-text1" >
                    <ul className="list-unstyled border-left p-2 ml-2"
                    >
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                    </ul>
                </div>
            </Collapse>
            <hr className="mt-2 mb-2" />
            <div onClick={() => setOpen2(!open2)}
                 aria-controls="collapse-text2"
                 aria-expanded={open2}
            >
              <Link style={{textDecoration: "none", color: "black"}}>
                  Elliot Wave Analysis
              </Link>
            </div>
            <Collapse in={open2}>
                <div id="collapse-text2" >
                    <ul className="list-unstyled border-left p-2 ml-2"
                    >
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                    </ul>
                </div>
            </Collapse>
        </Col>
        <Col xs={7} className="border-left">
          Content
        </Col>
        <Col className="border-left">
          SidebarR
        </Col>
      </Row>
    </Container>
)


}

   export default SidebarL

Solution

  • This answer assumes you're asking about the mechanics of such a menu and not the specifics of the styling.

    For the accordion, create a configuration object that represents the menu items. There are many ways you could do this, but here's an example tree structure that would accommodate a submenus of any depth:

    const menu = [
      {
        title: 'Getting Started with Technical Analysis',
        items: [
          {
            title: 'Best ways to learn technical analysis',
            href: `${url}/best-ways-to-learn`,
          },
          {
            title: 'Top 7 Books',
            href: `${url}/top-7-books`,
          },
        ],
      },
      {
        title: 'Essential Technical Analysis Strategies',
        items: [
          {
            title: 'Intro to Strategies',
            href: `${url}/intro-to-strategies`,
          }
        ],
      }
    ];
    

    With this structure in place you can create a recursive menu item component that renders an entry's title and its children:

    const MenuItem = ({ item: { title, items = [], href } }) => (
      <li>
        <div className='menu-item-title'>{title}</div>
        
        { items.length && (
          <ul className='submenu'>
            { items.map( item => <MenuItem key={item.title} item={item} /> ) }
          </ul>
        )}
      </li>
    )
    

    With that in place you can add a bit of component state and css to expand/collapse the submenus:

    // adding expanded/collapsed state to MenuItem component from before
    const MenuItem = ({ item: { title, items = [], href } }) => {
      const [expanded, setExpanded] = React.useState(false);
      const clickHandler = React.useCallback(() => setExpanded(!expanded), [expanded]);
    
      return (
        <li onClick={clickHandler} className={expanded ? 'expanded' : 'collapsed'}>
          <div className='menu-item-title'>{title}</div>
            { items.length && (
              <ul className='submenu'>
                { items.map( item => <MenuItem key={item.title} item={item} /> }
              </ul>
            )}
          </div>
        </li>
      );
    }
    

    You can control the rest with CSS.

    .collapsed .submenu {
      height: 0;
      overflow: hidden;
    }
    

    If you only want one item expanded at a time you could pass an onChange handler from the top that could collapse other items when something is expanded.