From 58ae3247d73d92f622d98ffb9c8db0f45dbf2ce8 Mon Sep 17 00:00:00 2001 From: Okan Sahin <39759830+mokimo@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:27:52 +0100 Subject: [PATCH 01/17] =?UTF-8?q?Revert=20"MWPW-140452=20-=20Icon=20author?= =?UTF-8?q?ing=20in=20milo=20using=20the=20federal=20repo=20a=E2=80=A6=20(?= =?UTF-8?q?#3357)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "MWPW-140452 - Icon authoring in milo using the federal repo and individual SVG assets (#3259)" This reverts commit 81a5770cecc2a17f07efe5229c65b71d2dbb9bb4. --- libs/blocks/table/table.css | 1 - libs/blocks/text/text.css | 7 +- libs/features/georoutingv2/georoutingv2.css | 1 - libs/features/georoutingv2/georoutingv2.js | 2 +- libs/features/icons/icons.css | 2 +- libs/features/icons/icons.js | 118 ++++---------------- libs/styles/styles.css | 30 ++--- libs/utils/utils.js | 43 +++---- test/features/icons/icons.test.js | 61 +++++----- test/features/icons/mocks/body.html | 4 +- 10 files changed, 81 insertions(+), 188 deletions(-) diff --git a/libs/blocks/table/table.css b/libs/blocks/table/table.css index d2476a42ce1..19eb5beed91 100644 --- a/libs/blocks/table/table.css +++ b/libs/blocks/table/table.css @@ -360,7 +360,6 @@ width: 15px; height: 15px; cursor: pointer; - margin-inline: unset; } .table .section-head-title:hover .icon.expand { diff --git a/libs/blocks/text/text.css b/libs/blocks/text/text.css index f8cbefd3652..6242f50132f 100644 --- a/libs/blocks/text/text.css +++ b/libs/blocks/text/text.css @@ -100,7 +100,7 @@ position: relative; } -.text-block .icon-list-item .icon.node-index-first { +.text-block .icon-list-item .icon.margin-right:not(.margin-left) { /* target first node only */ position: absolute; inset: 0 100% auto auto; } @@ -122,6 +122,7 @@ .text-block .icon-area { display: flex; + column-gap: var(--spacing-xs); } .text-block p.icon-area { /* NOT tags with icons in them */ @@ -217,6 +218,10 @@ max-width: unset; } +.text-block .icon-area.con-button { + column-gap: unset; +} + .text-block .icon-area picture { line-height: 0em; height: inherit; /* Safari + FF bug fix */ diff --git a/libs/features/georoutingv2/georoutingv2.css b/libs/features/georoutingv2/georoutingv2.css index 09df2cba8b3..255ba3f3013 100644 --- a/libs/features/georoutingv2/georoutingv2.css +++ b/libs/features/georoutingv2/georoutingv2.css @@ -83,7 +83,6 @@ } .dialog-modal.locale-modal-v2 span.icon { - display: inline; vertical-align: middle; } diff --git a/libs/features/georoutingv2/georoutingv2.js b/libs/features/georoutingv2/georoutingv2.js index 236e9b685a4..7213696f63a 100644 --- a/libs/features/georoutingv2/georoutingv2.js +++ b/libs/features/georoutingv2/georoutingv2.js @@ -194,7 +194,7 @@ function buildContent(currentPage, locale, geoData, locales) { { once: true }, ); img.src = `${config.miloLibs || config.codeRoot}/img/georouting/${flagFile}`; - const span = createTag('span', { class: 'icon node-index-first' }, img); + const span = createTag('span', { class: 'icon margin-inline-end' }, img); const mainAction = createTag('a', { class: 'con-button blue button-l', lang, role: 'button', 'aria-haspopup': !!locales, 'aria-expanded': false, href: '#', }, span); diff --git a/libs/features/icons/icons.css b/libs/features/icons/icons.css index 1b095e97dc6..f1a8ce53f9d 100644 --- a/libs/features/icons/icons.css +++ b/libs/features/icons/icons.css @@ -4,7 +4,7 @@ border-bottom: none; } -.milo-tooltip::before { +.milo-tooltip::before { content: attr(data-tooltip); position: absolute; top: 50%; diff --git a/libs/features/icons/icons.js b/libs/features/icons/icons.js index a39ff61e065..975c0625dc9 100644 --- a/libs/features/icons/icons.js +++ b/libs/features/icons/icons.js @@ -1,9 +1,5 @@ -import { getFederatedContentRoot } from '../../utils/federated.js'; -import { loadLink, loadStyle } from '../../utils/utils.js'; - let fetchedIcons; let fetched = false; -const federalIcons = {}; async function getSVGsfromFile(path) { /* c8 ignore next */ @@ -26,7 +22,6 @@ async function getSVGsfromFile(path) { return miloIcons; } -// TODO: remove after all consumers have stopped calling this method // eslint-disable-next-line no-async-promise-executor export const fetchIcons = (config) => new Promise(async (resolve) => { /* c8 ignore next */ @@ -39,10 +34,10 @@ export const fetchIcons = (config) => new Promise(async (resolve) => { resolve(fetchedIcons); }); -async function decorateToolTip(icon) { +function decorateToolTip(icon) { const wrapper = icon.closest('em'); - if (!wrapper) return; wrapper.className = 'tooltip-wrapper'; + if (!wrapper) return; const conf = wrapper.textContent.split('|'); // Text is the last part of a tooltip const content = conf.pop().trim(); @@ -50,101 +45,30 @@ async function decorateToolTip(icon) { icon.dataset.tooltip = content; // Position is the next to last part of a tooltip const place = conf.pop()?.trim().toLowerCase() || 'right'; - const defaultIcon = 'info-outline'; - icon.className = `icon icon-${defaultIcon} milo-tooltip ${place}`; - icon.dataset.name = defaultIcon; + icon.className = `icon icon-info milo-tooltip ${place}`; wrapper.parentElement.replaceChild(icon, wrapper); } -export function getIconData(icon) { - const fedRoot = getFederatedContentRoot(); - const name = [...icon.classList].find((c) => c.startsWith('icon-'))?.substring(5); - const path = `${fedRoot}/federal/assets/icons/svgs/${name}.svg`; - return { path, name }; -} - -function preloadInViewIconResources(config) { - const { base } = config; - loadStyle(`${base}/features/icons/icons.css`); -} - -const preloadInViewIcons = async (icons = []) => icons.forEach((icon) => { - const { path } = getIconData(icon); - loadLink(path, { rel: 'preload', as: 'fetch', crossorigin: 'anonymous' }); -}); - -function filterDuplicatedIcons(icons) { - if (!icons.length) return []; - const uniqueIconKeys = new Set(); - const uniqueIcons = []; - for (const icon of icons) { - const key = [...icon.classList].find((c) => c.startsWith('icon-'))?.substring(5); - if (!uniqueIconKeys.has(key)) { - uniqueIconKeys.add(key); - uniqueIcons.push(icon); - } - } - return uniqueIcons; -} - -export async function decorateIcons(area, icons, config) { - if (!icons.length) return; - const uniqueIcons = filterDuplicatedIcons(icons); - if (!uniqueIcons.length) return; - preloadInViewIcons(uniqueIcons); - preloadInViewIconResources(config); - icons.forEach((icon) => { - const iconName = [...icon.classList].find((c) => c.startsWith('icon-'))?.substring(5); - if (!iconName) return; - icon.dataset.name = iconName; - }); -} - -export default async function loadIcons(icons) { - const fedRoot = getFederatedContentRoot(); - const iconRequests = []; - const iconsToFetch = new Map(); - +export default async function loadIcons(icons, config) { + const iconSVGs = await fetchIcons(config); + if (!iconSVGs) return; icons.forEach(async (icon) => { - const isToolTip = icon.classList.contains('icon-tooltip'); - if (isToolTip) decorateToolTip(icon); - const iconName = icon.dataset.name; - if (icon.dataset.svgInjected || !iconName) return; - if (!federalIcons[iconName] && !iconsToFetch.has(iconName)) { - const url = `${fedRoot}/federal/assets/icons/svgs/${iconName}.svg`; - iconsToFetch.set(iconName, fetch(url) - .then(async (res) => { - if (!res.ok) throw new Error(`Failed to fetch SVG for ${iconName}: ${res.statusText}`); - const text = await res.text(); - const parser = new DOMParser(); - const svgDoc = parser.parseFromString(text, 'image/svg+xml'); - const svgElement = svgDoc.querySelector('svg'); - if (!svgElement) { - window.lana?.log(`No SVG element found in fetched content for ${iconName}`); - return; - } - const svgClone = svgElement.cloneNode(true); - svgClone.classList.add('icon-milo', `icon-milo-${iconName}`); - federalIcons[iconName] = svgClone; - }) - /* c8 ignore next 3 */ - .catch((error) => { - window.lana?.log(`Error fetching SVG for ${iconName}:`, error); - })); - } - iconRequests.push(iconsToFetch.get(iconName)); + const { classList } = icon; + if (classList.contains('icon-tooltip')) decorateToolTip(icon); + const iconName = icon.classList[1].replace('icon-', ''); + const existingIcon = icon.querySelector('svg'); + if (!iconSVGs[iconName] || existingIcon) return; const parent = icon.parentElement; - if (parent && parent.parentElement.tagName === 'LI') parent.parentElement.classList.add('icon-list-item'); - }); - - await Promise.all(iconRequests); - - icons.forEach((icon) => { - const iconName = icon.dataset.name; - if (iconName && federalIcons[iconName] && !icon.dataset.svgInjected) { - const svgClone = federalIcons[iconName].cloneNode(true); - icon.appendChild(svgClone); - icon.dataset.svgInjected = 'true'; + if (parent.childNodes.length > 1) { + if (parent.lastChild === icon) { + icon.classList.add('margin-inline-start'); + } else if (parent.firstChild === icon) { + icon.classList.add('margin-inline-end'); + if (parent.parentElement.tagName === 'LI') parent.parentElement.classList.add('icon-list-item'); + } else { + icon.classList.add('margin-inline-start', 'margin-inline-end'); + } } + icon.insertAdjacentHTML('afterbegin', iconSVGs[iconName].outerHTML); }); } diff --git a/libs/styles/styles.css b/libs/styles/styles.css index 66893a7c18e..839966a67e7 100644 --- a/libs/styles/styles.css +++ b/libs/styles/styles.css @@ -128,7 +128,6 @@ --icon-size-s: 32px; --icon-size-xs: 24px; --icon-size-xxs: 16px; - --icon-spacing: 8px; /* z-index */ --above-all: 9000; /* Used for page tools that overlay page content */ @@ -350,7 +349,6 @@ line-height: 20px; min-height: 21px; padding: 7px 18px 8px; - --icon-spacing: 12px; } .xl-button .con-button, @@ -360,7 +358,6 @@ line-height: 24px; min-height: 28px; padding: 10px 24px 8px; - --icon-spacing: 14px; } .xxl-button .con-button, @@ -370,7 +367,6 @@ line-height: 27px; min-height: 27px; padding: 14px 30px 15px; - --icon-spacing: 14px; } .con-button.button-justified { @@ -563,23 +559,19 @@ div[data-failed="true"]::before { color: var(--color-gray-300); } -span.icon { - width: 1em; - display: inline-block; - margin-inline: var(--icon-spacing); -} +span.icon.margin-right { margin-right: 8px; } -span.icon.node-index-first { margin-inline-start: unset; } -span.icon.node-index-middle { margin-inline: var(--icon-spacing); } -span.icon.node-index-last { margin-inline-end: unset; } -span.icon.node-index-only { margin-inline: unset; } +span.icon.margin-left { margin-left: 8px; } -span.icon svg { - height: 1em; - position: relative; - top: .1em; - width: auto; -} +span.icon.margin-inline-end { margin-inline-end: 8px; } + +span.icon.margin-inline-start { margin-inline-start: 8px; } + +.button-l .con-button span.icon.margin-left, +.con-button.button-l span.icon.margin-left { margin-left: 12px; } + +.button-xl .con-button span.icon.margin-left, +.con-button.button-xl span.icon.margin-left { margin-left: 14px; } /* Con Block Utils */ .con-block.xs-spacing { padding: var(--spacing-xs) 0; } diff --git a/libs/utils/utils.js b/libs/utils/utils.js index c0c6ddefbbd..3c9fe2e1945 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -789,6 +789,16 @@ function decorateHeader() { if (promo?.length) header.classList.add('has-promo'); } +async function decorateIcons(area, config) { + const icons = area.querySelectorAll('span.icon'); + if (icons.length === 0) return; + const { base } = config; + loadStyle(`${base}/features/icons/icons.css`); + loadLink(`${base}/img/icons/icons.svg`, { rel: 'preload', as: 'fetch', crossorigin: 'anonymous' }); + const { default: loadIcons } = await import('../features/icons/icons.js'); + await loadIcons(icons, config); +} + export async function customFetch({ resource, withCacheRules }) { const options = {}; if (withCacheRules) { @@ -1265,8 +1275,9 @@ function decorateDocumentExtras() { decorateHeader(); } -async function documentPostSectionLoading(area, config) { +async function documentPostSectionLoading(config) { decorateFooterPromo(); + const appendage = getMetadata('title-append'); if (appendage) { import('../features/title-append/title-append.js').then((module) => module.default(appendage)); @@ -1330,18 +1341,6 @@ async function resolveInlineFrags(section) { section.preloadLinks = newlyDecoratedSection.preloadLinks; } -export function setIconsIndexClass(icons) { - [...icons].forEach((icon) => { - const parent = icon.parentNode; - const children = parent.childNodes; - const nodeIndex = [...children].indexOf.call(children, icon); - let indexClass = (nodeIndex === children.length - 1) ? 'last' : 'middle'; - if (nodeIndex === 0) indexClass = 'first'; - if (children.length === 1) indexClass = 'only'; - icon.classList.add(`node-index-${indexClass}`); - }); -} - async function processSection(section, config, isDoc) { await resolveInlineFrags(section); const firstSection = section.el.dataset.idx === '0'; @@ -1349,6 +1348,7 @@ async function processSection(section, config, isDoc) { preloadBlockResources(section.preloadLinks); await Promise.all([ decoratePlaceholders(section.el, config), + decorateIcons(section.el, config), ]); const loadBlocks = [...stylePromises]; if (section.preloadLinks.length) { @@ -1381,11 +1381,6 @@ export async function loadArea(area = document) { decorateDocumentExtras(); } - const allIcons = area.querySelectorAll('span.icon'); - if (allIcons.length) { - setIconsIndexClass(allIcons); - } - const sections = decorateSections(area, isDoc); const areaBlocks = []; @@ -1398,21 +1393,13 @@ export async function loadArea(area = document) { }); } - if (allIcons.length) { - const { default: loadIcons, decorateIcons } = await import('../features/icons/icons.js'); - const areaIcons = area.querySelectorAll('span.icon'); - await decorateIcons(area, areaIcons, config); - await loadIcons(areaIcons); - } - const currentHash = window.location.hash; if (currentHash) { scrollToHashedElement(currentHash); } - if (isDoc) { - await documentPostSectionLoading(area, config); - } + if (isDoc) await documentPostSectionLoading(config); + await loadDeferred(area, areaBlocks, config); } diff --git a/test/features/icons/icons.test.js b/test/features/icons/icons.test.js index cb3cb994db1..cd355a0affe 100644 --- a/test/features/icons/icons.test.js +++ b/test/features/icons/icons.test.js @@ -1,56 +1,45 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; -import sinon, { stub } from 'sinon'; -import { waitForElement } from '../../helpers/waitfor.js'; +import { stub } from 'sinon'; +import { setConfig, getConfig, createTag } from '../../../libs/utils/utils.js'; -const { default: loadIcons, getIconData } = await import('../../../libs/features/icons/icons.js'); -const { setIconsIndexClass } = await import('../../../libs/utils/utils.js'); -const mockRes = ({ payload, status = 200, ok = true } = {}) => new Promise((resolve) => { - resolve({ - status, - ok, - json: () => payload, - text: () => payload, - }); -}); +const { default: loadIcons } = await import('../../../libs/features/icons/icons.js'); + +const codeRoot = '/libs'; +const conf = { codeRoot }; +setConfig(conf); +const config = getConfig(); document.body.innerHTML = await readFile({ path: './mocks/body.html' }); let icons; -const svgEx = ` - - -`; describe('Icon Suppprt', () => { - beforeEach(() => { - stub(window, 'fetch').callsFake(() => mockRes({})); - }); + let paramsGetStub; - afterEach(() => { - sinon.restore(); + before(() => { + paramsGetStub = stub(URLSearchParams.prototype, 'get'); + paramsGetStub.withArgs('cache').returns('off'); }); - it('Replaces span.icon', async () => { - const payload = svgEx; - window.fetch.returns(mockRes({ payload })); + after(() => { + paramsGetStub.restore(); + }); + before(async () => { icons = document.querySelectorAll('span.icon'); - icons.forEach((icon) => { - const { name } = getIconData(icon); - icon.dataset.name = name; - }); - await loadIcons(icons); + await loadIcons(icons, config); + await loadIcons(icons, config); // Test duplicate icon not created if run twice + }); - const selector = await waitForElement('span.icon svg'); - expect(selector).to.exist; + it('Fetches successfully with cache control enabled', async () => { + const otherIcons = [createTag('span', { class: 'icon icon-play' })]; + await loadIcons(otherIcons, config); }); - it('Sets icon index class', async () => { - icons = document.querySelectorAll('span.icon'); - setIconsIndexClass(icons); - const secondIconHasIndexClass = icons[2].classList.contains('node-index-last'); - expect(secondIconHasIndexClass).to.be.true; + it('Replaces span.icon', async () => { + const selector = icons[0].querySelector(':scope svg'); + expect(selector).to.exist; }); it('No duplicate icon', async () => { diff --git a/test/features/icons/mocks/body.html b/test/features/icons/mocks/body.html index 2d908a81cab..586013bf4f0 100644 --- a/test/features/icons/mocks/body.html +++ b/test/features/icons/mocks/body.html @@ -1,6 +1,4 @@ -
- -
+
From c56840dab6ab6ebb98780c981cce7eb413c9e327 Mon Sep 17 00:00:00 2001 From: "milo-pr-merge[bot]" <169241390+milo-pr-merge[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:01:11 +0100 Subject: [PATCH 02/17] [Release] Stage to Main (#3497) MWPW-165774 [Mobile-GNAV] page is not scrollable in live page (#3495) * check for new nav when disabling ios scroll * shortened the check from the previous commit Co-authored-by: Raghav Sharma <118168183+sharmrj@users.noreply.github.com> --- libs/blocks/global-navigation/utilities/utilities.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index 2ab1b87e295..137939d20e2 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -337,7 +337,8 @@ export function trigger({ element, event, type } = {}) { closeAllDropdowns({ type }); if (isOpen) return false; element.setAttribute('aria-expanded', 'true'); - if (!isDesktop.matches && type === 'dropdown') disableMobileScroll(); + if (!isDesktop.matches && type === 'dropdown' + && !!document.querySelector('header.new-nav')) disableMobileScroll(); return true; } From ec4bdff74d7ac49a5046db5dc88748a7e30476b7 Mon Sep 17 00:00:00 2001 From: "milo-pr-merge[bot]" <169241390+milo-pr-merge[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 12:13:47 +0100 Subject: [PATCH 03/17] [Release] Stage to Main (#3817) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ENB-7820: Add values for event95 (#3782) * Add values for event95 * Fix failing test case * Remove spaces * Minimal Impact: Add extra key for other features * Minimal Impact: change function name * [MWPW-168691] - Aside cta shrink JP (#3785) * [MWPW-168691] cta shrink fix * [MWPW-168691] update * [MWPW-168691] update * [MWPW-168691] update * [MWPW-168691] update * [MWPW-168691] update * [MWPW-168691] update * [MWPW-168691] update * [MWPW-168691] updated solution * Revert "[MWPW-168691]" This reverts commit a70f9ca77c4680a96348f1c69e175d7a46a00f05. Revert "[MWPW-168691] update" This reverts commit 6aac2bc7adf073958a1fb454b6417a749ed01ccf. Revert "[MWPW-168691] update" This reverts commit c0c6a3436ed9b8827e36d48c9dd389d301e14153. Revert "[MWPW-168691] update" This reverts commit 017edf08e23a8d6d38b48e3fd961a95d090681ec. Revert "[MWPW-168691] update" This reverts commit 219e5fd39b245a09d855aede9d3e5116bf69a66d. Revert "[MWPW-168691] update" This reverts commit a7df2b53c7e07dc7917ce2c3d8f8a9bb8a9b05b9. Revert "[MWPW-168691] update" This reverts commit 299a7d7eab0ba14508cab16c723a437b341bde18. Revert "[MWPW-168691] update" This reverts commit 67095158a5df1db00bea41dfd29ada59d16db966. Revert "[MWPW-168691] cta shrink fix" This reverts commit c90eea64042193a3ac97a7fbc363159069d9114f. * MWPW-168691: Prevent cta jp word wrap on large screens * MWPW-168691: Fix notification jp cta * MWPW-168691: Hide wbr cta for tablet and desktop --------- Co-authored-by: Ratko Zagorac * MWPW-169371 : Adding countryCode config to UNav (#3793) * Adding countryCode config to UNav * Updating countryCode for uk * Updating for upperCase * Passing countryCode according to MAS mapping for UCv3 cart --------- Co-authored-by: Snehal Sonawane Co-authored-by: Snehal Sonawane * MWPW-167716 Remove await from both gnav and georouting (#3680) Made georouting and globalnavigation non blocking * Lnav title and headline text overflow support (#3781) * Lnav title text overflow support * Fix for chevron icon top position * Adding white space break for localnav menus * Adding white space break for localnav menus * Adding white space break for localnav list menus * Inclufing white spaces to non menu items * [MWPW-169500] SEO- Links check failing breaks Preflight (#3803) fixed preflight seo links check * MWPW-169732 Fetch resources from adobe.com/federal instead of https://main--federal--adobecom.aem.live/federal (#3813) * on prod federal content for the standalone gnav and footer needs to come from * handled situations where we're stage and when we're neither stage nor prod * MWPW-169416 [MEP] stop treating remove action differently in preview (#3789) * truly remove elements, even in preview * unit test update * fix for MWPW-169416, new remove action in MEP * lint clean-up --------- Co-authored-by: John Pratt --------- Co-authored-by: Swati Mukherjee Co-authored-by: Dušan Kosanović Co-authored-by: Ratko Zagorac Co-authored-by: sonawanesnehal3 <152426902+sonawanesnehal3@users.noreply.github.com> Co-authored-by: Snehal Sonawane Co-authored-by: Snehal Sonawane Co-authored-by: Raghav Sharma <118168183+sharmrj@users.noreply.github.com> Co-authored-by: Bandana Laishram Co-authored-by: Robert Bogos <146744221+robert-bogos@users.noreply.github.com> Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Co-authored-by: John Pratt --- libs/blocks/aside/aside.css | 5 ++++ .../global-navigation/global-navigation.css | 8 +++++- .../global-navigation/global-navigation.js | 4 +++ .../global-navigation/utilities/menu/menu.css | 1 + libs/blocks/preflight/panels/seo.js | 3 +- .../personalization/personalization.js | 15 ++-------- libs/martech/helpers.js | 28 +++++++++++++++---- libs/navigation/navigation.js | 11 ++++++-- libs/utils/utils.js | 8 ++++-- .../personalization/mep-actions.test.js | 15 +++++----- .../merch-card-collection.test.js | 2 +- test/features/personalization/actions.test.js | 27 ------------------ 12 files changed, 68 insertions(+), 59 deletions(-) diff --git a/libs/blocks/aside/aside.css b/libs/blocks/aside/aside.css index 7f1491ae902..954e67c04bf 100644 --- a/libs/blocks/aside/aside.css +++ b/libs/blocks/aside/aside.css @@ -954,6 +954,11 @@ align-items: center; text-align: center; } + + .aside.promobar .action-area wbr, + .aside.notification .action-area wbr { + display: none; + } } @media (min-width: 600px) and (max-width: 1199px) { diff --git a/libs/blocks/global-navigation/global-navigation.css b/libs/blocks/global-navigation/global-navigation.css index dd5a2b6ebd7..cf4109810f8 100644 --- a/libs/blocks/global-navigation/global-navigation.css +++ b/libs/blocks/global-navigation/global-navigation.css @@ -1228,9 +1228,15 @@ header.new-nav .feds-breadcrumbs li:first-child:not(:nth-last-child(-n+3)):after background: var(--feds-background-popup); font-weight: 400; border-bottom: 0; - padding: 12px 38px; + padding: 12px 46px 12px 38px; outline-offset: -1px; color: var(--feds-color-link); + white-space: break-spaces; +} + +.feds-localnav .feds-localnav-items .feds-navLink-content .feds-navLink-title, +.feds-localnav .feds-localnav-items li .feds-navLink { + white-space: break-spaces; } .feds-localnav .feds-localnav-items .feds-navItem--centered { diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index 175e64b6abc..621a9f2a6ca 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -49,6 +49,8 @@ import { import { replaceKey, replaceKeyArray } from '../../features/placeholders.js'; +import { getMiloLocaleSettings } from '../merch/merch.js'; + const SIGNIN_CONTEXT = getConfig()?.signInContext; function getHelpChildren() { @@ -776,6 +778,7 @@ class Gnav { target: this.blocks.universalNav, env: environment, locale, + countryCode: getMiloLocaleSettings(getConfig().locale)?.country || 'US', imsClientId: window.adobeid?.client_id, theme: isDarkMode() ? 'dark' : 'light', analyticsContext: { @@ -1158,6 +1161,7 @@ class Gnav { elem?.addEventListener('click', (e) => { trigger({ element: e.currentTarget, event: e, type: 'headline' }); }); + elem.textContent = elem.textContent?.trim(); }); } }; diff --git a/libs/blocks/global-navigation/utilities/menu/menu.css b/libs/blocks/global-navigation/utilities/menu/menu.css index 630bf102408..3ca3d61a665 100644 --- a/libs/blocks/global-navigation/utilities/menu/menu.css +++ b/libs/blocks/global-navigation/utilities/menu/menu.css @@ -73,6 +73,7 @@ .feds-localnav-items .feds-menu-headline:after { right: 38px; + top: 20px; } [dir = "rtl"] .feds-menu-headline:after { diff --git a/libs/blocks/preflight/panels/seo.js b/libs/blocks/preflight/panels/seo.js index 67118905d31..f5c266b62e8 100644 --- a/libs/blocks/preflight/panels/seo.js +++ b/libs/blocks/preflight/panels/seo.js @@ -256,7 +256,8 @@ async function checkLinks() { badResults.push(...spidyResults); } - badLinks.value = badResults.map((result) => links.find((link) => compareResults(result, link))); + badLinks.value = badResults.map((result) => links.find((link) => compareResults(result, link))) + .filter(Boolean); // Format the results for display const count = badLinks.value.length; diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index 12aaaf0554d..9c1fdf40fce 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -220,13 +220,8 @@ export const createContent = (el, { content, manifestId, targetManifestId, actio }; const COMMANDS = { - [COMMANDS_KEYS.remove]: (el, { content, manifestId }) => { - if (content === 'false') return; - if (manifestId && !el.href?.includes('/tools/ost')) { - el.dataset.removedManifestId = manifestId; - return; - } - el.classList.add(CLASS_EL_DELETE); + [COMMANDS_KEYS.remove]: (el, { content }) => { + if (content !== 'false') el.classList.add(CLASS_EL_DELETE); }, [COMMANDS_KEYS.replace]: (el, cmd) => { if (!el || el.classList.contains(CLASS_EL_REPLACE)) return; @@ -1111,11 +1106,7 @@ export function handleFragmentCommand(command, a) { return fragment; } if (action === COMMANDS_KEYS.remove) { - if (manifestId) { - a.parentElement.dataset.removedManifestId = manifestId; - } else { - a.parentElement.remove(); - } + a.parentElement.remove(); } return false; } diff --git a/libs/martech/helpers.js b/libs/martech/helpers.js index 53333ee22eb..20c8a9ed55b 100644 --- a/libs/martech/helpers.js +++ b/libs/martech/helpers.js @@ -149,6 +149,22 @@ function getOrGenerateUserId() { }; } +function getUpdatedVisitAttempt() { + const { hostname } = window.location; + const secondVisitAttempt = Number(localStorage.getItem('secondHit')) || 0; + + const isAdobeDomain = hostname === 'www.adobe.com' || hostname === 'www.stage.adobe.com'; + const consentCookieValue = getCookie('OptanonConsent'); + + if (consentCookieValue?.includes('C0002:1') && isAdobeDomain) { + const updatedVisitAttempt = secondVisitAttempt === 0 ? 1 : secondVisitAttempt + 1; + localStorage.setItem('secondHit', updatedVisitAttempt); + return updatedVisitAttempt; + } + + return secondVisitAttempt; +} + function getPageNameForAnalytics({ locale }) { const { host, pathname } = new URL(window.location.href); const [modifiedPath] = pathname.split('/').filter((x) => x !== locale.prefix).join(':').split('.'); @@ -240,6 +256,7 @@ function createRequestPayload({ updatedContext, pageName, locale, env, hitType } approach: 'martech-API', edgeConfigIdLaunch: dataStreamId, edgeConfigId: dataStreamId, + personalisation: 'hybrid', }, }, }, @@ -269,6 +286,12 @@ function createRequestPayload({ updatedContext, pageName, locale, env, hitType } data.web = { webPageDetails }; data.eventType = hitTypeEventTypeMap[hitType]; + if (getUpdatedVisitAttempt() === 2) { + digitalData.adobe = { + libraryVersions: 'alloy-api', + experienceCloud: { secondVisits: 'setEvent' }, + }; + } xdm.implementationDetails = { name: 'https://ns.adobe.com/experience/alloy/reactor', version: '1.0', @@ -496,22 +519,17 @@ export const loadAnalyticsAndInteractionData = async ( const CURRENT_DATE = new Date(); const localTime = CURRENT_DATE.toISOString(); - const timezoneOffset = CURRENT_DATE.getTimezoneOffset(); if (hybridPersEnabled) { window.hybridPers = true; } const hitType = hybridPersEnabled ? 'pageView' : 'propositionFetch'; - const pageName = getPageNameForAnalytics({ locale }); - const updatedContext = getUpdatedContext({ ...getDeviceInfo(), localTime, timezoneOffset }); - const requestUrl = createRequestUrl({ env, hitType, }); - const requestPayload = { updatedContext, pageName, locale, env, hitType }; const requestBody = createRequestPayload(requestPayload); diff --git a/libs/navigation/navigation.js b/libs/navigation/navigation.js index 3d9eb800160..6acfac6eba8 100644 --- a/libs/navigation/navigation.js +++ b/libs/navigation/navigation.js @@ -112,6 +112,13 @@ export default async function loadBlock(configs, customLib) { import('../utils/utils.js'), ]); const paramConfigs = getParamsConfigs(configs); + const origin = (() => { + switch (env) { + case 'prod': return 'https://www.adobe.com'; + case 'stage': return 'https://www.stage.adobe.com'; + default: return 'https://main--federal--adobecom.aem.page'; + } + })(); const clientConfig = { theme, prodDomains, @@ -122,8 +129,8 @@ export default async function loadBlock(configs, customLib) { locales: configs.locales || locales, contentRoot: authoringPath || footer?.authoringPath, stageDomainsMap: getStageDomainsMap(stageDomainsMap), - origin: `https://main--federal--adobecom.aem.${env === 'prod' ? 'live' : 'page'}`, - allowedOrigins: [...allowedOrigins, `https://main--federal--adobecom.aem.${env === 'prod' ? 'live' : 'page'}`], + origin, + allowedOrigins: [...allowedOrigins, origin], onFooterReady: footer?.onReady, onFooterError: footer?.onError, ...paramConfigs, diff --git a/libs/utils/utils.js b/libs/utils/utils.js index e7edae37413..0bac53b77eb 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1257,13 +1257,15 @@ async function loadPostLCP(config) { const georouting = getMetadata('georouting') || config.geoRouting; if (georouting === 'on') { const jsonPromise = fetch(`${config.contentRoot ?? ''}/georoutingv2.json`); - const { default: loadGeoRouting } = await import('../features/georoutingv2/georoutingv2.js'); - await loadGeoRouting(config, createTag, getMetadata, loadBlock, loadStyle, jsonPromise); + import('../features/georoutingv2/georoutingv2.js') + .then(({ default: loadGeoRouting }) => { + loadGeoRouting(config, createTag, getMetadata, loadBlock, loadStyle, jsonPromise); + }); } const header = document.querySelector('header'); if (header) { header.classList.add('gnav-hide'); - await loadBlock(header); + loadBlock(header); header.classList.remove('gnav-hide'); } loadTemplate(); diff --git a/nala/features/personalization/mep-actions.test.js b/nala/features/personalization/mep-actions.test.js index cc257b867b9..edde208bcc9 100644 --- a/nala/features/personalization/mep-actions.test.js +++ b/nala/features/personalization/mep-actions.test.js @@ -7,6 +7,7 @@ import TextBlock from '../../blocks/text/text.page.js'; import MarqueeBlock from '../../blocks/marquee/marquee.page.js'; const miloLibs = process.env.MILO_LIBS || ''; +const sec5Loc = 'main:has-text("Section 5")'; // Test 0: confirm the default page test(`[Test Id - ${features[0].tcid}] ${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { @@ -17,6 +18,7 @@ test(`[Test Id - ${features[0].tcid}] ${features[0].name},${features[0].tags}`, await page.goto(URL); await expect(textBlock1.headlineAlt).toHaveText('Base page text. Section 2'); await expect(textBlock7.headlineAlt).toHaveText('Base page text fragment'); + await expect(page.locator(sec5Loc)).toHaveCount(1); }); // Test 1: confirm various MEP actions on the personalized page @@ -33,7 +35,6 @@ test(`[Test Id - ${features[1].tcid}] ${features[1].name},${features[1].tags}`, const textBlock10 = new TextBlock(page, 10); const textBlock11 = new TextBlock(page, 11); const textBlock12 = new TextBlock(page, 12); - const textBlock13 = new TextBlock(page, 13); const URL = `${baseURL}${features[1].path}${miloLibs}`; console.info(`[Test Page]: ${URL}`); await page.goto(URL); @@ -44,12 +45,12 @@ test(`[Test Id - ${features[1].tcid}] ${features[1].name},${features[1].tags}`, await expect(textBlock5.headlineAlt).toHaveText('Appended to 3'); await expect(textBlock6.headlineAlt).toHaveText('Prepended to 4'); await expect(textBlock7.headlineAlt).toHaveText('Base page text. Section 4'); - await expect(textBlock8.headlineAlt).not.toBeVisible(); - await expect(textBlock9.headlineAlt).toHaveText('Section 6 replacement'); - await expect(textBlock10.headlineAlt).toHaveText('Base page text. Section 7'); - await expect(textBlock11.headlineAlt).toHaveText('Replaced basepage fragment'); - await expect(textBlock12.headlineAlt).toHaveText('Inserted after basepage fragment'); - await expect(textBlock13.headlineAlt).toHaveText('Inserted after replaced fragment'); + await expect(page.locator(sec5Loc)).toHaveCount(0); + await expect(textBlock8.headlineAlt).toHaveText('Section 6 replacement'); + await expect(textBlock9.headlineAlt).toHaveText('Base page text. Section 7'); + await expect(textBlock10.headlineAlt).toHaveText('Replaced basepage fragment'); + await expect(textBlock11.headlineAlt).toHaveText('Inserted after basepage fragment'); + await expect(textBlock12.headlineAlt).toHaveText('Inserted after replaced fragment'); }); // Test 2: confirm insertScript (make text orange) diff --git a/test/blocks/merch-card-collection/merch-card-collection.test.js b/test/blocks/merch-card-collection/merch-card-collection.test.js index e70d4513fc0..dae60aafe00 100644 --- a/test/blocks/merch-card-collection/merch-card-collection.test.js +++ b/test/blocks/merch-card-collection/merch-card-collection.test.js @@ -235,7 +235,7 @@ describe('Merch Cards', async () => { const merchCards = await init(el); expect(merchCards.filter).to.equal('all'); await delay(500); - expect(merchCards.querySelectorAll('h3[data-removed-manifest-id]').length).to.equal(4); + expect(merchCards.querySelectorAll('h3').length).to.equal(0); }); it('should localize the query-index url', async () => { diff --git a/test/features/personalization/actions.test.js b/test/features/personalization/actions.test.js index 9c4110ba5df..0508c10d8e6 100644 --- a/test/features/personalization/actions.test.js +++ b/test/features/personalization/actions.test.js @@ -244,33 +244,6 @@ describe('remove action', () => { await initFragments(removeMeFrag); expect(document.querySelector('a[href="/fragments/removeme"]')).to.be.null; }); - - it('removeContent should tag but not remove content in preview', async () => { - document.body.innerHTML = await readFile({ path: './mocks/personalization.html' }); - - let manifestJson = await readFile({ path: './mocks/actions/manifestRemove.json' }); - manifestJson = JSON.parse(manifestJson); - setFetchResponse(manifestJson); - delete config.mep; - - expect(document.querySelector('.z-pattern')).to.not.be.null; - await init({ - mepParam: '', - mepHighlight: false, - mepButton: false, - pzn: '/path/to/manifest.json', - promo: false, - target: false, - }); - - expect(document.querySelector('.z-pattern')).to.not.be.null; - expect(document.querySelector('.z-pattern').dataset.removedManifestId).to.equal('manifest.json'); - - const removeMeFrag = document.querySelector('a[href="/fragments/removeme"]'); - await initFragments(removeMeFrag); - expect(document.querySelector('a[href="/fragments/removeme"]')).to.not.be.null; - expect(document.querySelector('a[href="/fragments/removeme"]').dataset.removedManifestId).to.not.be.null; - }); }); describe('useBlockCode action', async () => { From c4840835cb16f9bea341afa6f88d3dcc0ed1e0d8 Mon Sep 17 00:00:00 2001 From: Okan Sahin <39759830+mokimo@users.noreply.github.com> Date: Sat, 15 Mar 2025 07:44:59 +0100 Subject: [PATCH 04/17] MWPW-167191 Removed an unnecessary css url() that was causing a console error (#3824) MWPW-167191 Removed an unnecessary css url() that was causing a console error (#3630) * updated an svg inside a url() in the globalnav css to have data:image/svg+xml;utf8 inorder to prevent a console error * changed a ; to a , * Removed the svg as it was unnecessary Co-authored-by: Raghav Sharma <118168183+sharmrj@users.noreply.github.com> --- libs/blocks/global-navigation/global-navigation.css | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/blocks/global-navigation/global-navigation.css b/libs/blocks/global-navigation/global-navigation.css index cf4109810f8..eaf5503dd48 100644 --- a/libs/blocks/global-navigation/global-navigation.css +++ b/libs/blocks/global-navigation/global-navigation.css @@ -859,7 +859,6 @@ header.new-nav .feds-nav > section.feds-navItem > button.feds-navLink { } header.new-nav .feds-nav > section.feds-navItem > button.feds-navLink::after { - content: url(''); width: 9.271px; height: 9.179px; transform: rotate(-45deg); From 01b4dab6e6bad808c8702fae06545d7514e9b1dd Mon Sep 17 00:00:00 2001 From: Chris Peyer Date: Wed, 14 May 2025 17:03:11 -0400 Subject: [PATCH 05/17] Reduce gnav lana sampleRate to 0.01% (#4150) --- libs/blocks/global-navigation/utilities/utilities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index dc38ec0ffad..42e46752cd7 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -95,7 +95,7 @@ export const logPerformance = ( .join(','); window.lana.log(measureStr, { clientId: LANA_CLIENT_ID, - sampleRate: 50, + sampleRate: 0.01, }); } catch (e) { console.error(e); From 025a1e989b512490d1876031aa52dfa8280dbb50 Mon Sep 17 00:00:00 2001 From: Okan Sahin <39759830+mokimo@users.noreply.github.com> Date: Tue, 20 May 2025 10:30:20 +0200 Subject: [PATCH 06/17] Revert "Improve carousel accessibility (#4103)" from prod (#4195) Revert "Improve carousel accessibility (#4103)" This reverts commit 34d2016b56d19a2e496dc216e9c7c3e8f4ef06a2. --- libs/blocks/carousel/carousel.css | 11 ----- libs/blocks/carousel/carousel.js | 61 +++++++++------------------ nala/blocks/carousel/carousel.page.js | 4 +- nala/blocks/carousel/carousel.test.js | 12 +++--- 4 files changed, 27 insertions(+), 61 deletions(-) diff --git a/libs/blocks/carousel/carousel.css b/libs/blocks/carousel/carousel.css index 5814302650d..bc5acf9bdf1 100644 --- a/libs/blocks/carousel/carousel.css +++ b/libs/blocks/carousel/carousel.css @@ -193,10 +193,6 @@ html[dir="rtl"] .carousel-slides .section.carousel-slide { opacity: 1; } -.carousel-slide.non-visible-slide { - visibility: hidden; -} - .carousel-slides.is-ready, .carousel-slides.is-ready.is-reversing, .carousel[class*='show-'] .carousel-slides.is-ready, @@ -726,10 +722,3 @@ html[dir="rtl"] .carousel .move-indicators { transform: translateX(calc(round(down, (var(--offset) / (var(--slides) - 1)), 2px) * -1)); } } - -.carousel .aria-live-container { - position: absolute; - width: 0; - height: 0; - overflow: hidden; -} diff --git a/libs/blocks/carousel/carousel.js b/libs/blocks/carousel/carousel.js index b39315b4c12..ea15efac496 100644 --- a/libs/blocks/carousel/carousel.js +++ b/libs/blocks/carousel/carousel.js @@ -67,7 +67,7 @@ function decorateLightboxButtons() { return [expandBtn, closeBtn]; } -function decorateSlideIndicators(slides) { +function decorateSlideIndicators(slides, jumpTo) { const indicatorDots = []; for (let i = 0; i < slides.length; i += 1) { @@ -76,16 +76,17 @@ function decorateSlideIndicators(slides) { 'data-index': i, }); - li.setAttribute('role', 'tab'); - li.setAttribute('tabindex', -1); - li.setAttribute('aria-selected', false); - li.setAttribute('aria-label', `Slide ${i + 1} of ${slides.length}`); + if (jumpTo) { + li.setAttribute('role', 'tab'); + li.setAttribute('tabindex', -1); + li.setAttribute('aria-selected', false); + li.setAttribute('aria-labelledby', `Viewing Slide ${i + 1}`); + } // Set inital active state if (i === 0) { li.classList.add('active'); - li.setAttribute('aria-current', 'location'); - li.setAttribute('tabindex', 0); + if (jumpTo) li.setAttribute('tabindex', 0); } indicatorDots.push(li); } @@ -178,13 +179,6 @@ function setIndicatorMultiplyer(carouselElements, activeSlideIndicator, event) { } } -function updateAriaLive(ariaLive, slide) { - slide.querySelectorAll(':scope > :not(.section-metadata')?.forEach((el) => { - ariaLive.textContent += el.textContent; - }); -} - -let hiddenSlideTimeout; function moveSlides(event, carouselElements, jumpToIndex) { const { slideContainer, @@ -193,12 +187,9 @@ function moveSlides(event, carouselElements, jumpToIndex) { slideIndicators, controlsContainer, direction, - ariaLive, + jumpTo, } = carouselElements; - clearTimeout(hiddenSlideTimeout); - ariaLive.textContent = ''; - let referenceSlide = slideContainer.querySelector('.reference-slide'); let activeSlide = slideContainer.querySelector('.active'); let activeSlideIndicator = controlsContainer.querySelector('.active'); @@ -219,8 +210,7 @@ function moveSlides(event, carouselElements, jumpToIndex) { activeSlide.classList.remove('active'); activeSlide.querySelectorAll('a, video').forEach((focusableElement) => focusableElement.setAttribute('tabindex', -1)); activeSlideIndicator.classList.remove('active'); - activeSlideIndicator.setAttribute('tabindex', -1); - activeSlideIndicator.removeAttribute('aria-current'); + if (jumpTo) activeSlideIndicator.setAttribute('tabindex', -1); /* * If indicator dot buttons are clicked update: @@ -267,10 +257,7 @@ function moveSlides(event, carouselElements, jumpToIndex) { referenceSlide.classList.add('reference-slide'); referenceSlide.style.order = '1'; - updateAriaLive(ariaLive, activeSlide); - // Update active slide and indicator dot attributes - activeSlide.classList.remove('non-visible-slide'); activeSlide.classList.add('active'); const indexOfActive = [...activeSlide.parentElement.children] .findIndex((ele) => activeSlide.isSameNode(ele)); @@ -291,8 +278,7 @@ function moveSlides(event, carouselElements, jumpToIndex) { .forEach((focusableElement) => { focusableElement.setAttribute('tabindex', 0); }); } activeSlideIndicator.classList.add('active'); - activeSlideIndicator.setAttribute('tabindex', 0); - activeSlideIndicator.setAttribute('aria-current', 'location'); + if (jumpTo) activeSlideIndicator.setAttribute('tabindex', 0); setIndicatorMultiplyer(carouselElements, activeSlideIndicator, event); // Loop over all slide siblings to update their order @@ -309,12 +295,7 @@ function moveSlides(event, carouselElements, jumpToIndex) { */ const slideDelay = 25; slideContainer.classList.remove('is-ready'); - hiddenSlideTimeout = setTimeout(() => { - slides[activeSlideIndex].classList.add('non-visible-slide'); - }, 625); - return setTimeout(() => { - slideContainer.classList.add('is-ready'); - }, slideDelay); + return setTimeout(() => slideContainer.classList.add('is-ready'), slideDelay); } export function getSwipeDistance(start, end) { @@ -433,7 +414,7 @@ export default function init(el) { const slides = [...candidateKeys].reduce((rdx, key) => { if (key.textContent === 'carousel' && key.nextElementSibling.textContent === carouselName) { const slide = key.closest('.section'); - slide.classList.add('carousel-slide', 'non-visible-slide'); + slide.classList.add('carousel-slide'); rdx.push(slide); slide.setAttribute('data-index', rdx.indexOf(slide)); } @@ -444,17 +425,12 @@ export default function init(el) { const fragment = new DocumentFragment(); const nextPreviousBtns = decorateNextPreviousBtns(); const nextPreviousContainer = createTag('div', { class: 'carousel-button-container' }); - const slideIndicators = decorateSlideIndicators(slides); + const slideIndicators = decorateSlideIndicators(slides, jumpTo); const controlsContainer = createTag('div', { class: 'carousel-controls is-delayed' }); convertMpcMp4(slides); fragment.append(...slides); const slideWrapper = createTag('div', { class: 'carousel-wrapper' }); - const ariaLive = createTag('div', { - class: 'aria-live-container', - 'aria-live': 'polite', - }); - slideWrapper.appendChild(ariaLive); const slideContainer = createTag('div', { class: 'carousel-slides' }, fragment); const carouselElements = { el, @@ -465,7 +441,6 @@ export default function init(el) { controlsContainer, direction: undefined, jumpTo, - ariaLive, }; if (el.classList.contains('lightbox')) { @@ -487,8 +462,11 @@ export default function init(el) { el.append(slideWrapper); const dotsUl = createTag('ul', { class: 'carousel-indicators' }); - dotsUl.setAttribute('role', 'tablist'); - dotsUl.setAttribute('tabindex', 0); + if (jumpTo) { + dotsUl.setAttribute('role', 'tablist'); + dotsUl.setAttribute('tabindex', 0); + } + dotsUl.append(...slideIndicators); controlsContainer.append(dotsUl); nextPreviousContainer.append(...nextPreviousBtns, controlsContainer); @@ -503,7 +481,6 @@ export default function init(el) { } parentArea.addEventListener(MILO_EVENTS.DEFERRED, handleDeferredImages, true); - slides[0].classList.remove('non-visible-slide'); slides[0].classList.add('active'); const IndexOfShowClass = [...el.classList].findIndex((ele) => ele.includes('show-')); let NoOfVisibleSlides = 1; diff --git a/nala/blocks/carousel/carousel.page.js b/nala/blocks/carousel/carousel.page.js index 654bc18e45f..bd7a0e29616 100644 --- a/nala/blocks/carousel/carousel.page.js +++ b/nala/blocks/carousel/carousel.page.js @@ -150,14 +150,14 @@ export default class Carousel { * @param {string} tagOrClass - The tag name or class name of the element containing the text. * @returns {Promise} The text content of the specified carousel slide. */ - async getSlideText(index, tagOrClass, elVisibility = 'visible') { + async getSlideText(index, tagOrClass) { let slideSelector = `.carousel-slide:nth-child(${index}) `; if (tagOrClass.startsWith('.')) { slideSelector += tagOrClass; } else { slideSelector += `${tagOrClass}`; } - await this.page.waitForSelector(slideSelector, { state: elVisibility }); + await this.page.waitForSelector(slideSelector); const slide = await this.page.$(slideSelector); const text = await slide.textContent(); return text; diff --git a/nala/blocks/carousel/carousel.test.js b/nala/blocks/carousel/carousel.test.js index fbed94cec07..93fdf25e95a 100644 --- a/nala/blocks/carousel/carousel.test.js +++ b/nala/blocks/carousel/carousel.test.js @@ -32,7 +32,7 @@ test.describe('Milo Carousel Block test suite', () => { // verify carousel indictor and active indicator expect(await carousel.areIndicatorsDisplayed()).toBeTruthy(); expect(await carousel.getNumberOfIndicators()).toBe(4); - expect(await carousel.getCurrentIndicatorIndex()).toBe('0'); + expect(await carousel.getCurrentIndicatorIndex()).toBeNull(); // verify carousel next and previous buttons expect(await carousel.isNextButtonlVisible()).toBeTruthy(); @@ -43,12 +43,12 @@ test.describe('Milo Carousel Block test suite', () => { // move to next slide by clicking next button and verify h2 tag header await carousel.moveToNextSlide(); expect(await carousel.getCurrentSlideIndex()).toBe('1'); - expect(await carousel.getSlideText(1, 'h2', 'hidden')).toBeTruthy(); + expect(await carousel.getSlideText(1, 'h2', 'Orange Slices')).toBeTruthy(); // move to 3rd slide by clicking indicator and verify h2 tag header await carousel.moveToIndicator(3); - expect(await carousel.getCurrentIndicatorIndex()).toBe('0'); - expect(await carousel.getSlideText(3, 'h2', 'hidden')).toBeTruthy(); + expect(await carousel.getCurrentIndicatorIndex()).toBeNull(); + expect(await carousel.getSlideText(3, 'h2', 'Apples')).toBeTruthy(); }); await test.step('step-4: Verify the accessibility test on the carousel block', async () => { @@ -76,7 +76,7 @@ test.describe('Milo Carousel Block test suite', () => { // verify indicator visibility, count and index of active slide expect(await carousel.areIndicatorsDisplayed()).toBeTruthy(); expect(await carousel.getNumberOfIndicators()).toBe(4); - expect(await carousel.getCurrentIndicatorIndex()).toBe('0'); + expect(await carousel.getCurrentIndicatorIndex()).toBeNull(); expect(await carousel.isNextButtonlVisible()).toBeTruthy(); expect(await carousel.isPreviousButtonlVisible()).toBeTruthy(); @@ -119,7 +119,7 @@ test.describe('Milo Carousel Block test suite', () => { await test.step('step-3: Perform carousel slides and controls operation and verify contents', async () => { // move to next slide by clicking next button and verify h2 tag header await carousel.moveToNextSlide(); - expect(await carousel.getSlideText(1, 'h2')).toBeTruthy(); + expect(await carousel.getSlideText(1, 'h2', 'Melon')).toBeTruthy(); }); await test.step('step-3: Verify the accessibility test on the carousel show-2 container block', async () => { From 9549475381e66a90a9da1c2eb9fbfc379e4f97b0 Mon Sep 17 00:00:00 2001 From: Okan Sahin <39759830+mokimo@users.noreply.github.com> Date: Fri, 23 May 2025 10:31:47 +0200 Subject: [PATCH 07/17] Revert "[MWPW-173470] - Korea free trial restrict" (main) (#4236) Revert "[MWPW-173470] - Korea free trial restrict (#4185)" This reverts commit 351cfe6d66e95e351b7b2137121aa8087fd3f2f5. --- libs/blocks/global-navigation/utilities/utilities.js | 8 +------- libs/blocks/merch/merch.js | 6 ------ libs/utils/decorate.js | 6 +----- libs/utils/utils.js | 12 ------------ 4 files changed, 2 insertions(+), 30 deletions(-) diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index 34016ecb102..64e12513a47 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -10,7 +10,6 @@ import { getFederatedContentRoot, getFederatedUrl, getFedsPlaceholderConfig, - shouldBlockFreeTrialLinks, } from '../../../utils/utils.js'; import { processTrackingLabels } from '../../../martech/attributes.js'; import { replaceKey, replaceText } from '../../../features/placeholders.js'; @@ -102,7 +101,7 @@ export const logPerformance = ( sampleRate: 0.01, }); } catch (e) { - // eslint-disable-next-line no-empty + console.error(e); } }; @@ -247,11 +246,6 @@ export async function loadDecorateMenu() { } export function decorateCta({ elem, type = 'primaryCta', index } = {}) { - if (shouldBlockFreeTrialLinks({ - button: elem, - localePrefix: getConfig()?.locale?.prefix, - parent: elem.parentElement, - })) return null; const modifier = type === 'secondaryCta' ? 'secondary' : 'primary'; const clone = elem.cloneNode(true); diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index 42c94d6dae9..fbf8c1d8816 100644 --- a/libs/blocks/merch/merch.js +++ b/libs/blocks/merch/merch.js @@ -844,12 +844,6 @@ export async function buildCta(el, params) { cta.setAttribute('aria-label', ariaLabel); }); } - - // @see https://jira.corp.adobe.com/browse/MWPW-173470 - cta.onceSettled().then(() => { - if (getConfig()?.locale?.prefix === '/kr' && cta.value[0]?.offerType === OFFER_TYPE_TRIAL) cta.remove(); - }); - return cta; } diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 3fbd83f993f..6210cd0c2ea 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -5,7 +5,6 @@ import { createIntersectionObserver, getFederatedContentRoot, getFedsPlaceholderConfig, - shouldBlockFreeTrialLinks, } from './utils.js'; const { miloLibs, codeRoot } = getConfig(); @@ -24,12 +23,9 @@ export function decorateButtons(el, size) { const buttons = el.querySelectorAll('em a, strong a, p > a strong'); if (buttons.length === 0) return; const buttonTypeMap = { STRONG: 'blue', EM: 'outline', A: 'blue' }; - const localePrefix = getConfig()?.locale?.prefix; - buttons.forEach((button) => { - const parent = button.parentElement; - if (shouldBlockFreeTrialLinks({ button, localePrefix, parent })) return; let target = button; + const parent = button.parentElement; const buttonType = buttonTypeMap[parent.nodeName] || 'outline'; if (button.nodeName === 'STRONG') { target = parent; diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 8b91193918c..99b55448273 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -415,18 +415,6 @@ export const getFedsPlaceholderConfig = ({ useCache = true } = {}) => { return fedsPlaceholderConfig; }; -export const shouldBlockFreeTrialLinks = ({ button, localePrefix, parent }) => { - if (localePrefix !== '/kr' || (!button.dataset?.modalPath?.includes('/kr/cc-shared/fragments/trial-modals') - && !['free-trial', 'free trial', '무료 체험판', '무료 체험하기', '{{try-for-free}}'] - .some((pattern) => button.textContent?.toLowerCase()?.includes(pattern.toLowerCase())))) { - return false; - } - - const elementToRemove = (parent?.tagName === 'STRONG' || parent?.tagName === 'EM') && parent?.children?.length === 1 ? parent : button; - elementToRemove.remove(); - return true; -}; - export function isInTextNode(node) { return (node.parentElement.childNodes.length > 1 && node.parentElement.firstChild.tagName === 'A') || node.parentElement.firstChild.nodeType === Node.TEXT_NODE; } From 4fb87b7c41a9d8a74e02997601f948e450219741 Mon Sep 17 00:00:00 2001 From: Angelo Statescu Date: Fri, 23 May 2025 13:40:02 +0300 Subject: [PATCH 08/17] MWPW-173888 + MWPW-173919: Appending extra options for relative urls & multiple cta reopen fix (#4237) --- libs/blocks/merch/merch.js | 12 ++++++++++-- test/blocks/merch/merch.test.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index fbf8c1d8816..8bdf76e754d 100644 --- a/libs/blocks/merch/merch.js +++ b/libs/blocks/merch/merch.js @@ -521,7 +521,8 @@ export function appendExtraOptions(url, extraOptions) { const extraOptionsObj = JSON.parse(extraOptions); let urlWithExtraOptions; try { - urlWithExtraOptions = new URL(url); + const fullUrl = url.startsWith('/') ? `${window.location.origin}${url}` : url; + urlWithExtraOptions = new URL(fullUrl); } catch (err) { window.lana?.log(`Invalid URL ${url} : ${err}`); return url; @@ -539,7 +540,7 @@ export function appendExtraOptions(url, extraOptions) { } async function openExternalModal(url, getModal, extraOptions) { - await loadStyle(`${getConfig().base}/blocks/iframe/iframe.css`); + loadStyle(`${getConfig().base}/blocks/iframe/iframe.css`); const root = createTag('div', { class: 'milo-iframe' }); const urlWithExtraOptions = appendExtraOptions(url, extraOptions); const urlWithTabName = appendTabName(urlWithExtraOptions); @@ -796,12 +797,19 @@ 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'; diff --git a/test/blocks/merch/merch.test.js b/test/blocks/merch/merch.test.js index 19e4f03a2f7..76ddf72a06f 100644 --- a/test/blocks/merch/merch.test.js +++ b/test/blocks/merch/merch.test.js @@ -25,6 +25,7 @@ import merch, { appendExtraOptions, getMiloLocaleSettings, reopenModal, + resetReopenStatus, setCtaHash, openModal, PRICE_TEMPLATE_LEGAL, @@ -494,6 +495,29 @@ describe('Merch Block', () => { reopenModal(cta); expect(clickSpy.called).to.be.true; window.location.hash = prevHash; + resetReopenStatus(); + }); + + it('only reopens one modal if multiples hashes match', async () => { + const prevHash = window.location.hash; + window.location.hash = '#try-photoshop'; + + const cta1 = document.createElement('a'); + cta1.setAttribute('data-modal-id', 'try-photoshop'); + const clickSpy1 = sinon.spy(cta1, 'click'); + + const cta2 = document.createElement('a'); + cta1.setAttribute('data-modal-id', 'try-photoshop'); + const clickSpy2 = sinon.spy(cta2, 'click'); + + reopenModal(cta1); + reopenModal(cta2); + + expect(clickSpy1.called).to.be.true; + expect(clickSpy2.called).to.be.false; + + window.location.hash = prevHash; + resetReopenStatus(); }); }); @@ -861,6 +885,12 @@ describe('Merch Block', () => { const resultUrl2 = appendExtraOptions(invalidUrl); expect(resultUrl2).to.equal(invalidUrl); }); + + it('appends extra options if the provided url is relative', () => { + const relativeUrl = '/plans-fragments/modals/individual/modals-content-rich/all-apps/master.modal.html'; + const resultUrl = appendExtraOptions(relativeUrl, JSON.stringify({ promoid: 'test' })); + expect(resultUrl).to.include('?promoid=test'); + }); }); describe('locale settings', () => { From fa991168a7402e89be072bb8951e636d0fa55614 Mon Sep 17 00:00:00 2001 From: Blaine Gunn Date: Fri, 23 May 2025 09:14:03 -0600 Subject: [PATCH 09/17] MWPW-173875 direct to main --- libs/utils/logWebVitals.js | 24 ++++++++++++++---------- test/utils/logWebVitals.test.js | 2 +- test/utils/logWebVitalsUtils.test.js | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/libs/utils/logWebVitals.js b/libs/utils/logWebVitals.js index 6c74f7e8940..a236d59b6ee 100644 --- a/libs/utils/logWebVitals.js +++ b/libs/utils/logWebVitals.js @@ -8,7 +8,7 @@ function sendToLana(lanaData) { Object.assign(lanaData, { chromeVer: ua.match(/Chrome\/(\d+\.\d+\.\d+\.\d+)/)?.[1] || '', - country: sessionStorage.getItem('akamai') || '', + country: sessionStorage.getItem('akamai') || sessionStorage.getItem('feds_location') || '', // eslint-disable-next-line compat/compat downlink: window.navigator?.connection?.downlink || '', loggedIn: window.adobeIMS?.isSignedInUser() || false, @@ -19,7 +19,7 @@ function sendToLana(lanaData) { || (ua.match(/Linux/) && 'linux') || '', tablet: (ua.match(/(ipad|iPad|tablet|(android(?!.*mobile))|(windows(?!.*phone).*touch))/) && 'yes') - || '', + || 'no', windowHeight: window.innerHeight, windowWidth: window.innerWidth, url: `${window.location.host}${window.location.pathname}`, @@ -88,15 +88,19 @@ function observeLCP(lanaData, delay, mep) { const lastEntry = entries[entries.length - 1]; // Use the latest LCP candidate lanaData.lcp = parseInt(lastEntry.startTime, 10); const lcpEl = lastEntry.element; - lanaData.lcpElType = lcpEl?.nodeName?.toLowerCase() || 'Element Was Replaced'; - lanaData.lcpEl = getElementInfo(lcpEl); - lanaData.lcpSectionOne = boolStr(sectionOne.contains(lcpEl)); - const closestFrag = lcpEl.closest('.fragment'); - lanaData.isFrag = boolStr(closestFrag); - if (closestFrag) { - lanaData.isMep = boolStr(isFragmentFromMep(closestFrag.dataset.path, mep)); + lanaData.lcpElType = lcpEl?.nodeName?.toLowerCase(); + if (lanaData.lcpElType) { + lanaData.lcpEl = getElementInfo(lcpEl); + lanaData.lcpSectionOne = boolStr(sectionOne.contains(lcpEl)); + const closestFrag = lcpEl.closest('.fragment'); + lanaData.isFrag = boolStr(closestFrag); + if (closestFrag) { + lanaData.isMep = boolStr(isFragmentFromMep(closestFrag.dataset.path, mep)); + } else { + lanaData.isMep = 'false'; + } } else { - lanaData.isMep = 'false'; + lanaData.lcpElType = 'Element Was Replaced'; } setTimeout(() => { diff --git a/test/utils/logWebVitals.test.js b/test/utils/logWebVitals.test.js index 5fc8c42deb7..9bed313aee3 100644 --- a/test/utils/logWebVitals.test.js +++ b/test/utils/logWebVitals.test.js @@ -34,7 +34,7 @@ describe('Log Web Vitals', () => { expect(vitals.manifest4path).to.equal('/cc-shared/fragments/tests/2024/q2/ace0875/ace0875.json'); expect(vitals.manifest4selected).to.equal('target-var-marqueelink'); expect(vitals.os).to.be.oneOf(['mac', 'iOS', 'win', 'android', 'linux', '']); - expect(vitals.tablet).to.be.oneOf(['yes', '']); + expect(vitals.tablet).to.be.oneOf(['yes', 'no']); expect(vitals.url).to.equal('localhost:2000/'); expect(vitals.isMep).to.equal('false'); expect(vitals.isFrag).to.equal('false'); diff --git a/test/utils/logWebVitalsUtils.test.js b/test/utils/logWebVitalsUtils.test.js index 31948a54852..9e74690b4d0 100644 --- a/test/utils/logWebVitalsUtils.test.js +++ b/test/utils/logWebVitalsUtils.test.js @@ -50,7 +50,7 @@ describe('Log Web Vitals Utils', () => { expect(vitals.loggedIn).to.equal('false'); expect(vitals.os).to.be.oneOf(['mac', 'iOS', 'win', 'android', 'linux', '']); - expect(vitals.tablet).to.be.oneOf(['yes', '']); + expect(vitals.tablet).to.be.oneOf(['yes', 'no']); expect(vitals.url).to.equal('localhost:2000/'); expect(parseInt(vitals.windowHeight, 10)).to.be.greaterThan(200); expect(parseInt(vitals.windowWidth, 10)).to.be.greaterThan(200); From 3735184a12139f638e7bc848113bd236e742a535 Mon Sep 17 00:00:00 2001 From: Rares Munteanu Date: Mon, 2 Jun 2025 12:37:31 +0200 Subject: [PATCH 10/17] Korea free trial restrict (#4282) * [MWPW-173016] - block korea free trial links/buttons * [MWPW-173016] - korea text filter added * [MWPW-173016] - update if * [MWPW-173016] - code optimization * [MWPW-173016] - improve import * [MWPW-173016] - modal check, string check added * [MWPW-173016] - strings added * [MWPW-173470] - update logic * [MWPW-173470] - update logic * [MWPW-173470] - update logic * [MWPW-173470] - remove whitespace * [MWPW-173470] - optimize * [MWPW-173470] - remove variable * [MWPW-173016] - optimize code * [MWPW-173016] - group logic * [MWPW-173016] - merch solution added * [MWPW-173016] - merch ref comment added * [MWPW-173470] - null safe * [MWPW-173470] - code optimization * [MWPW-173470] - nav korea restrict coverage * [MWPW-173470] - fix eslint console error --------- Co-authored-by: Dusan Kosanovic --- libs/blocks/global-navigation/utilities/utilities.js | 8 +++++++- libs/blocks/merch/merch.js | 6 ++++++ libs/utils/decorate.js | 6 +++++- libs/utils/utils.js | 12 ++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index 4c6e919d08a..a7b752cf72c 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -10,6 +10,7 @@ import { getFederatedContentRoot, getFederatedUrl, getFedsPlaceholderConfig, + shouldBlockFreeTrialLinks, } from '../../../utils/utils.js'; import { processTrackingLabels } from '../../../martech/attributes.js'; import { replaceKey, replaceText } from '../../../features/placeholders.js'; @@ -102,7 +103,7 @@ export const logPerformance = ( sampleRate: 0.01, }); } catch (e) { - console.error(e); + // eslint-disable-next-line no-empty } }; @@ -247,6 +248,11 @@ export async function loadDecorateMenu() { } export function decorateCta({ elem, type = 'primaryCta', index } = {}) { + if (shouldBlockFreeTrialLinks({ + button: elem, + localePrefix: getConfig()?.locale?.prefix, + parent: elem.parentElement, + })) return null; const modifier = type === 'secondaryCta' ? 'secondary' : 'primary'; const clone = elem.cloneNode(true); diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index 8bdf76e754d..f11a9325845 100644 --- a/libs/blocks/merch/merch.js +++ b/libs/blocks/merch/merch.js @@ -852,6 +852,12 @@ export async function buildCta(el, params) { cta.setAttribute('aria-label', ariaLabel); }); } + + // @see https://jira.corp.adobe.com/browse/MWPW-173470 + cta.onceSettled().then(() => { + if (getConfig()?.locale?.prefix === '/kr' && cta.value[0]?.offerType === OFFER_TYPE_TRIAL) cta.remove(); + }); + return cta; } diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 6210cd0c2ea..3fbd83f993f 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -5,6 +5,7 @@ import { createIntersectionObserver, getFederatedContentRoot, getFedsPlaceholderConfig, + shouldBlockFreeTrialLinks, } from './utils.js'; const { miloLibs, codeRoot } = getConfig(); @@ -23,9 +24,12 @@ export function decorateButtons(el, size) { const buttons = el.querySelectorAll('em a, strong a, p > a strong'); if (buttons.length === 0) return; const buttonTypeMap = { STRONG: 'blue', EM: 'outline', A: 'blue' }; + const localePrefix = getConfig()?.locale?.prefix; + buttons.forEach((button) => { - let target = button; const parent = button.parentElement; + if (shouldBlockFreeTrialLinks({ button, localePrefix, parent })) return; + let target = button; const buttonType = buttonTypeMap[parent.nodeName] || 'outline'; if (button.nodeName === 'STRONG') { target = parent; diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 99b55448273..8b91193918c 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -415,6 +415,18 @@ export const getFedsPlaceholderConfig = ({ useCache = true } = {}) => { return fedsPlaceholderConfig; }; +export const shouldBlockFreeTrialLinks = ({ button, localePrefix, parent }) => { + if (localePrefix !== '/kr' || (!button.dataset?.modalPath?.includes('/kr/cc-shared/fragments/trial-modals') + && !['free-trial', 'free trial', '무료 체험판', '무료 체험하기', '{{try-for-free}}'] + .some((pattern) => button.textContent?.toLowerCase()?.includes(pattern.toLowerCase())))) { + return false; + } + + const elementToRemove = (parent?.tagName === 'STRONG' || parent?.tagName === 'EM') && parent?.children?.length === 1 ? parent : button; + elementToRemove.remove(); + return true; +}; + export function isInTextNode(node) { return (node.parentElement.childNodes.length > 1 && node.parentElement.firstChild.tagName === 'A') || node.parentElement.firstChild.nodeType === Node.TEXT_NODE; } From 7c3650fdbf0d51259953ceec081812bb4772ce65 Mon Sep 17 00:00:00 2001 From: Narcis Radu Date: Wed, 4 Jun 2025 10:33:12 +0300 Subject: [PATCH 11/17] Revert "[MWPW-173717] Hyphenate large headings on mobile (#4199)" This reverts commit 7b8282100a4ae343406bf2a07e7efae7d9abe7eb. --- libs/styles/styles.css | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libs/styles/styles.css b/libs/styles/styles.css index b1a8fc98421..ec9310ef201 100644 --- a/libs/styles/styles.css +++ b/libs/styles/styles.css @@ -930,12 +930,7 @@ a.static:active { } /* mobile only */ -@media (max-width: 599px) { - :root:not(:lang(ja-JP), :lang(ja)) :is(.heading-xxxl, .heading-xxl) { - hyphens: auto; - hyphenate-limit-chars: 15 5 5; - } - +@media (max-width: 600px) { .con-button.button-justified-mobile { display: block; text-align: center; From 3c04f4b37a3196867c94708a8a3977104a85960a Mon Sep 17 00:00:00 2001 From: Raghav Sharma <118168183+sharmrj@users.noreply.github.com> Date: Wed, 4 Jun 2025 18:43:19 +0530 Subject: [PATCH 12/17] Update utilities.js (#4312) Making a change to the utilities file Co-authored-by: Saloni Jain <6162294+salonijain3@users.noreply.github.com> --- libs/blocks/global-navigation/utilities/utilities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index 0eda63e2a28..ef300dfd1f3 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -20,7 +20,7 @@ loadLana(); const FEDERAL_PATH_KEY = 'federal'; // Set a default height for LocalNav, -// as sticky blocks position themselves before LocalNav loads into the DOM. +// as sticky blocks position themselves before LocalNav loads into the document object model(DOM). const DEFAULT_LOCALNAV_HEIGHT = 40; const LANA_CLIENT_ID = 'feds-milo'; From d053a22faebb4e710a1c66da257f3bb2e27a0f6a Mon Sep 17 00:00:00 2001 From: Bandana Laishram Date: Mon, 16 Jun 2025 16:00:56 +0530 Subject: [PATCH 13/17] Revert "Adding mutation observer to support viewport change" (#4408) Revert "Adding mutation observer to support viewport change (#4388)" This reverts commit 8af16b44377b23505d533e79c6d65430c7a705c0. --- libs/blocks/global-navigation/global-navigation.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index fd309b1e5f3..ddd7e74f28a 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -1172,12 +1172,12 @@ class Gnav { if (this.elements.aside.clientHeight > fedsPromoWrapper.clientHeight) { lanaLog({ message: 'Promo height is more than expected, potential CLS', tags: 'gnav-promo', errorType: 'i' }); + updateLayout(); + + this.promoResizeObserver?.disconnect(); + this.promoResizeObserver = new ResizeObserver(updateLayout); + this.promoResizeObserver.observe(this.elements.aside); } - this.promoResizeObserver?.disconnect(); - this.promoResizeObserver = new ResizeObserver(updateLayout); - this.promoResizeObserver.observe(this.elements.aside); - updateLayout(); - isDesktop.addEventListener('change', updateLayout); performance.mark('Gnav-Aside-End'); logPerformance('Gnav-Aside-Time', 'Gnav-Aside-Start', 'Gnav-Aside-End'); return this.elements.aside; From 679c8e6c060ff920f8a07db8ceda29b3f41b36d1 Mon Sep 17 00:00:00 2001 From: Ryan Clayton Date: Mon, 16 Jun 2025 13:54:21 -0600 Subject: [PATCH 14/17] Revert "[Fire-Drill] This will have to get reverted by ryan (#4368)" This reverts commit fdbaea6ac2d17a0342fa1a6a655d0b3bd8cc73ee. --- test/blocks/fire-drill-ryan/README.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test/blocks/fire-drill-ryan/README.md diff --git a/test/blocks/fire-drill-ryan/README.md b/test/blocks/fire-drill-ryan/README.md deleted file mode 100644 index 9afbd5c1cd9..00000000000 --- a/test/blocks/fire-drill-ryan/README.md +++ /dev/null @@ -1 +0,0 @@ -Let's revert this after this is on production. \ No newline at end of file From 961a7b7b6da3a0506266d0967246145cb12ef740 Mon Sep 17 00:00:00 2001 From: Narcis Radu Date: Thu, 26 Jun 2025 15:14:27 +0300 Subject: [PATCH 15/17] Revert "MWPW-174987: Change DOM order of elements based on viewport (#4438)" This reverts commit 9d02d5391a7aa2c26c46724840c9e47a4b7c73b1. --- libs/blocks/hero-marquee/hero-marquee.css | 12 ++++ libs/blocks/hero-marquee/hero-marquee.js | 48 ---------------- test/blocks/hero-marquee/hero-marquee.test.js | 24 -------- test/blocks/hero-marquee/mocks/body.html | 56 ------------------- 4 files changed, 12 insertions(+), 128 deletions(-) diff --git a/libs/blocks/hero-marquee/hero-marquee.css b/libs/blocks/hero-marquee/hero-marquee.css index d4e84e061b1..d9095227b35 100644 --- a/libs/blocks/hero-marquee/hero-marquee.css +++ b/libs/blocks/hero-marquee/hero-marquee.css @@ -379,6 +379,12 @@ html[dir="rtl"] .hero-marquee li.icon-item span.icon { /* min height */ .hero-marquee.s-min-height-tablet { min-height: var(--s-min-height);} .hero-marquee.l-min-height-tablet { min-height: var(--l-min-height);} + + /* helper classes */ + .hero-marquee .order-0-tablet { order: 0; } + .hero-marquee .order-1-tablet { order: 1; } + .hero-marquee .order-2-tablet { order: 2; } + .hero-marquee .order-3-tablet { order: 3; } } @media screen and (min-width: 920px) { @@ -468,4 +474,10 @@ html[dir="rtl"] .hero-marquee li.icon-item span.icon { /* min height */ .hero-marquee.s-min-height-desktop { min-height: var(--s-min-height);} .hero-marquee.l-min-height-desktop { min-height: var(--l-min-height);} + + /* helper classes */ + .hero-marquee .order-0-desktop { order: 0; } + .hero-marquee .order-1-desktop { order: 1; } + .hero-marquee .order-2-desktop { order: 2; } + .hero-marquee .order-3-desktop { order: 3; } } diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index 1664f0cc7c2..f747559e297 100644 --- a/libs/blocks/hero-marquee/hero-marquee.js +++ b/libs/blocks/hero-marquee/hero-marquee.js @@ -166,53 +166,6 @@ function loadBreakpointThemes() { loadStyle(`${base}/styles/breakpoint-theme.css`); } -export function getViewportOrder(viewport, content, previousViewportOrder) { - const orderEls = [...content.querySelectorAll(':scope > div[class*="order-"]')]; - const nonOrderEls = [...content.querySelectorAll(':scope > div:not([class*="order-"])')]; - const viewportOrder = Array(orderEls.length).fill(null); - orderEls.forEach((el) => { - let order; - el.classList.forEach((className) => { - if (!className.startsWith('order-') || !className.endsWith(viewport)) return; - order = parseInt(className.split('-')[1], 10); - }); - if (Number.isInteger(order)) viewportOrder[order] = el; - }); - const nonEmpty = viewportOrder.every((el) => el); - return nonEmpty ? [...nonOrderEls, ...viewportOrder] : previousViewportOrder; -} - -function handleViewportOrder(content) { - const hasOrder = content.querySelector(':scope > div[class*="order-"]'); - if (!hasOrder) return; - - const mobileOrder = [...content.children]; - const tabletOrder = getViewportOrder('tablet', content, mobileOrder); - const viewports = { - mobile: { - media: '(max-width: 599px)', - elements: mobileOrder, - }, - tablet: { - media: '(min-width: 600px) and (max-width: 1199px)', - elements: tabletOrder, - }, - desktop: { - media: '(min-width: 1200px)', - elements: getViewportOrder('desktop', content, tabletOrder), - }, - }; - - Object.entries(viewports).forEach(([viewport, { media, elements }]) => { - const mediaQuery = window.matchMedia(media); - if (mediaQuery.matches && viewport !== 'mobile') content.replaceChildren(...elements); - mediaQuery.addEventListener('change', (e) => { - if (!e.matches) return; - content.replaceChildren(...elements); - }); - }); -} - export default async function init(el) { el.classList.add('con-block'); let rows = el.querySelectorAll(':scope > div'); @@ -315,7 +268,6 @@ export default async function init(el) { } }); decorateTextOverrides(el, ['-heading', '-body', '-detail'], mainCopy); - handleViewportOrder(copy); if (el.classList.contains('countdown-timer')) { promiseArr.push(loadCDT(copy, el.classList)); diff --git a/test/blocks/hero-marquee/hero-marquee.test.js b/test/blocks/hero-marquee/hero-marquee.test.js index b664f172b7d..063f9b7631a 100644 --- a/test/blocks/hero-marquee/hero-marquee.test.js +++ b/test/blocks/hero-marquee/hero-marquee.test.js @@ -3,7 +3,6 @@ import { expect } from '@esm-bundle/chai'; import { stub } from 'sinon'; import { waitForElement } from '../../helpers/waitfor.js'; import { setConfig } from '../../../libs/utils/utils.js'; -import { getViewportOrder } from '../../../libs/blocks/hero-marquee/hero-marquee.js'; window.lana = { log: stub() }; @@ -36,27 +35,4 @@ describe('Hero Marquee', () => { const hr = await waitForElement('.has-divider'); expect(hr).to.exist; }); - it('sorts con-block elements based on order class and viewport', async () => { - const orderMarquee = document.querySelector('#hero-order'); - const orderCopy = orderMarquee.querySelector('.copy'); - const mobileOrder = [...orderCopy.children]; - const tabletOrder = getViewportOrder('tablet', orderCopy, mobileOrder); - const desktopOrder = getViewportOrder('desktop', orderCopy, tabletOrder); - expect(tabletOrder[0].classList.contains('main-copy')).to.be.true; - expect(desktopOrder[0].classList.contains('main-copy')).to.be.true; - tabletOrder.splice(0, 1); - desktopOrder.splice(0, 1); - // eslint-disable-next-line no-plusplus - for (let i = 0; i < tabletOrder.length; i++) { - expect(tabletOrder[i].classList.contains(`order-${i}-tablet`)).to.be.true; - expect(desktopOrder[i].classList.contains(`order-${i}-desktop`)).to.be.true; - } - }); - it('order of con-blocks is the same as mobile if there is no order class', async () => { - const nonOrderMarquee = document.querySelector('#hero-all'); - const nonOrderCopy = nonOrderMarquee.querySelector('.copy'); - const mobileOrder = [...nonOrderCopy.children]; - const tabletOrder = getViewportOrder('tablet', nonOrderCopy, mobileOrder); - for (const [index, el] of tabletOrder.entries()) expect(el === mobileOrder[index]).to.be.true; - }); }); diff --git a/test/blocks/hero-marquee/mocks/body.html b/test/blocks/hero-marquee/mocks/body.html index abdaf013d73..013471e69b1 100644 --- a/test/blocks/hero-marquee/mocks/body.html +++ b/test/blocks/hero-marquee/mocks/body.html @@ -121,59 +121,3 @@

Hero w/ Adobe.tv link

https://video.tv.adobe.com/v/3427744
- -
-
-
- - - -
-
- - - -
-
- - - -
-
-
-
con-block-row-lockup (xl-icon-size, order-3-tablet, order-2-desktop)
-
XL Icon Size
-
-
-
con-block-row-lockup (xl-lockup, order-1-tablet, order-3-desktop)
-
XL Icon Size
-
-
-
-

After Effects

-

DETAIL TEXT

-

This Hero has all row types

-

lockup, list, qrcode, text, background

-

See more Other options you say?

-
-
-
-
con-block-row-list (max-width-6-tablet, order-0-tablet, order-1-desktop)
-
-
    -
  • Small
  • -
  • Medium length text
  • -
  • Long length text that may break onto a new line, what will happen, keep it going so this is even longer and really wraps?
  • -
  • Another list
  • -
-
-
-
-
con-block-row-qrcode(order-2-tablet, order-0-desktop)
-
-

https://main--milo--adobecom.hlx.page/drafts/rparrish/assets/qr-adobe-com.svg

-

Google play

-

Apple store

-
-
-
From 872fa645b34363698d44d4f881d1b85b624c3204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilyas=20T=C3=BCrkben?= Date: Thu, 26 Jun 2025 23:40:15 +0200 Subject: [PATCH 16/17] MWPW-175654: preload ucv3 script (#4477) * MWPW-175654: preload ucv3 script ucv3 script doesn't support modern preload and requires an id attribute on the script element therefore we need to use loadScript instead of loadLink * add defer * use window.milo.deferredPromise --- libs/blocks/merch/merch.js | 10 +++++++--- libs/utils/utils.js | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index 999b6f12a32..e19d7045844 100644 --- a/libs/blocks/merch/merch.js +++ b/libs/blocks/merch/merch.js @@ -1,6 +1,6 @@ import { createTag, getConfig, loadArea, loadScript, loadStyle, localizeLink, SLD, getMetadata, - loadLink, shouldAllowKrTrial, + shouldAllowKrTrial, } from '../../utils/utils.js'; import { replaceKey } from '../../features/placeholders.js'; @@ -316,7 +316,7 @@ export function getMasBase(hostname, maslibs) { function getCommercePreloadUrl() { const { env } = getConfig(); - if (env === 'prod') { + if (env.name === 'prod') { return 'https://commerce.adobe.com/store/iframe/preload.js'; } return 'https://commerce-stg.adobe.com/store/iframe/preload.js'; @@ -622,7 +622,11 @@ export async function getModalAction(offers, options, el) { // The script can preload more, based on clientId, but for the ones in use // ('mini-plans', 'creative') there is no difference, so we can just use either one. const client = 'creative'; - loadLink(`${baseUrl}?cli=${client}`, 'text/javascript', { id: 'ucv3-preload-script', as: 'script', crossorigin: 'anonymous', rel: 'preload' }); + window.milo.deferredPromise.then(() => { + setTimeout(() => { + loadScript(`${baseUrl}?cli=${client}`, 'text/javascript', { mode: 'defer', id: 'ucv3-preload-script' }); + }, 1000); + }); } const [{ diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 51afaa30ae9..471f9c26d0f 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -650,12 +650,13 @@ export function appendHtmlToLink(link) { } } -export const loadScript = (url, type, { mode } = {}) => new Promise((resolve, reject) => { +export const loadScript = (url, type, { mode, id } = {}) => new Promise((resolve, reject) => { let script = document.querySelector(`head > script[src="${url}"]`); if (!script) { const { head } = document; script = document.createElement('script'); script.setAttribute('src', url); + if (id) script.setAttribute('id', id); if (type) { script.setAttribute('type', type); } From 2c83023130e8ed9e06d4f6de8cf7a07e1b7dea55 Mon Sep 17 00:00:00 2001 From: Okan Sahin Date: Fri, 27 Jun 2025 11:27:50 +0200 Subject: [PATCH 17/17] fix rebase mistakes --- libs/blocks/hero-marquee/hero-marquee.css | 12 ------------ libs/blocks/hero-marquee/hero-marquee.js | 1 + 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/libs/blocks/hero-marquee/hero-marquee.css b/libs/blocks/hero-marquee/hero-marquee.css index d9095227b35..d4e84e061b1 100644 --- a/libs/blocks/hero-marquee/hero-marquee.css +++ b/libs/blocks/hero-marquee/hero-marquee.css @@ -379,12 +379,6 @@ html[dir="rtl"] .hero-marquee li.icon-item span.icon { /* min height */ .hero-marquee.s-min-height-tablet { min-height: var(--s-min-height);} .hero-marquee.l-min-height-tablet { min-height: var(--l-min-height);} - - /* helper classes */ - .hero-marquee .order-0-tablet { order: 0; } - .hero-marquee .order-1-tablet { order: 1; } - .hero-marquee .order-2-tablet { order: 2; } - .hero-marquee .order-3-tablet { order: 3; } } @media screen and (min-width: 920px) { @@ -474,10 +468,4 @@ html[dir="rtl"] .hero-marquee li.icon-item span.icon { /* min height */ .hero-marquee.s-min-height-desktop { min-height: var(--s-min-height);} .hero-marquee.l-min-height-desktop { min-height: var(--l-min-height);} - - /* helper classes */ - .hero-marquee .order-0-desktop { order: 0; } - .hero-marquee .order-1-desktop { order: 1; } - .hero-marquee .order-2-desktop { order: 2; } - .hero-marquee .order-3-desktop { order: 3; } } diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index 7494a9c9c11..1cac8cf4277 100644 --- a/libs/blocks/hero-marquee/hero-marquee.js +++ b/libs/blocks/hero-marquee/hero-marquee.js @@ -329,6 +329,7 @@ export default async function init(el) { }); decorateTextOverrides(el, ['-heading', '-body', '-detail'], mainCopy); handleViewportOrder(copy); + if (el.classList.contains('countdown-timer')) { promiseArr.push(loadCDT(copy, el.classList)); }