diff --git a/libs/navigation/bootstrapper.js b/libs/navigation/bootstrapper.js index cac50f7ef8..4f19bb3ffd 100644 --- a/libs/navigation/bootstrapper.js +++ b/libs/navigation/bootstrapper.js @@ -1,5 +1,5 @@ export default async function bootstrapBlock(miloLibs, blockConfig) { - const { name, targetEl, layout, noBorder } = 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`); @@ -46,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.js b/libs/navigation/navigation.js index bd5c0261d8..896dfecfda 100644 --- a/libs/navigation/navigation.js +++ b/libs/navigation/navigation.js @@ -79,6 +79,7 @@ export default async function loadBlock(configs, customLib) { redirect: configBlock.redirect, layout: configBlock.layout, noBorder: configBlock.noBorder, + jarvis: configBlock.jarvis, }), }); configBlock.onReady?.(); diff --git a/test/navigation/bootstrapper.test.js b/test/navigation/bootstrapper.test.js index 666dfa0429..b16743b57b 100644 --- a/test/navigation/bootstrapper.test.js +++ b/test/navigation/bootstrapper.test.js @@ -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', @@ -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({ @@ -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(); }); @@ -78,4 +86,25 @@ describe('Bootstrapper', async () => { 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; + }); }); diff --git a/test/navigation/mocks/gnav.html b/test/navigation/mocks/gnav.html index 8f0ee27c0b..e79faf161a 100644 --- a/test/navigation/mocks/gnav.html +++ b/test/navigation/mocks/gnav.html @@ -52,6 +52,9 @@