Story
I'm using this react.js template http://skote-v-light.react.themesbrand.com/email-read from https://themeforest.net/item/skote-react-admin-dashboard-template/26318700 for building my react app.
I use and modify the sidebar component from the template and name it Sidebar.js.
There is SidebarContent.js for building the content inside the sidebar.
There is MenuComponent.js inside SidebarContent.js for building the menu inside the sidebar.
MenuComponent.js takes a list from menuList.js and renders it.
There are two dummy pages which are Route/Summary page (Summary.js) and Payload page (Payload.js) inside the Sidebar. I combine them all inside PageWithSidebar.js.
Question
Why don't always the ABC, DEF, etc folders in the sidebar open when I click them?
Ideal Condition I Wanted
Every folder (ABC, DEF, etc) inside the sidebar always opens when I click one of them.
Actual Condition
Please watch this demo to understand the context: https://drive.google.com/file/d/1Lig1SKKRtyRqxWm_OYNkuQennrytV0jo/view?usp=sharing
Steps:
- Click the
Route/Summaryoption inside theABCfolder ->Summarypage is shown (as I wanted) - Click the
Payloadoption inside theABCfolder ->Payloadpage is shown (as I wanted) - Click another
Route/Summaryoption inside theABCfolder ->Summarypage is shown (as I wanted) - Close the
ABCfolder -> The folder closes (as I wanted) - Click the
DEFfolder -> The folder doesn't open (not I wanted)
There is no error or warning log in the browser console.
Code
PageWithSidebar.js:
import React from 'react'
import { Route, Switch } from "react-router-dom";
// COMPONENTS
import Sidebar from '../../components/Menu/Sidebar/Sidebar'
// PAGES
// ROUTE
import Summary from '../Routes/Summary/Summary'
// PAYLOAD
import Payload from '../Payload/Payload'
const PageWithSidebar = () => {
return(
<div id="layout-wrapper">
<Sidebar/>
<div className="main-content">
<Switch>
{/* ROUTE */}
<Route path="/route/summary/location=:locationName">
<Summary/>
</Route>
<Route path="/payload/location=:locationName">
<Payload/>
</Route>
</Switch>
</div>
</div>
)
}
export default PageWithSidebar
Sidebar.js:
import React from "react"
import { Link } from "react-router-dom"
// COMPONENTS
import SidebarContent from './SidebarContent'
// IMAGES
import IconClose from '../../../images/icons/close.svg'
import IconOpen from '../../../images/icons/open.svg'
const Sidebar = () => {
const toogleSidebar = () => {
var body = document.body;
body.classList.toggle("vertical-collpsed");
body.classList.toggle("sidebar-enable");
}
return (
<React.Fragment>
<div className="vertical-menu">
<div className="navbar-brand-box">
<Link to='/' className="logo logo-dark">
{/* SIDEBAR IS CLOSED */}
<span className="logo-sm">
{/* TOOGLE SIDEBAR BUTTON */}
<img src={IconOpen} alt='' className='icon-open' onClick={toogleSidebar}/>
</span>
{/* SIDEBAR IS OPEN */}
<span className="logo-lg sidebar-is-open-container">
{/* TOOGLE SIDEBAR BUTTON */}
<img src={IconClose} alt='' className='icon-close' onClick={toogleSidebar}/>
</span>
</Link>
</div>
{/* SIDEBAR CONTENT */}
<SidebarContent />
<div className="sidebar-background"></div>
</div>
</React.Fragment>
)
}
export default Sidebar
SidebarContent.js:
import PropTypes from "prop-types"
import React, { useEffect, useRef, useState } from "react"
import { withRouter, Link, useLocation } from "react-router-dom"
// Import Scrollbar
import SimpleBar from "simplebar-react"
// MetisMenu
import MetisMenu from "metismenujs"
//i18n
import { withTranslation } from "react-i18next"
// COMPONENTS
import MenuComponent from './MenuComponent'
// IMAGES
import IconFolderGray from '../../../images/icons/folder-gray.svg'
import IconFolderRed from '../../../images/icons/folder-red.svg'
import IconLogout from '../../../images/icons/logout.svg'
// MENU ITEMS
import menuList from './menuList/menuList'
// STYLES
import './styles.scss'
import logoutUser from '../../../utils/authentication/logOut'
const SidebarContent = props => {
// MY OWN CODE SECTION
const location = useLocation()
const pathName = location['pathname']
let locationName = ''
if(pathName.includes('route')) {
locationName = pathName.split('/')[3].replace('location=', '')
}
else {
locationName = pathName.split('/')[2].replace('location=', '')
}
// console.log('SidebarContent, locationName:', locationName)
// TEMPLATE CODE SECTION
const ref = useRef()
useEffect(() => {
const pathName = props.location.pathname
const initMenu = () => {
new MetisMenu("#side-menu")
let matchingMenuItem = null
const ul = document.getElementById("side-menu")
const items = ul.getElementsByTagName("a")
for (let i = 0; i < items.length; ++i) {
if (pathName === items[i].pathname) {
matchingMenuItem = items[i]
break
}
}
if (matchingMenuItem) {
activateParentDropdown(matchingMenuItem)
}
}
initMenu()
}, [props.location.pathname])
useEffect(() => {
ref.current.recalculate()
})
function scrollElement(item) {
if (item) {
const currentPosition = item.offsetTop
if (currentPosition > window.innerHeight) {
ref.current.getScrollElement().scrollTop = currentPosition - 300
}
}
}
function activateParentDropdown(item) {
item.classList.add("active")
const parent = item.parentElement
const parent2El = parent.childNodes[1]
if (parent2El && parent2El.id !== "side-menu") {
parent2El.classList.add("mm-show")
}
if (parent) {
parent.classList.add("mm-active")
const parent2 = parent.parentElement
if (parent2) {
parent2.classList.add("mm-show") // ul tag
const parent3 = parent2.parentElement // li tag
if (parent3) {
parent3.classList.add("mm-active") // li
parent3.childNodes[0].classList.add("mm-active") //a
const parent4 = parent3.parentElement // ul
if (parent4) {
parent4.classList.add("mm-show") // ul
const parent5 = parent4.parentElement
if (parent5) {
parent5.classList.add("mm-show") // li
parent5.childNodes[0].classList.add("mm-active") // a tag
}
}
}
}
scrollElement(item);
return false
}
scrollElement(item);
return false
}
// MY OWN CODE SECTION
const itemList = ['ABC', 'DEF', 'GHI', 'JKL', 'MNO', 'PQR']
const [ selectedItem, setSelectedItem ] = useState(itemList.indexOf(locationName.toUpperCase()))
return (
<React.Fragment>
<SimpleBar style={{ maxHeight: "100%" }} ref={ref}>
<div id="sidebar-menu">
<ul className="metismenu list-unstyled sidebar-content-menu-container" id="side-menu">
{/* TITLE */}
<li className="menu-title sidebar-content-site-profile-title">Dummy Locations</li>
{/* ITEMS */}
{
itemList.map((item, index) => (
<li
key={index}
className={selectedItem === index ? 'sidebar-content-dropdown-selected' : 'sidebar-content-dropdown'}
// onClick={() => setSelectedItem(index)}
>
{/* TITLE */}
<Link to="/" className="has-arrow dropdown-link">
{/* FOLDER ICON */}
<img
src={selectedItem === index ? IconFolderRed : IconFolderGray}
alt=''
className='bx sidebar-content-icon-folder-gray'
/>
{/* TEXT */}
<span className='dropdown-span'>
{item}
</span>
</Link>
{/* MENU COMPONENT*/}
<ul className="sub-menu" aria-expanded="false">
<div className='border-line'></div>
<MenuComponent list={menuList} pathName={item.toLowerCase()}/>
</ul>
</li>
))
}
{/* PROFILE */}
<li className='sidebar-content-single-item-container'>
<Link to='/'>
<div onClick={logoutUser}>
<img src={IconLogout} alt='' className='sidebar-content-icon-single-item'/>
<span className='sidebar-content-span-single-item'>Logout</span>
</div>
</Link>
</li>
</ul>
</div>
</SimpleBar>
</React.Fragment>
)
}
SidebarContent.propTypes = {
location: PropTypes.object,
t: PropTypes.any,
}
export default withRouter(withTranslation()(SidebarContent))
MenuComponent.js:
import React, { useState } from 'react'
import { Link } from "react-router-dom"
const MenuComponent = (props) => {
const [ selectedItem, setSelectedItem ] = useState(0)
return(
props['list'].map((item, index) => (
<li
key={index}
onClick={() => setSelectedItem(index)}
>
<Link
to={`${item['path']}/location=${props['pathName']}`}
className='menu-component-root'
>
{/* CIRCLE */}
<div className={selectedItem === index ? 'circle-selected' : 'circle'}></div>
{/* TEXT */}
<p className='text'>
{item['text']}
</p>
</Link>
</li>
))
)
}
export default MenuComponent
menuList.js:
const menuList = [
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/payload',
text: 'Payload'
},
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/route/summary',
text: 'Route/Summary'
}
]
export default menuList
Summary.js:
import React from "react"
import { Row, Col } from "reactstrap"
// STYLES
import './styles.scss'
const Summary = () => {
return (
<React.Fragment>
<div className="container-fluid route-summary-root">
<Row>
<Col className="col-12">
{/* TOP CONTAINER */}
<div className='top-container'>
{/* LEFT CONTAINER */}
<div className='top-left-container'>
<h3 className='title'> Route/Summary Page</h3>
</div>
</div>
</Col>
</Row>
</div>
</React.Fragment>
)
}
export default Summary
Payload.js:
import React from "react"
import { Row, Col } from "reactstrap"
// STYLES
import './styles.scss'
const Payload = () => {
return (
<React.Fragment>
<div className="container-fluid payload-root">
<Row>
<Col className="col-12">
{/* TOP CONTAINER */}
<div className='top-container'>
{/* LEFT CONTAINER */}
<div className='top-left-container'>
<h3 className='title'>Payload Page</h3>
</div>
</div>
</Col>
</Row>
</div>
</React.Fragment>
)
}
export default Payload
Note: let me know if the question is confusing. I will try to update it.