diff --git a/libs/blocks/global-navigation/utilities/keyboard/mainNav.js b/libs/blocks/global-navigation/utilities/keyboard/mainNav.js index 9f0d2a2ce7..73c08a69f0 100644 --- a/libs/blocks/global-navigation/utilities/keyboard/mainNav.js +++ b/libs/blocks/global-navigation/utilities/keyboard/mainNav.js @@ -19,8 +19,24 @@ class MainNavItem { return; } + const newNav = !!document.querySelector('header.new-nav'); + switch (e.code) { case 'Tab': { + if (newNav) { + const activePopup = document.querySelector(selectors.activePopup); + if (!activePopup) e.preventDefault(); + const items = [...document.querySelectorAll(`${selectors.mainMenuItems}, ${selectors.mainMenuLinks}`)]; + const current = items.findIndex((x) => x === e.target); + if (current > -1) { + const next = current < items.length - 1 ? current + 1 : 0; + const prev = current > 0 ? current - 1 : items.length - 1; + if (e.shiftKey) items[prev].focus(); + else items[next].focus(); + } else items?.[0]?.focus(); + break; + } + if (e.shiftKey) { const { prev, openTrigger } = this.getState(); if (openTrigger) { @@ -36,9 +52,12 @@ class MainNavItem { } case 'Escape': { closeAllDropdowns(); + const activePopup = document.querySelector(selectors.activePopup); + if (newNav && !activePopup) document.querySelector('header.new-nav .feds-toggle').click(); break; } case 'ArrowLeft': { + if (newNav) break; const { next, prev } = this.getState(); if (document.dir !== 'rtl') { if (prev === -1) break; @@ -50,12 +69,14 @@ class MainNavItem { break; } case 'ArrowUp': { + if (newNav) break; e.preventDefault(); e.stopPropagation(); this.focusPrev({ focus: 'last' }); break; } case 'ArrowRight': { + if (newNav) break; const { next, prev, openTrigger } = this.getState(); if (document.dir !== 'rtl') { if (next === -1) break; @@ -70,6 +91,7 @@ class MainNavItem { break; } case 'ArrowDown': { + if (newNav) break; e.stopPropagation(); e.preventDefault(); const { items, curr } = this.getState(); diff --git a/libs/blocks/global-navigation/utilities/keyboard/mobilePopup.js b/libs/blocks/global-navigation/utilities/keyboard/mobilePopup.js index 0746d1612d..4f6c1b2847 100644 --- a/libs/blocks/global-navigation/utilities/keyboard/mobilePopup.js +++ b/libs/blocks/global-navigation/utilities/keyboard/mobilePopup.js @@ -6,7 +6,7 @@ import { getOpenPopup, selectors, } from './utils.js'; -import { closeAllDropdowns, logErrorFor, setActiveDropdown } from '../utilities.js'; +import { closeAllDropdowns, dropWhile, logErrorFor, setActiveDropdown, takeWhile } from '../utilities.js'; const closeHeadlines = () => { const open = [...document.querySelectorAll(`${selectors.headline}[aria-expanded="true"]`)]; @@ -83,10 +83,12 @@ class Popup { setActiveDropdown(focus === 'first' ? popupItems[first] : popupItems[last]); } - mobileArrowUp = ({ prev, curr, element, isFooter }) => { + mobileArrowUp = ({ prev, curr, element, isFooter, newNav }) => { // Case 1: Move focus to the previous item + const state = getState(element); + const { currentSection } = state; + const popupItems = newNav ? this.popupItems() : state.popupItems; if (prev !== -1 && curr - 1 === prev) { - const { currentSection, popupItems } = getState(element); popupItems[prev].focus(); if (currentSection !== getState(element).currentSection) closeHeadlines(); return; @@ -94,33 +96,37 @@ class Popup { // Case 2: No headline + no previous item, move to the main nav const { prevHeadline } = getState(element); - if (!prevHeadline) { + if (!prevHeadline && !newNav) { this.focusMainNav(isFooter); return; } // Case 3: Open the previous headline - openHeadline({ headline: prevHeadline, focus: 'last' }); + if (newNav) popupItems?.[popupItems.length - 1]?.focus(); + else openHeadline({ headline: prevHeadline, focus: 'last' }); }; - mobileArrowDown = ({ next, element, isFooter }) => { + mobileArrowDown = ({ next, element, isFooter, newNav }) => { // Case 1: Move focus to the next item + const state = getState(element); + const { currentSection } = state; + const popupItems = newNav ? this.popupItems() : state.popupItems; if (next !== -1) { - const { currentSection, popupItems } = getState(element); popupItems[next].focus(); if (currentSection !== getState(element).currentSection) closeHeadlines(); return; } // Case 2: No headline + no next item, move to the main nav const { nextHeadline } = getState(element); - if (!nextHeadline) { + if (!nextHeadline && !newNav) { closeHeadlines(); this.focusMainNavNext(isFooter); return; } // Case 3: Open the next headline - openHeadline({ headline: nextHeadline, focus: 'first' }); + if (newNav) popupItems?.[0]?.focus(); + else openHeadline({ headline: nextHeadline, focus: 'first' }); }; focusMainNav = (isFooter) => { @@ -134,8 +140,29 @@ class Popup { this.mainNav.open(); }; + popupItems = () => { + const activePopup = document.querySelector(selectors.activePopup); + if (!activePopup) return []; + const tabs = [...activePopup.querySelectorAll(selectors.tab)]; + const activeTab = tabs.find((tab) => tab.getAttribute('aria-selected') === 'true'); + const anteActiveTab = takeWhile(tabs, (tab) => tab !== activeTab); + const postActiveTab = dropWhile(tabs, (tab) => tab !== activeTab).slice(1); + const activeLinks = [...activePopup.querySelectorAll(selectors.activeLinks)]; + const stickyCTA = activePopup.querySelector(selectors.stickyCta); + return [ + ...anteActiveTab, + activeTab, + ...activeLinks, + stickyCTA, + ...postActiveTab, + ].filter(Boolean); + }; + handleKeyDown = ({ e, element, isFooter }) => { - const popupItems = [...element.querySelectorAll(selectors.popupItems)]; + const newNav = !!document.querySelector('header.new-nav'); + const popupItems = newNav + ? this.popupItems() + : [...element.querySelectorAll(selectors.popupItems)]; const curr = popupItems.findIndex((el) => el === e.target); const prev = getPreviousVisibleItemPosition(curr, popupItems); const next = getNextVisibleItemPosition(curr, popupItems); @@ -146,9 +173,9 @@ class Popup { switch (e.code) { case 'Tab': { if (e.shiftKey) { - this.mobileArrowUp({ prev, curr, element, isFooter }); + this.mobileArrowUp({ prev, curr, element, isFooter, newNav }); } else { - this.mobileArrowDown({ curr, next, element, isFooter }); + this.mobileArrowDown({ curr, next, element, isFooter, newNav }); } break; } @@ -158,6 +185,7 @@ class Popup { break; } case 'ArrowLeft': { + if (newNav) break; const { prevHeadline, nextHeadline } = getState(element); const headline = document.dir !== 'rtl' ? prevHeadline : nextHeadline; if (!headline) { @@ -173,10 +201,12 @@ class Popup { break; } case 'ArrowUp': { + if (newNav) break; this.mobileArrowUp({ prev, curr, element, isFooter }); break; } case 'ArrowRight': { + if (newNav) break; const { prevHeadline, nextHeadline } = getState(element); const headline = document.dir !== 'rtl' ? nextHeadline : prevHeadline; if (!headline) { @@ -192,6 +222,7 @@ class Popup { break; } case 'ArrowDown': { + if (newNav) break; this.mobileArrowDown({ next, element, isFooter }); break; } diff --git a/libs/blocks/global-navigation/utilities/keyboard/utils.js b/libs/blocks/global-navigation/utilities/keyboard/utils.js index e9c61bdeaa..cb07a93dbb 100644 --- a/libs/blocks/global-navigation/utilities/keyboard/utils.js +++ b/libs/blocks/global-navigation/utilities/keyboard/utils.js @@ -30,6 +30,14 @@ const selectors = { socialLink: '.feds-social-link', privacyLink: '.feds-footer-privacyLink', menuContent: '.feds-menu-content', + /* mobile redesign popup selectors */ + mainMenuItems: 'header.new-nav section.feds-navItem > button', + mainMenuLinks: ' header.new-nav feds-navItem > a', + activePopup: 'header.new-nav section.feds-dropdown--active > .feds-popup', + tab: 'button[role="tab"]', + activeTabpanel: '.tab-content [role="tabpanel"]', + activeLinks: '.tab-content [role="tabpanel"]:not([hidden="true"]) a', + stickyCta: 'header.new-nav .feds-popup .sticky-cta a', }; selectors.profileDropdown = ` diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index 722e330810..3f20380181 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -423,7 +423,7 @@ export const transformTemplateToMobile = async (popup, item, localnav = false) =