diff --git a/libs/blocks/global-navigation/features/aside/aside.js b/libs/blocks/global-navigation/features/aside/aside.js index 2abbf96eaa..0d956460ee 100644 --- a/libs/blocks/global-navigation/features/aside/aside.js +++ b/libs/blocks/global-navigation/features/aside/aside.js @@ -2,8 +2,9 @@ import { loadBlock, decorateAutoBlock } from '../../../../utils/utils.js'; import { toFragment, lanaLog } from '../../utilities/utilities.js'; import { processTrackingLabels } from '../../../../martech/attributes.js'; -export default async function decorateAside({ headerElem, promoPath } = {}) { +export default async function decorateAside({ headerElem, fedsPromoWrapper, promoPath } = {}) { const onError = () => { + fedsPromoWrapper?.remove(); headerElem?.classList.remove('has-promo'); lanaLog({ message: 'Gnav Promo fragment not replaced, potential CLS' }); return ''; diff --git a/libs/blocks/global-navigation/global-navigation.css b/libs/blocks/global-navigation/global-navigation.css index 97443bcc9d..d084f3139c 100644 --- a/libs/blocks/global-navigation/global-navigation.css +++ b/libs/blocks/global-navigation/global-navigation.css @@ -1077,6 +1077,10 @@ header.new-nav .feds-breadcrumbs li:first-child:not(:nth-last-child(-n+3)):after z-index: 9; } +.feds-localnav.has-promo { + top: var(--global-height-navPromo); +} + .feds-localnav a { text-decoration: unset; } @@ -1200,6 +1204,13 @@ header.new-nav .feds-breadcrumbs li:first-child:not(:nth-last-child(-n+3)):after display: block; } +.feds-promo-aside-wrapper { + position: sticky; + top: 0; + z-index: 11; + height: var(--global-height-navPromo); +} + @keyframes slideright { from { translate: 0 0; diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index 0c4cc1dae7..d51b6a1ff5 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -444,6 +444,8 @@ class Gnav { document.body.classList.remove('disable-scroll'); enableMobileScroll(); }); + const promo = document.querySelector('.feds-promo-aside-wrapper'); + if (promo) localNav.classList.add('has-promo'); this.elements.localNav = localNav; localNavItems[0].querySelector('a').textContent = title.trim(); const isAtTop = () => { @@ -472,7 +474,6 @@ class Gnav { this.block.append( this.elements.curtain, - this.elements.aside, this.elements.topnavWrapper, ); }; @@ -936,14 +937,18 @@ class Gnav { decorateAside = async () => { this.elements.aside = ''; const promoPath = getMetadata('gnav-promo-source'); + const fedsPromoWrapper = document.querySelector('.feds-promo-aside-wrapper'); + if (!promoPath) { + fedsPromoWrapper?.remove(); this.block.classList.remove('has-promo'); return this.elements.aside; } const { default: decorate } = await import('./features/aside/aside.js'); if (!decorate) return this.elements.aside; - this.elements.aside = await decorate({ headerElem: this.block, promoPath }); + this.elements.aside = await decorate({ headerElem: this.block, fedsPromoWrapper, promoPath }); + fedsPromoWrapper.append(this.elements.aside); return this.elements.aside; }; diff --git a/libs/styles/styles.css b/libs/styles/styles.css index a252b22aaa..a2d888cc11 100644 --- a/libs/styles/styles.css +++ b/libs/styles/styles.css @@ -716,11 +716,6 @@ header.global-navigation, header.global-navigation.feds--dark { visibility: hidden; } -header.global-navigation.has-promo { - height: auto; - min-height: calc(var(--global-height-nav) + var(--global-height-navPromo)); -} - header.global-navigation a { text-decoration: unset; } @@ -750,6 +745,11 @@ header.global-navigation + .feds-localnav { header.global-navigation + .feds-localnav { display: none; } + + header.global-navigation.has-promo { + height: auto; + top: var(--global-height-navPromo); + } } .breadcrumbs { diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 15d02e5f44..10f91e0d30 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -812,7 +812,11 @@ async function decorateHeader() { } if (breadcrumbs) header.append(breadcrumbs); const promo = getMetadata('gnav-promo-source'); - if (promo?.length) header.classList.add('has-promo'); + if (promo?.length) { + const fedsPromoWrapper = createTag('div', { class: 'feds-promo-aside-wrapper' }); + header.before(fedsPromoWrapper); + header.classList.add('has-promo'); + } } async function decorateIcons(area, config) { diff --git a/test/blocks/global-navigation/gnav-promo.test.js b/test/blocks/global-navigation/gnav-promo.test.js index 7828ff7929..58ac789836 100644 --- a/test/blocks/global-navigation/gnav-promo.test.js +++ b/test/blocks/global-navigation/gnav-promo.test.js @@ -19,27 +19,31 @@ describe('Promo', () => { it('doesn\'t exist if metadata is not referencing a fragment', async () => { const wrongPromoMeta = toFragment``; document.head.append(wrongPromoMeta); - const nav = await createFullGlobalNavigation({ hasPromo: true }); + const nav = await createFullGlobalNavigation({ hasPromo: true, hasBreadcrumbs: false }); expect(nav.block.classList.contains('has-promo')).to.be.false; - expect(nav.block.querySelector('.aside.promobar')).to.equal(null); + expect(document.body.querySelector('.aside.promobar')).to.equal(null); wrongPromoMeta.remove(); }); it('doesn\'t exist if fragment doesn\'t contain an aside block', async () => { const promoMeta = toFragment``; document.head.append(promoMeta); - const nav = await createFullGlobalNavigation({ hasPromo: true }); + const nav = await createFullGlobalNavigation({ hasPromo: true, hasBreadcrumbs: false }); expect(nav.block.classList.contains('has-promo')).to.be.false; - expect(nav.block.querySelector('.aside.promobar')).to.equal(null); + expect(document.body.querySelector('.aside.promobar')).to.equal(null); promoMeta.remove(); }); it('is available if set up correctly', async () => { const promoMeta = toFragment``; document.head.append(promoMeta); - const nav = await createFullGlobalNavigation({ hasPromo: true, imsInitialized: true }); + const nav = await createFullGlobalNavigation({ + hasPromo: true, + imsInitialized: true, + hasBreadcrumbs: false, + }); expect(nav.block.classList.contains('has-promo')).to.be.true; - const asideElem = nav.block.querySelector('.aside.promobar'); + const asideElem = document.body.querySelector('.aside.promobar'); expect(asideElem).to.exist; expect(asideElem.getAttribute('daa-lh')).to.equal('Promo'); asideElem.querySelectorAll('a').forEach((linkElem) => { diff --git a/test/blocks/global-navigation/test-utilities.js b/test/blocks/global-navigation/test-utilities.js index 8d4154c913..a32b195f48 100644 --- a/test/blocks/global-navigation/test-utilities.js +++ b/test/blocks/global-navigation/test-utilities.js @@ -217,15 +217,17 @@ export const createFullGlobalNavigation = async ({ ${breadcrumbsEl} `); + if (hasPromo) { + document.body.prepend(toFragment`
`); + } + await Promise.all([ loadStyles('../../../../libs/styles/styles.css'), loadStyles( '../../../../libs/blocks/global-navigation/global-navigation.css', ), ]); - const instancePromise = initGnav(document.body.querySelector('header')); - await clock.runToLastAsync(); clock.tick(1000); const instance = await instancePromise;