Skip to content
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

[Release] Stage to Main #3764

Merged
merged 9 commits into from
Mar 4, 2025
Merged
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
Diff view
2 changes: 1 addition & 1 deletion libs/blocks/adobetv/adobetv.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createTag } from '../../utils/utils.js';

export default function init(a) {
a.classList.add('hide-video');
const bgBlocks = ['aside', 'marquee', 'hero-marquee', 'long-from'];
const bgBlocks = ['aside', 'marquee', 'hero-marquee', 'long-form'];
if (a.href.includes('.mp4') && bgBlocks.some((b) => a.closest(`.${b}`))) {
a.classList.add('hide');
if (!a.parentNode) return;
Expand Down
62 changes: 11 additions & 51 deletions libs/blocks/figure/figure.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { applyHoverPlay, decorateAnchorVideo, applyAccessibilityEvents, decoratePausePlayWrapper, isVideoAccessible } from '../../utils/decorate.js';
import { createTag } from '../../utils/utils.js';

function buildCaption(pEl) {
Expand All @@ -8,76 +7,37 @@ function buildCaption(pEl) {
return figCaptionEl;
}

function htmlToElement(html) {
const template = document.createElement('template');
const convertHtml = html.trim();
template.innerHTML = convertHtml;
return template.content.firstChild;
}

function decorateVideo(clone, figEl) {
const videoTag = clone.querySelector('video');
const anchorTag = clone.querySelector('a[href*=".mp4"]');
if (anchorTag && !anchorTag.hash) anchorTag.hash = '#autoplay';
if (anchorTag) decorateAnchorVideo({ src: anchorTag.href, anchorTag });
function decorateVideo(videoEl, figEl) {
const videoTag = videoEl.querySelector('video');
if (videoTag) {
videoTag.removeAttribute('data-mouseevent');
if (videoTag.dataset?.videoSource) {
videoTag.appendChild(
createTag('source', {
src: videoTag.dataset?.videoSource,
type: 'video/mp4',
}),
);
}
applyHoverPlay(videoTag);
if (!videoTag.controls && isVideoAccessible(anchorTag)) {
applyAccessibilityEvents(videoTag);
decoratePausePlayWrapper(videoTag, 'autoplay');
}
if (videoTag.controls) {
const io = new IntersectionObserver((entries) => {
entries.forEach(({ isIntersecting, target }) => {
if (!isIntersecting && !target.paused) target.pause();
});
}, { rootMargin: '0px' });
io.observe(videoTag);
}
figEl.prepend(clone.querySelector('.video-container, .pause-play-wrapper, video'));
figEl.prepend(videoEl.querySelector('.video-container, .pause-play-wrapper, video'));
}
}

export function buildFigure(blockEl) {
const figEl = document.createElement('figure');
figEl.classList.add('figure');
const figEl = createTag('figure', { class: 'figure' });
Array.from(blockEl.children).forEach((child) => {
const clone = child.cloneNode(true);
// picture, video, or embed link is NOT wrapped in P tag
const tags = ['PICTURE', 'VIDEO', 'A'];
if (tags.includes(clone.nodeName) || (clone.nodeName === 'SPAN' && clone.classList.contains('modal-img-link'))) {
if (clone.href?.includes('.mp4')) {
const videoPlaceholderLink = `<p>${clone.outerHTML}</p>`;
const videoLink = htmlToElement(videoPlaceholderLink);
decorateVideo(videoLink, figEl);
}
figEl.prepend(clone);
if (tags.includes(child.nodeName) || (child.nodeName === 'SPAN' && child.classList.contains('modal-img-link'))) {
figEl.prepend(child);
} else {
// content wrapped in P tag(s)
const imageVideo = clone.querySelector('.modal-img-link');
const imageVideo = child.querySelector('.modal-img-link');
if (imageVideo) {
figEl.prepend(imageVideo);
}
const picture = clone.querySelector('picture');
const picture = child.querySelector('picture');
if (picture) {
figEl.prepend(picture);
}
decorateVideo(clone, figEl);
const caption = clone.querySelector('em');
decorateVideo(child, figEl);
const caption = child.querySelector('em');
if (caption) {
const figElCaption = buildCaption(caption);
figEl.append(figElCaption);
}
const link = clone.querySelector('a');
const link = child.querySelector('a');
if (link) {
const img = figEl.querySelector('picture') || figEl.querySelector('video');
if (img && !link.classList.contains('pause-play-wrapper')) {
Expand Down
30 changes: 25 additions & 5 deletions libs/blocks/global-navigation/global-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,11 @@ const convertToPascalCase = (str) => str
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
.join(' ');

const removeLocalNav = () => {
lanaLog({ message: 'Gnav Localnav was removed, potential CLS', tags: 'gnav-localnav' });
document.querySelector('.feds-localnav')?.remove();
};

class Gnav {
constructor({ content, block, newMobileNav } = {}) {
this.content = content;
Expand Down Expand Up @@ -428,9 +433,12 @@ class Gnav {
};

decorateLocalNav = async () => {
if (!this.isLocalNav()) return;
if (!this.isLocalNav()) {
removeLocalNav();
return;
}
const localNavItems = this.elements.navWrapper.querySelector('.feds-nav').querySelectorAll('.feds-navItem:not(.feds-navItem--section, .feds-navItem--mobile-only)');
const firstElem = localNavItems[0]?.querySelector('a');
const firstElem = localNavItems[0]?.querySelector('a') || localNavItems[0]?.querySelector('button');
if (!firstElem) {
lanaLog({ message: 'GNAV: Incorrect authoring of localnav found.', tags: 'gnav', errorType: 'info' });
return;
Expand All @@ -454,7 +462,11 @@ class Gnav {

localNavItems.forEach((elem, idx) => {
const clonedItem = elem.cloneNode(true);
const link = clonedItem.querySelector('a');
const link = clonedItem.querySelector('a, button');

if (link) {
link.dataset.title = link.textContent;
}

if (idx === 0) {
localNav.querySelector('.feds-localnav-title').innerText = title.trim();
Expand All @@ -477,7 +489,7 @@ class Gnav {
const promo = document.querySelector('.feds-promo-aside-wrapper');
if (promo) localNav.classList.add('has-promo');
this.elements.localNav = localNav;
localNavItems[0].querySelector('a').textContent = title.trim();
firstElem.textContent = title.trim();
const isAtTop = () => {
const rect = this.elements.localNav.getBoundingClientRect();
// note: ios safari changes between -0.34375, 0, and 0.328125
Expand Down Expand Up @@ -1115,10 +1127,18 @@ class Gnav {
// Copying dropdown contents to localNav items
const decorateLocalNavItems = (navItem, template) => {
const elements = [...document.querySelectorAll('.feds-localnav .feds-navItem')].find(
(el) => el.textContent.trim() === navItem.textContent,
(el) => {
const link = el.querySelector('a, button');
return link.dataset.title?.trim() === navItem.textContent;
},
);
if (elements) {
const dropdownBtn = elements.querySelector('button');
elements.innerHTML = template.innerHTML;
// To override the textcontent of button of first item of localnav
if (dropdownBtn) {
elements.querySelector('button').textContent = dropdownBtn.textContent;
}
// Reattach click events & mutation observers, as cloned elem don't retain event listeners
elements.querySelector('.feds-localnav-items button')?.addEventListener('click', (e) => {
trigger({ element: e.currentTarget, event: e, type: 'localNavItem' });
Expand Down
105 changes: 105 additions & 0 deletions libs/blocks/marketo/marketo-multi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { createTag } from '../../utils/utils.js';
import { debounce } from '../../utils/action.js';

const VALIDATION_STEP = {
name: '2',
phone: '2',
mktoFormsJobTitle: '2',
mktoFormsFunctionalArea: '2',
company: '3',
state: '3',
postcode: '3',
mktoFormsPrimaryProductInterest: '3',
mktoFormsCompanyType: '3',
};

function updateStepDetails(formEl, step, totalSteps) {
formEl.classList.add('hide-errors');
formEl.classList.remove('show-warnings');
formEl.dataset.step = step;
formEl.querySelector('.step-details .step').textContent = `Step ${step} of ${totalSteps}`;
formEl.querySelector('#mktoButton_new')?.classList.toggle('mktoHidden', step !== totalSteps);
formEl.querySelector('#mktoButton_next')?.classList.toggle('mktoHidden', step === totalSteps);
setTimeout(() => {
formEl.querySelector(`.mktoFormRowTop[data-validate="${step}"]:not(.mktoHidden) input`)?.focus();
}, 100);
}

function showPreviousStep(formEl, totalSteps) {
const currentStep = parseInt(formEl.dataset.step, 10);
const previousStep = currentStep - 1;
const backBtn = formEl.querySelector('.back-btn');

updateStepDetails(formEl, previousStep, totalSteps);
if (previousStep === 1) backBtn?.remove();
}

const showNextStep = (formEl, currentStep, totalSteps) => {
if (currentStep === totalSteps) return;
const nextStep = currentStep + 1;
const stepDetails = formEl.querySelector('.step-details');

if (!stepDetails.querySelector('.back-btn')) {
const backBtn = createTag('button', { class: 'back-btn', type: 'button' }, 'Back');
backBtn.addEventListener('click', () => showPreviousStep(formEl, totalSteps));
stepDetails.prepend(backBtn);
}

updateStepDetails(formEl, nextStep, totalSteps);
};

export const formValidate = (formEl) => {
const currentStep = parseInt(formEl.dataset.step, 10) || 1;

if (formEl.querySelector(`.mktoFormRowTop[data-validate="${currentStep}"] .mktoInvalid`)) {
return false;
}

const totalSteps = formEl.closest('.marketo').classList.contains('multi-3') ? 3 : 2;
showNextStep(formEl, currentStep, totalSteps);

return currentStep === totalSteps;
};

function setValidationSteps(formEl, totalSteps) {
formEl.querySelectorAll('.mktoFormRowTop').forEach((row) => {
const rowAttr = row.getAttribute('data-mktofield') || row.getAttribute('data-mkto_vis_src');
const step = VALIDATION_STEP[rowAttr] ? Math.min(VALIDATION_STEP[rowAttr], totalSteps) : 1;
row.dataset.validate = rowAttr?.startsWith('adobe-privacy') ? totalSteps : step;
});
}

function onRender(formEl, totalSteps) {
const currentStep = parseInt(formEl.dataset.step, 10);
const submitButton = formEl.querySelector('#mktoButton_new');
submitButton?.classList.toggle('mktoHidden', currentStep !== totalSteps);
formEl.querySelector('.step-details .step').textContent = `Step ${currentStep} of ${totalSteps}`;
setValidationSteps(formEl, totalSteps);
}

const readyForm = (form, totalSteps) => {
const formEl = form.getFormElem().get(0);
form.onValidate(() => formValidate(formEl));

const nextButton = createTag('button', { type: 'button', id: 'mktoButton_next', class: 'mktoButton mktoUpdatedBTN mktoVisible' }, 'Next');
nextButton.addEventListener('click', () => form.validate());
const nextContainer = createTag('div', { class: 'mktoButtonRow' }, nextButton);
const stepEl = createTag('p', { class: 'step' }, `Step 1 of ${totalSteps}`);
const stepDetails = createTag('div', { class: 'step-details' }, stepEl);
formEl.append(nextContainer, stepDetails);

const debouncedOnRender = debounce(() => onRender(formEl, totalSteps), 10);
const observer = new MutationObserver(debouncedOnRender);
observer.observe(formEl, { childList: true, subtree: true });
debouncedOnRender();
};

export default (el) => {
if (!el.classList.contains('multi-step')) return;
const formEl = el.querySelector('form');
const totalSteps = el.classList.contains('multi-3') ? 3 : 2;
formEl.dataset.step = 1;

const { MktoForms2 } = window;
MktoForms2.whenReady((form) => { readyForm(form, totalSteps); });
};
74 changes: 71 additions & 3 deletions libs/blocks/marketo/marketo.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
--marketo-form-focus: #147AF3;
--marketo-form-placeholder-height: calc(78px * 7 + 57px); /* 7 rows + submit */
--marketo-form-placeholder-height-desktop: calc(78px * 4 + 57px); /* 4 rows + submit */
--marketo-form-placeholder-height-multi: calc(78px * 2 + 57px); /* 2 rows + submit */
--marketo-form-placeholder-height-multi-desktop: calc(78px + 57px); /* 1 row + submit */
--marketo-form-min-height: 215px;
--marketo-form-max-height: 10000px;
}
Expand All @@ -25,6 +27,10 @@
min-height: var(--marketo-form-placeholder-height);
}

.marketo.marketo.multi-step.loading form {
min-height: var(--marketo-form-placeholder-height-multi);
}

.marketo .marketo-title {
font-size: 28px;
font-weight: bold;
Expand Down Expand Up @@ -87,6 +93,50 @@
display: contents;
}

.marketo.multi-step .mktoFormRow.mktoFormRowTop[data-validate="2"],
.marketo.multi-step .mktoFormRow.mktoFormRowTop[data-validate="3"] {
display: none;
}

.marketo.multi-step .mktoForm[data-step="2"] .mktoFormRow.mktoFormRowTop[data-validate="2"],
.marketo.multi-step .mktoForm[data-step="3"] .mktoFormRow.mktoFormRowTop[data-validate="3"] {
display: contents;
}

.marketo.multi-step .mktoForm[data-step="2"] .mktoFormRow.mktoFormRowTop[data-validate="1"],
.marketo.multi-step .mktoForm[data-step="3"] .mktoFormRow.mktoFormRowTop[data-validate="1"] {
display: none;
}

.marketo.multi-step .mktoForm[data-step="1"] .mktoFormRow.mktoFormRowTop.adobe-privacy,
.marketo.multi-step .mktoForm[data-step="2"] .mktoFormRow.mktoFormRowTop.adobe-privacy {
display: none;
}

.marketo.multi-step .mktoForm[data-step="3"] .mktoFormRow.mktoFormRowTop.adobe-privacy,
.marketo.multi-step.multi-2 .mktoForm[data-step="2"] .mktoFormRow.mktoFormRowTop.adobe-privacy {
display: grid;
}

.marketo.multi-step .step-details {
display: flex;
justify-content: center;
font-size: var(--type-body-xs-size);
font-weight: normal;
line-height: var(--type-body-xs-lh);
grid-column: span 2;
}

.marketo.multi-step .step-details button {
background: none;
border: none;
padding: 0;
text-decoration: underline;
cursor: pointer;
color: var(--link-color-dark);
margin-right: 8px;
}

.marketo .mktoFormCol.mktoVisible {
width: 100%;
margin-bottom: 11px;
Expand Down Expand Up @@ -322,12 +372,12 @@
outline: 2px solid var(--color-accent-focus-ring);
}

.marketo .mktoForm .mktoFormRow.msg-error {
.marketo .mktoFormRow.mktoFormRowTop.msg-error .mktoFormCol {
display: none;
}

.marketo .mktoForm.show-warnings .mktoFormRow.msg-error {
display: contents;
.marketo .show-warnings .mktoFormRow.mktoFormRowTop.msg-error .mktoFormCol {
display: block;
}

.marketo .mktoForm .mktoFormRow.msg-error .mktoHtmlText {
Expand Down Expand Up @@ -513,6 +563,16 @@
grid-column: span 2;
}

.marketo.multi-step .mktoForm[data-step="2"] .mktoFormRow.mktoFormRowTop[data-validate="2"].comments,
.marketo.multi-step .mktoForm[data-step="2"] .mktoFormRow.mktoFormRowTop[data-validate="2"].demo,
.marketo.multi-step .mktoForm[data-step="2"] .mktoFormRow.mktoFormRowTop[data-validate="2"].name,
.marketo.multi-step .mktoForm[data-step="3"] .mktoFormRow.mktoFormRowTop[data-validate="3"].comments,
.marketo.multi-step .mktoForm[data-step="3"] .mktoFormRow.mktoFormRowTop[data-validate="3"].demo,
.marketo.multi-step .mktoForm[data-step="3"] .mktoFormRow.mktoFormRowTop[data-validate="3"].name {
display: block;
grid-column: span 2;
}

.marketo .mktoFormRow.mktoFormRowTop.name .mktoFormRow[data-mktofield="Salutation"] {
grid-area: salutation;
}
Expand Down Expand Up @@ -585,9 +645,17 @@
min-height: var(--marketo-form-placeholder-height-desktop);
}

.marketo.multi-step.loading form {
min-height: var(--marketo-form-placeholder-height-multi-desktop);
}

.marketo .mktoForm {
max-height: var(--marketo-form-placeholder-height-desktop);
}

.marketo.multi-step .mktoForm {
max-height: var(--marketo-form-placeholder-height-multi-desktop);
}

.resource-form.section.two-up {
grid-template-columns: repeat(2, 1fr);
Expand Down
Loading
Loading