8000 [Release] Stage to Main by milo-pr-merge[bot] · Pull Request #4515 · adobecom/milo · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[Release] Stage to Main #4515

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
8000
Diff view
2 changes: 1 addition & 1 deletion libs/blocks/caas-config/caas-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import {
getConfig,
parseEncodedConfig,
loadStyle,
isValidHtmlUrl,
} from '../../utils/utils.js';
import Accordion from '../../ui/controls/Accordion.js';
import {
decodeCompressedString,
defaultState,
initCaas,
isValidHtmlUrl,
isValidUuid,
loadCaasFiles,
loadCaasTags,
Expand Down
4 changes: 0 additions & 4 deletions libs/blocks/caas/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,6 @@ export function getPageLocale(currentPath, locales = pageLocales) {
return '';
}

export const isValidHtmlUrl = (url) => {
const regex = /^https:\/\/[^\s]+$/;
return regex.test(url);
};
export const isValidUuid = (id) => /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id);

export const loadStrings = async (
Expand Down
3 changes: 2 additions & 1 deletion libs/blocks/global-navigation/global-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,7 @@ class Gnav {
header.style.top = 0;
localNav.style.top = promoHeight;
}
if (!isDesktop.matches) this.updatePopupPosition();
};

if (this.elements.aside.clientHeight > fedsPromoWrapper.clientHeight) {
Expand Down Expand Up @@ -1547,7 +1548,7 @@ class Gnav {
return !this.customLinks.includes(linkHash);
};
[...customLinksSection.classList].splice(1).forEach((className) => {
customLinkModifier = ` feds-navItem--${className}`;
customLinkModifier += ` feds-navItem--${className}`;
});
removeCustomLink = removeLink();
} else if (itemHasActiveLink) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,56 @@ function getSidenav(collection) {
return sidenav;
}

function handleCustomAnalyticsEvent(eventName, element) {
let daaLhValue = '';
let daaLhElement = element.closest('[daa-lh]');
while (daaLhElement) {
if (daaLhValue) {
daaLhValue = `|${daaLhValue}`;
}
const daaLhAttrValue = daaLhElement.getAttribute('daa-lh');
daaLhValue = `${daaLhAttrValue}${daaLhValue}`;
daaLhElement = daaLhElement.parentElement.closest('[daa-lh]');
}
if (daaLhValue) {
// eslint-disable-next-line no-underscore-dangle
window._satellite?.track('event', {
xdm: {},
data: { web: { webInteraction: { name: `${eventName}|${daaLhValue}` } } },
});
}
}

function enableAnalytics(el) {
const tabs = el.closest('.tabs');
if (!tabs || tabs.analyticsInitiated) return;
tabs.analyticsInitiated = true;

window.addEventListener('merch-sidenav:select', ({ target }) => {
if (!target || target.oldValue === target.selectedValue) return;
handleCustomAnalyticsEvent(`${target.selectedValue}--cat`, target);
target.oldValue = target.selectedValue;
});

window.addEventListener('mas:ready', ({ target }) => {
target.querySelectorAll('merch-addon').forEach((ao) => {
ao.addEventListener('change', (aoe) => {
handleCustomAnalyticsEvent(`addon-${aoe.detail.checked ? 'checked' : 'unchecked'}`, aoe.target);
});
});
target.querySelectorAll('merch-quantity-select').forEach((qs) => {
qs.addEventListener('merch-quantity-selector:change', (qse) => {
handleCustomAnalyticsEvent(`quantity-${qse.detail.option}`, qse.target);
});
});
});

window.addEventListener('milo:tab:changed', () => {
const tab = tabs.querySelector('button[role="tab"][aria-selected="true"]');
if (tab) handleCustomAnalyticsEvent(`tab-change--${tab.getAttribute('daa-ll')}`, tab);
});
}

export async function checkReady(masElement) {
const readyPromise = masElement.checkReady();
const success = await Promise.race([readyPromise, getTimeoutPromise()]);
Expand Down Expand Up @@ -152,11 +202,13 @@ export async function createCollection(el, options) {
if (sidenav) {
container.insertBefore(sidenav, collection);
collection.sidenav = sidenav;
sidenav.setAttribute('daa-lh', 'b3|filters');
}
}

postProcessAutoblock(collection);
collection.requestUpdate();
enableAnalytics(collection);
}

