diff --git a/app/assets/javascripts/sidenav.js b/app/assets/javascripts/sidenav.js index 289b0a03a..24c9f2c5b 100644 --- a/app/assets/javascripts/sidenav.js +++ b/app/assets/javascripts/sidenav.js @@ -1,42 +1,57 @@ document.addEventListener('DOMContentLoaded', () => { const sidenavItems = document.querySelectorAll('.usa-sidenav__item > .parent-link'); + let lastPath = window.location.pathname; + let debounceTimeout = null; sidenavItems.forEach((link) => { const parentItem = link.parentElement; - const hasChildren = parentItem.querySelector('.usa-sidenav__sublist'); - const currentUrl = window.location.pathname; + const sublist = parentItem.querySelector('.usa-sidenav__sublist'); + const targetHref = link.getAttribute('href'); - if ( - link.getAttribute('href') === currentUrl || - currentUrl.startsWith(link.getAttribute('href')) - ) { + // initialize the menu to open the correct submenu based on the current route + if (window.location.pathname.startsWith(targetHref)) { parentItem.classList.add('open'); link.setAttribute('aria-expanded', 'true'); } link.addEventListener('click', (event) => { - if (hasChildren) { - event.preventDefault(); - - const isOpen = parentItem.classList.contains('open'); + const currentPath = window.location.pathname; - document.querySelectorAll('.usa-sidenav__item.open').forEach((item) => { - if (item !== parentItem) { - item.classList.remove('open'); - item.querySelector('.parent-link').setAttribute('aria-expanded', 'false'); - } - }); + // prevent default behavior only if navigating to the same route + if (currentPath === targetHref) { + event.preventDefault(); + return; + } - if (!isOpen) { + if (sublist && !parentItem.classList.contains('open')) { + // debounce the menu update to avoid flickering + clearTimeout(debounceTimeout); + debounceTimeout = setTimeout(() => { parentItem.classList.add('open'); link.setAttribute('aria-expanded', 'true'); - } else { - parentItem.classList.remove('open'); - link.setAttribute('aria-expanded', 'false'); - } + }, 50); + } + }); + }); + + // handle browser back/forward navigation + window.addEventListener('popstate', () => { + const currentPath = window.location.pathname; - window.location.href = link.getAttribute('href'); + // sync menu state + sidenavItems.forEach((link) => { + const parentItem = link.parentElement; + const targetHref = link.getAttribute('href'); + + if (currentPath.startsWith(targetHref)) { + parentItem.classList.add('open'); + link.setAttribute('aria-expanded', 'true'); + } else { + parentItem.classList.remove('open'); + link.setAttribute('aria-expanded', 'false'); } }); + + lastPath = currentPath; }); }); diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss index 59bc5e12f..c06bc43ab 100644 --- a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss +++ b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss @@ -915,17 +915,20 @@ li.linked-card:hover svg, } .usa-sidenav__sublist { - display: none; /* Default: hidden */ + display: none; } .usa-sidenav__item.open .usa-sidenav__sublist { - display: block; /* Show sublist when the parent has the 'open' class */ + display: block; } .usa-sidenav__sublist .bold { font-weight: bold; } +.usa-sidenav__sublist li[role="menuitem"] { + border-top: 1px solid #dfe1e2; +} .icon-list { display: flex; diff --git a/app/templates/base.html b/app/templates/base.html index 1d2778a9e..bc46d3171 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -50,7 +50,7 @@ {% block maincolumn_content %}