diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index bc76e60afb..8af6c8c0bc 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -15,6 +15,7 @@ import { getExperienceName, getFedsPlaceholderConfig, hasActiveLink, + isActiveLink, icons, isDesktop, isTangentToViewport, @@ -967,21 +968,17 @@ class Gnav { let customLinkModifier = ''; let removeCustomLink = false; const linkElem = item.querySelector('a'); + const customLinksSection = item.closest('.link-group'); linkElem.className = 'feds-navLink'; linkElem.setAttribute('daa-ll', getAnalyticsValue(linkElem.textContent, index + 1)); - if (itemHasActiveLink) { - linkElem.removeAttribute('href'); - linkElem.setAttribute('role', 'link'); - linkElem.setAttribute('aria-disabled', 'true'); - linkElem.setAttribute('aria-current', 'page'); - linkElem.setAttribute('tabindex', 0); - } - const customLinksSection = item.closest('.link-group'); if (customLinksSection) { const removeLink = () => { const url = new URL(linkElem.href); linkElem.setAttribute('href', `${url.origin}${url.pathname}${url.search}`); + if (isActiveLink(linkElem)) { + linkElem.removeAttribute('href'); + } const linkHash = url.hash.slice(2); return !this.customLinks.includes(linkHash); }; @@ -989,6 +986,12 @@ class Gnav { customLinkModifier = ` feds-navItem--${className}`; }); removeCustomLink = removeLink(); + } else if (itemHasActiveLink) { + linkElem.removeAttribute('href'); + linkElem.setAttribute('role', 'link'); + linkElem.setAttribute('aria-disabled', 'true'); + linkElem.setAttribute('aria-current', 'page'); + linkElem.setAttribute('tabindex', 0); } const linkTemplate = toFragment` diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index a5050ea458..2dc258fd0c 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -266,20 +266,22 @@ export const [setDisableAEDState, getDisableAEDState] = (() => { ]; })(); -export const [hasActiveLink, setActiveLink, getActiveLink] = (() => { +export const [hasActiveLink, setActiveLink, isActiveLink, getActiveLink] = (() => { let activeLinkFound; + const { origin, pathname } = window.location; + const url = `${origin}${pathname}`; return [ () => activeLinkFound, (val) => { activeLinkFound = !!val; }, + (el) => (el.href === url || el.href.startsWith(`${url}?`) || el.href.startsWith(`${url}#`)), (area) => { - const disableAED = getDisableAEDState(); + const isCustomLinks = area.closest('.link-group')?.classList.contains('mobile-only'); + const disableAED = getDisableAEDState() || isCustomLinks; if (disableAED || hasActiveLink() || !(area instanceof HTMLElement)) return null; - const { origin, pathname } = window.location; - const url = `${origin}${pathname}`; const activeLink = [ ...area.querySelectorAll('a:not([data-modal-hash])'), - ].find((el) => (el.href === url || el.href.startsWith(`${url}?`) || el.href.startsWith(`${url}#`))); + ].find(isActiveLink); if (!activeLink) return null; diff --git a/libs/navigation/bootstrapper.js b/libs/navigation/bootstrapper.js index f231b4df6d..4f19bb3ffd 100644 --- a/libs/navigation/bootstrapper.js +++ b/libs/navigation/bootstrapper.js @@ -1,17 +1,28 @@ export default async function bootstrapBlock(miloLibs, blockConfig) { - const { name, targetEl } = blockConfig; + const { name, targetEl, layout, noBorder, jarvis } = blockConfig; const { getConfig, createTag, loadLink, loadScript } = await import(`${miloLibs}/utils/utils.js`); const { default: initBlock } = await import(`${miloLibs}/blocks/${name}/${name}.js`); const styles = [`${miloLibs}/blocks/${name}/${name}.css`, `${miloLibs}/navigation/navigation.css`]; styles.forEach((url) => loadLink(url, { rel: 'stylesheet' })); + const setNavLayout = () => { + const element = document.querySelector(targetEl); + if (layout === 'fullWidth') { + element.classList.add('feds--full-width'); + } + if (noBorder) { + element.classList.add('feds--no-border'); + } + }; + if (!document.querySelector(targetEl)) { const block = createTag(targetEl, { class: name }); document.body[blockConfig.appendType](block); } // Configure Unav components and redirect uri if (blockConfig.targetEl === 'header') { + setNavLayout(); const metaTags = [ { key: 'unavComponents', name: 'universal-nav' }, { key: 'redirect', name: 'adobe-home-redirect' }, @@ -35,4 +46,42 @@ export default async function bootstrapBlock(miloLibs, blockConfig) { loadPrivacy(getConfig, loadScript); }, blockConfig.delay); } + + /** Jarvis Chat */ + if (jarvis?.id) { + const isChatInitialized = (client) => !!client?.isAdobeMessagingClientInitialized(); + + const isChatOpen = (client) => isChatInitialized(client) && client?.getMessagingExperienceState()?.windowState !== 'hidden'; + + const openChat = (event) => { + const client = window.AdobeMessagingExperienceClient; + + /* c8 ignore next 4 */ + if (!isChatInitialized(client)) { + window.location.assign('https://helpx.adobe.com'); + return; + } + + const open = client?.openMessagingWindow; + if (typeof open !== 'function' || isChatOpen(client)) { + return; + } + + const sourceType = event?.target.tagName?.toLowerCase(); + const sourceText = sourceType === 'img' ? event.target.alt?.trim() : event.target.innerText?.trim(); + + open(event ? { sourceType, sourceText } : {}); + }; + + const addDomEvents = () => { + document.addEventListener('click', (event) => { + if (!event.target.closest('[href*="#open-jarvis-chat"]')) return; + event.preventDefault(); + openChat(event); + }); + }; + + // Attach DOM events + addDomEvents(); + } } diff --git a/libs/navigation/navigation.css b/libs/navigation/navigation.css index 7accdaca5d..cb028671a6 100644 --- a/libs/navigation/navigation.css +++ b/libs/navigation/navigation.css @@ -37,6 +37,10 @@ header.global-navigation, header.global-navigation.feds--dark { color: #136FF6; } + .feds--no-border .feds-topnav-wrapper { + border-bottom: none; + } + .feds--full-width .feds-topnav { max-width: none; padding: 0 15px; diff --git a/libs/navigation/navigation.js b/libs/navigation/navigation.js index 993c2399b8..896dfecfda 100644 --- a/libs/navigation/navigation.js +++ b/libs/navigation/navigation.js @@ -74,7 +74,13 @@ export default async function loadBlock(configs, customLib) { if (configBlock) { await bootstrapBlock(`${miloLibs}/libs`, { ...block, - ...(block.key === 'header' && { unavComponents: configBlock.unav?.unavComponents, redirect: configBlock.redirect }), + ...(block.key === 'header' && { + unavComponents: configBlock.unav?.unavComponents, + redirect: configBlock.redirect, + layout: configBlock.layout, + noBorder: configBlock.noBorder, + jarvis: configBlock.jarvis, + }), }); configBlock.onReady?.(); } diff --git a/test/blocks/global-navigation/global-navigation.test.js b/test/blocks/global-navigation/global-navigation.test.js index d28284a70f..9c978c246f 100644 --- a/test/blocks/global-navigation/global-navigation.test.js +++ b/test/blocks/global-navigation/global-navigation.test.js @@ -649,7 +649,7 @@ describe('global navigation', () => { describe('Custom Links for mobile hamburger menu', () => { it('Add custom links through Link Group block in parallel to large menu\'s', async () => { - const customLinks = 'home,learn'; + const customLinks = 'home,apps,learn'; await createFullGlobalNavigation({ viewport: 'mobile', globalNavigation: navigationWithCustomLinks, diff --git a/test/blocks/global-navigation/mocks/navigation-with-custom-links.plain.js b/test/blocks/global-navigation/mocks/navigation-with-custom-links.plain.js index b58cb5259b..2c8dc0f1ec 100644 --- a/test/blocks/global-navigation/mocks/navigation-with-custom-links.plain.js +++ b/test/blocks/global-navigation/mocks/navigation-with-custom-links.plain.js @@ -46,7 +46,7 @@ export default `