export default async function init(el) {
Expand Down
11 changes: 0 additions & 11 deletions libs/blocks/merch/merch.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,6 @@ a[is='checkout-link'].con-button > span {
visibility: hidden;
}

.three-in-one sp-theme {
height: 100%;
width: 100%;
align-content: center;
}

.three-in-one sp-theme sp-progress-circle {
inset-inline-start: 50%;
transform: translate(-50%);
}

.error-wrapper {
display: flex;
justify-content: space-between;
Expand Down
101 changes: 79 additions & 22 deletions libs/blocks/merch/merch.js
Original file line number Diff line number Diff line change
Expand Up @@ -567,20 +567,83 @@ async function openExternalModal(url, getModal, extraOptions, el) {

const isInternalModal = (url) => /\/fragments\//.test(url);

// Modal state handling: see merch.md
export const modalState = { isOpen: false };

export async function updateModalState({ cta, closedByUser } = {}) {
const { hash } = window.location;

if (hash?.includes('=')) {
const modal = document.querySelector('.dialog-modal');
if (!modal) return modalState.isOpen;
modalState.isOpen = false;
document.querySelectorAll(`#${modal.id}`).forEach((mod) => {
if (mod.classList.contains('dialog-modal')) {
const modalCurtain = document.querySelector(`#${modal.id}~.modal-curtain`);
if (modalCurtain) {
modalCurtain.remove();
}
mod.remove();
}
document.querySelector(`[data-modal-hash="#${mod.id}"]`)?.focus();
});

if (!document.querySelectorAll('.modal-curtain').length) {
document.body.classList.remove('disable-scroll');
}

[...document.querySelectorAll('header, main, footer')]
.forEach((element) => element.removeAttribute('aria-disabled'));

return modalState.isOpen;
}

const modal = document.querySelector(`.dialog-modal${hash}`);

if (hash && !cta && !modalState.isOpen && !modal) {
const ctaToClick = document.querySelector(`[is=checkout-link][data-modal-id=${hash.replace('#', '')}]`);
if (ctaToClick && !ctaToClick.dataset.clickDisabled) {
ctaToClick.dataset.clickDisabled = 'true';
ctaToClick.click();
setTimeout(() => {
delete ctaToClick.dataset.clickDisabled;
}, 1000);
}
modalState.isOpen = true;
return modalState.isOpen;
}

if (hash && hash === `#${cta?.getAttribute('data-modal-id')}` && !modalState.isOpen && !modal) {
cta.click();
modalState.isOpen = true;
return modalState.isOpen;
}

if (closedByUser && modal) {
modalState.isOpen = false;
return modalState.isOpen;
}

if (!hash && modal) {
modalState.isOpen = false;
const { closeModal } = await import('../modal/modal.js');
closeModal(modal);
}

return modalState.isOpen;
}

export async function openModal(e, url, offerType, hash, extraOptions, el) {
e.preventDefault();
e.stopImmediatePropagation();
if (modalState.isOpen) return;
modalState.isOpen = true;
const { getModal } = await import('../modal/modal.js');
await import('../modal/modal.merch.js');
const offerTypeClass = offerType === OFFER_TYPE_TRIAL ? 'twp' : 'crm';
let modal;
if (hash) {
const prevHash = window.location.hash.replace('#', '') === hash ? '' : window.location.hash;
window.location.hash = hash;
window.addEventListener('milo:modal:closed', () => {
window.history.pushState({}, document.title, prevHash !== '' ? `#${prevHash}` : `${window.location.pathname}${window.location.search}`);
}, { once: true });
}

if (hash) window.location.hash = hash;

if (el?.isOpen3in1Modal) {
const { default: openThreeInOneModal, handle3in1IFrameEvents } = await import('./three-in-one.js');
Expand Down Expand Up @@ -813,19 +876,6 @@ export async function getPriceContext(el, params) {
};
}

let modalReopened = false;
export function reopenModal(cta) {
if (modalReopened) return;
if (cta && cta.getAttribute('data-modal-id') === window.location.hash.replace('#', '')) {
cta.click();
modalReopened = true;
}
}

export function resetReopenStatus() {
modalReopened = false;
}

export async function buildCta(el, params) {
const large = !!el.closest('.marquee');
const strong = el.firstElementChild?.tagName === 'STRONG' || el.parentElement?.tagName === 'STRONG';
Expand All @@ -848,8 +898,7 @@ export async function buildCta(el, params) {
cta.classList.add(LOADING_ENTITLEMENTS);
cta.onceSettled().finally(() => {
cta.classList.remove(LOADING_ENTITLEMENTS);
// after opening a modal, navigating to another page and back we need to reopen the modal
reopenModal(cta);
updateModalState({ cta });
});
}

Expand Down Expand Up @@ -941,3 +990,11 @@ export default async function init(el) {
log.warn('Failed to get context:', { el });
return null;
}

window.addEventListener('hashchange', updateModalState);

window.addEventListener('popstate', updateModalState);

window.addEventListener('milo:modal:closed', () => {
updateModalState({ closedByUser: true });
});
42 changes: 42 additions & 0 deletions libs/blocks/merch/merch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Merch Modal State Handling

The `updateModalState` function manages the state of merch modals across different user interactions and browser navigation scenarios. This function handles five primary use cases to ensure proper modal behavior.

## Use Cases

### Use Case #1: Merch Card Collection Filters

When users have filters selected on merch card collections, open a modal, and then close it, the hash changes to the previous one (with filters). The modal doesn't get closed by `modal.js` because `modal.js` only closes modals when there is no hash in the URL. This function handles closing the modal in this scenario.

**Example URL with filters in hash:**
```
https://main--cc--adobecom.aem.live/products/catalog#category=photo&types=desktop
```

**Technical Details:**
- When hash includes '=' it is not a valid selector and throws an error in the console when trying to find the modal by hash
- Example: `document.querySelector('.dialog-modal#category=photo&types=desktop')`
- To avoid this error, we select the modal only by the class

### Use Case #2: Browser Back-Forward Navigation

Handles user clicks and browser back-forward navigation scenarios.

**Scenario:** When a user opens a modal, closes it, and clicks 'Back' in the browser, the page is not reloaded. The function finds the first CTA matching the hash and clicks it to restore the modal state.

### Use Case #3: Reopening Modal on Page Load

Reopens the modal on page load if the URL contains the hash. The function waits for each CTA to be ready and tries to reopen the modal by clicking the first CTA with a matching `data-modal-id` attribute.

### Use Case #4: Modal Closed by User

Updates the modal state to reflect when a modal has been closed by the user.

### Use Case #5: Hash Removed from URL

If there is no hash in the URL but the modal is still open, the function closes it to maintain consistency.

## Implementation Notes

- The function returns the current modal state, which is used in tests
- Modal state is tracked via the `modalState.isOpen` boolean
6 changes: 3 additions & 3 deletions libs/blocks/merch/three-in-one.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,15 @@ export const handle3in1IFrameEvents = ({ data: msgData }) => {
}
};

export const handleTimeoutError = () => {
export const handleTimeoutError = async () => {
const modal = document.querySelector('.three-in-one');
const miloIframe = modal?.querySelector('.milo-iframe');
const iframe = modal?.querySelector('iframe');
const theme = modal?.querySelector('sp-theme');
if (iframe?.getAttribute('data-pageloaded') || !miloIframe || !iframe || !theme) return;
const wasReloaded = iframe.getAttribute('data-wasreloaded') === 'true';

showErrorMsg({ iframe, miloIframe, showBtn: !wasReloaded, theme, handleTimeoutError });
await showErrorMsg({ iframe, miloIframe, showBtn: !wasReloaded, theme, handleTimeoutError });

if (wasReloaded) {
setTimeout(() => {
Expand All @@ -133,7 +133,7 @@ export const handleTimeoutError = () => {

export function createContent(iframeUrl) {
const content = createTag('div', { class: 'milo-iframe' });
content.innerHTML = `<sp-theme system="light" color="light" scale="medium" dir="ltr">
content.innerHTML = `<sp-theme system="light" color="light" scale="medium" dir="ltr" style="display: flex; justify-content: center; align-items: center; height: 100%;">
<sp-progress-circle label="progress circle" indeterminate="" size="l" dir="ltr" role="progressbar" aria-label="progress circle"></sp-progress-circle>
</sp-theme>
<iframe src="${iframeUrl}" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen="true" loading="lazy" class="loading" style="height: 100%;"></iframe>`;
Expand Down
5 changes: 3 additions & 2 deletions libs/blocks/modal/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ export function closeModal(modal) {
[...document.querySelectorAll('header, main, footer')]
.forEach((element) => element.removeAttribute('aria-disabled'));

const hashId = window.location.hash.replace('#', '');
if (hashId === modal.id) window.history.pushState('', document.title, `${window.location.pathname}${window.location.search}`);
if (window.location.hash === `#${modal.id}`) {
window.history.pushState(window.history.state, document.title, `${window.location.pathname}${window.location.search}`);
}
isDelayedModal = false;
if (prevHash) {
window.location.hash = prevHash;
Expand Down
4 changes: 2 additions & AEB1 2 deletions libs/blocks/reading-time/reading-time.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@import '../../styles/inline.css';

.reading-time > span {
color: #adadad;
color: rgb(80 80 80);
font-style: italic;
font-weight: 700;
font-size: var(--type-body-s-size);
Expand Down Expand Up @@ -40,7 +40,7 @@ main > .section > .inline-wrapper > .inline.reading-time + .inline.share {
border-left: none;
padding-left: 0;
}

main > .section > .inline-wrapper > .inline + .inline.reading-time {
border-top: 1px solid #adadad;
}
Expand Down
Loading
Loading
0