Skip to content

Commit

Permalink
[MWPW-159905] - Add config for full width and no border along with ja…
Browse files Browse the repository at this point in the history
…rvis fix for standalone GNAV (#3020)

* Add config for full width Standalone GNAV

* lint fix

* UT coverage

* fix reveiw comment

* Make GNAV border bottom configurable for standalone GNAV

* UT added

* relative url AED disable for custom links

* Fix review comment

* relative url AED disable for custom links

* fix active link href

* UT

* Added jarvis fix in this PR
  • Loading branch information
Deva309 authored Oct 22, 2024
1 parent deae355 commit 63ff041
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 20 deletions.
19 changes: 11 additions & 8 deletions libs/blocks/global-navigation/global-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
getExperienceName,
getFedsPlaceholderConfig,
hasActiveLink,
isActiveLink,
icons,
isDesktop,
isTangentToViewport,
Expand Down Expand Up @@ -968,28 +969,30 @@ 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);
};
[...customLinksSection.classList].splice(1).forEach((className) => {
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`
Expand Down
12 changes: 7 additions & 5 deletions libs/blocks/global-navigation/utilities/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
51 changes: 50 additions & 1 deletion libs/navigation/bootstrapper.js
Original file line number Diff line number Diff line change
@@ -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' },
Expand All @@ -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();
}
}
4 changes: 4 additions & 0 deletions libs/navigation/navigation.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion libs/navigation/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,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?.();
}
Expand Down
2 changes: 1 addition & 1 deletion test/blocks/global-navigation/global-navigation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default `<body><div>
<div>
<div>
<h2 id="home"><a href="https://www.google.com/#_home">Home</a></h2>
<h2 id="apps"><a href="https://www.google.com/#_apps">Apps</a></h2>
<h2 id="apps"><a href="/#_apps">Apps</a></h2>
<h2 id="learn"><a href="https://www.google.com/#_learn">Learn</a></h2>
</div>
</div>
Expand Down
49 changes: 46 additions & 3 deletions test/navigation/bootstrapper.test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { readFile } from '@web/test-runner-commands';
import { expect } from '@esm-bundle/chai';
import { stub, useFakeTimers, restore } from 'sinon';
import { stub, useFakeTimers, restore, spy } from 'sinon';
import loadBlock from '../../libs/navigation/bootstrapper.js';
import fetchedFooter from '../blocks/global-footer/mocks/fetched-footer.js';
import placeholders from '../blocks/global-navigation/mocks/placeholders.js';
import { setConfig } from '../../libs/utils/utils.js';
import { mockRes } from '../blocks/global-navigation/test-utilities.js';

document.body.innerHTML = await readFile({ path: './mocks/body.html' });

const blockConfig = {
footer: {
name: 'global-footer',
Expand All @@ -26,7 +24,9 @@ const blockConfig = {
const miloLibs = 'http://localhost:2000/libs';

describe('Bootstrapper', async () => {
let openMessagingWindowSpy;
beforeEach(async () => {
document.body.innerHTML = await readFile({ path: './mocks/body.html' });
stub(window, 'fetch').callsFake(async (url) => {
if (url.includes('/footer')) {
return mockRes({
Expand All @@ -41,10 +41,18 @@ describe('Bootstrapper', async () => {

return null;
});
window.AdobeMessagingExperienceClient = window.AdobeMessagingExperienceClient
|| {
openMessagingWindow: () => {},
isAdobeMessagingClientInitialized: () => {},
getMessagingExperienceState: () => {},
};
openMessagingWindowSpy = spy(window.AdobeMessagingExperienceClient, 'openMessagingWindow');
setConfig({ miloLibs, contentRoot: '/federal/dev' });
});

afterEach(() => {
document.body.innerHTML = '';
restore();
});

Expand All @@ -64,4 +72,39 @@ describe('Bootstrapper', async () => {
const el = document.getElementsByTagName('header');
expect(el).to.exist;
});

it('Renders the header with full width', async () => {
blockConfig.header.layout = 'fullWidth';
await loadBlock(miloLibs, blockConfig.header);
const el = document.querySelector('header');
expect(el.classList.contains('feds--full-width')).to.be.true;
});

it('Renders the header with no border bottom', async () => {
blockConfig.header.noBorder = true;
await loadBlock(miloLibs, blockConfig.header);
const el = document.querySelector('header');
expect(el.classList.contains('feds--no-border')).to.be.true;
});

it('should call openMessagingWindow when click on jarvis enabled button', async () => {
blockConfig.header.jarvis = { id: '1.1' };
stub(window.AdobeMessagingExperienceClient, 'isAdobeMessagingClientInitialized').returns(true);
stub(window.AdobeMessagingExperienceClient, 'getMessagingExperienceState').returns({ windowState: 'hidden' });
await loadBlock(miloLibs, blockConfig.header);
const el = document.querySelector('.feds-cta[href*="#open-jarvis-chat"]');
const event = new CustomEvent('click', { bubbles: true });
el.dispatchEvent(event);
expect(openMessagingWindowSpy.called).to.be.true;
});

it('should not call openMessagingWindow when chat dialog is already open', async () => {
stub(window.AdobeMessagingExperienceClient, 'isAdobeMessagingClientInitialized').returns(true);
stub(window.AdobeMessagingExperienceClient, 'getMessagingExperienceState').returns({ windowState: 'docked' });
await loadBlock(miloLibs, blockConfig.header);
const el = document.querySelector('.feds-cta[href*="#open-jarvis-chat"]');
const event = new CustomEvent('click', { bubbles: true });
el.dispatchEvent(event);
expect(openMessagingWindowSpy.called).to.be.false;
});
});
3 changes: 3 additions & 0 deletions test/navigation/mocks/gnav.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ <h2 id="help--support"><a href="/federal/globalnav/drafts/blaishram/sections/sec
<div>
<p><strong><a href="http://www.google.com/">Just link</a></strong></p>
</div>
<div>
<p><strong><a href="https://helpx.adobe.com/support.html#open-jarvis-chat">Jarvis Chat</a></strong></p>
</div>
<div></div>
</main>
<footer></footer>
Expand Down

0 comments on commit 63ff041

Please sign in to comment.