diff --git a/.kodiak/config.yaml b/.kodiak/config.yaml index 2e8487ab1..dc1f1c9fa 100644 --- a/.kodiak/config.yaml +++ b/.kodiak/config.yaml @@ -1,20 +1,24 @@ version: 1.0 +#### If you have questions about this, please contact #kodiak-security on Slack. #### +# Every repository requires a unique Service Now ID number. You need to create a new service using +# the Service Registry Portal https://adobe.service-now.com/service_registry_portal.do#/search +# You should NOT leave this default! snow: - id: 545804 # Milo Express notifications: jira: default: - project: MWPW # Mandatory + project: MWPW # Project queue in which the security ticket will be created. Can be any valid JIRA project. filters: include: - risk_rating: R5 + risk_rating: R3 # Please do not change this unless instructed to do so. fields: - customfield_11800: MWPW-145743 #epic link + customfield_11800: MWPW-164516 # Jira epic security tickets will be assigned to. customfield_12900: - value: Express dev - watchers: + value: Express dev # Team security tickets will be assigned to + watchers: # Everyone who is an admin on your repository should be a watcher. - dstrong - vhargrave - jinglhua @@ -23,9 +27,9 @@ notifications: - all66020 - cod87753 - apganapa - labels: + labels: # You can add additional labels to your tickets here. Do not delete/change the first three. - "OriginatingProcess=Kodiak" - "security" - "kodiak-ticket" components: - - name: "DevOps Security" + - name: "DevOps Security" # Please do not change this. diff --git a/express/blocks/banner/banner.css b/express/blocks/banner/banner.css index 63e641981..a7e3aa6d6 100644 --- a/express/blocks/banner/banner.css +++ b/express/blocks/banner/banner.css @@ -8,6 +8,14 @@ main div:has(> .banner.cool) { background: linear-gradient(170.96deg, #bfe4ff -0.25%, #cecef6 104.12%); } +main .section .banner .bg-img-container { + background-size: cover; + background-position: center; + background-repeat: no-repeat; + padding: 20px; + max-height: 300px; +} + main .section.banner-container, main .section.banner-light-container { padding: 80px 15px; diff --git a/express/blocks/banner/banner.js b/express/blocks/banner/banner.js index ce29f874b..99a48e745 100644 --- a/express/blocks/banner/banner.js +++ b/express/blocks/banner/banner.js @@ -6,6 +6,20 @@ export default async function decorate(block) { const isBannerCoolVariant = block.classList.contains('cool'); const isBannerNarrowVariant = block.classList.contains('narrow'); + const bgImgURL = block.children[0]?.querySelector('img')?.src; + const header = block.querySelector('h2'); + if (header) { + const headerParent = header.parentElement; + if (bgImgURL) { + const firstChild = block.children[0]; + if (firstChild) { + block.removeChild(firstChild); + } + headerParent.classList.add('bg-img-container'); + headerParent.style.backgroundImage = `url(${bgImgURL})`; + } + } + if (isBannerStandoutVariant || isBannerCoolVariant) { const contentContainer = createTag('div', { class: 'content-container', diff --git a/express/blocks/how-to-steps-carousel/how-to-steps-carousel.css b/express/blocks/how-to-steps-carousel/how-to-steps-carousel.css index 1f3bc46c3..40b18b36f 100755 --- a/express/blocks/how-to-steps-carousel/how-to-steps-carousel.css +++ b/express/blocks/how-to-steps-carousel/how-to-steps-carousel.css @@ -11,10 +11,8 @@ main .how-to-steps-carousel-container > picture > img { main .how-to-steps-carousel-container > div { background-color: var(--color-gray-100); border-radius: 40px; - margin: 0 24px; padding: 56px 24px; - width: 100%; max-width: fit-content; } diff --git a/express/blocks/how-to-v2/how-to-v2.css b/express/blocks/how-to-v2/how-to-v2.css new file mode 100644 index 000000000..ea7a97634 --- /dev/null +++ b/express/blocks/how-to-v2/how-to-v2.css @@ -0,0 +1,216 @@ +main .how-to-v2 h2 { + font-size: 22px; + text-align: center; + padding: 0 24px; +} + +main .how-to-v2 ol { + list-style-type: none; + font-size: 21px; + font-weight: 900; +} + +main .how-to-v2 ol li { + padding: 8px 0; + cursor: pointer; + transition: all 0.21s; + display: flex; +} + +main .how-to-v2 ol li .step-indicator { + width: 5px; + min-width: 5px; + background: linear-gradient(-96.68deg, #FF4885 5.24%, #FC7D00 94.76%), #686DF4; + border-radius: 2.5px; +} + +main .how-to-v2 ol li .step-content { + padding: 8px 16px; + margin: 0; +} + +main .how-to-v2 ol li .step-indicator:has(+ div .closed) { + background: linear-gradient(95.55deg, rgba(255, 255, 255, 0.5) 4.43%, rgba(255, 255, 255, 0.3) 93.65%), #8F8F8F; +} + +main .how-to-v2 ol li h3 { + text-align: left; + font-size: 18px; + line-height: 23.4px; + font-weight: 700; +} + +main .how-to-v2 ol li .detail-container { + font-size: 16px; + line-height: 20.8px; + font-weight: 400; +} + +main .how-to-v2 ol li .detail-container { + max-height: 0; + overflow: hidden; + transition: max-height 0.21s ease-out; +} + +main .how-to-v2 ol li .detail-text { + padding-top: 10px; +} + +/* so that there is no initial re-paint */ +main .how-to-v2 ol li:first-child .detail-container { + max-height: none; +} + +main .how-to-v2.video lite-youtube, +main .how-to-v2.image picture { + border-radius: 16px; + overflow: hidden; +} + +main .how-to-v2 em { + font-style: normal; + background: linear-gradient(140.08deg, #FF4DD2 67.54%, #FF993B 76.42%); + background-size: 108% 108%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-weight: 900; +} + +main .how-to-v2 .steps-content { + display: flex; + flex-direction: column; + padding: 0 24px; + position: relative; +} + +main .how-to-v2 .steps-content .steps-content-backg { + position: absolute; + width: 100%; + top: -24vw; + z-index: -1; + height: 100%; + transform: scale(0.9); +} + +main .how-to-v2 ol.steps { + margin: 6px 0 0; + padding: 0; +} + +main .how-to-v2 ol.steps .step .step-content h3:hover { + background-color: var( --color-gray-100); + margin: -8px 0 -8px -16px; + padding: 8px 0 8px 16px; +} + +main .how-to-v2.video .video-container { + margin-top: 20px; +} + +main .how-to-v2.image .image-container { + margin-top: 20px; + width: 100%; + overflow: hidden; +} + +main .how-to-v2.image .image-container img, +main .how-to-v2.video .video-container > * { + border-radius: 16px; + max-width: 400px; + width: 100%; + height: 100%; + object-fit: cover; +} + +main .how-to-v2.video .video-container > * { + margin-left: auto; + margin-right: auto; + height: unset; +} + +main .how-to-v2.video, +main .how-to-v2.image { + /* note we don't set margin here because background gradient has to go past into this container */ + margin: 0; + max-width: unset; +} + +@media (min-width: 768px) { + + main .how-to-v2 h2 { + font-size: 28px; + } + + main .how-to-v2.video, + main .how-to-v2.image { + max-width: 950px; + margin: auto; + } + + main .how-to-v2 .steps-content { + display: flex; + flex-direction: row; + padding: 0 24px; + } + + main .how-to-v2 .steps-content > * { + flex: 1 1 50%; + box-sizing: border-box; + } + + main .how-to-v2.image .image-container, + main .how-to-v2.video .video-container { + align-items: center; + display: inline-grid; + } + + main .how-to-v2.image .image-container img { + height: unset; + } + + main .how-to-v2 .steps-content .steps { + padding-left: 20px; + } + + main .how-to-v2 .steps-content .steps-content-backg { + left: -90px; + width: 67%; + transform: scale(0.9); + top: -68px; + } +} + +@media (min-width: 1280px) { + + main .how-to-v2 h2 { + font-size: 36px; + } + + main .how-to-v2.video, + main .how-to-v2.image { + max-width: 1300px; + margin: auto; + } + + main .how-to-v2.image .image-container img, + main .how-to-v2.video .video-container > * { + max-height: 300px; + max-width: 533px; + } + + main .how-to-v2.image .image-container { + display: inline-block; + } + + main .how-to-v2 .steps-content { + margin-top: 30px; + } + + main .how-to-v2 ol li h3 { + text-align: left; + font-size: 22px; + line-height: 28.6px; + font-weight: 700; + } +} diff --git a/express/blocks/how-to-v2/how-to-v2.js b/express/blocks/how-to-v2/how-to-v2.js new file mode 100644 index 000000000..6d4099b25 --- /dev/null +++ b/express/blocks/how-to-v2/how-to-v2.js @@ -0,0 +1,129 @@ +/* eslint-disable import/named, import/extensions */ +import { createTag } from '../../scripts/utils.js'; +import { embedYoutube } from '../../scripts/embed-videos.js'; + +function setStepDetails(block, indexOpenedStep) { + const listItems = block.querySelectorAll(':scope li'); + + listItems.forEach((item, i) => { + const detail = item.querySelector('.detail-container'); + + if (i === indexOpenedStep) { + detail.classList.remove('closed'); + detail.style.maxHeight = `${detail.scrollHeight}px`; + } else { + detail.classList.add('closed'); + detail.style.maxHeight = '0'; + } + }); +} + +function buildAccordion(block, rows, stepsContent) { + let indexOpenedStep = 0; + const list = createTag('OL', { class: 'steps' }); + + rows.forEach((row, i) => { + const [stepTitle, stepDetail] = row.querySelectorAll(':scope div'); + + const newStepTitle = createTag('h3'); + newStepTitle.replaceChildren(...stepTitle.childNodes); + + const listItem = createTag('LI', { class: 'step', tabindex: '0' }); + list.append(listItem); + + const listItemIndicator = createTag('div', { class: 'step-indicator' }); + const listItemContent = createTag('div', { class: 'step-content' }); + + const detailText = stepDetail; + detailText.classList.add('detail-text'); + + const detailContainer = createTag('div', { class: 'detail-container' }); + + if (i !== 0) { + detailContainer.classList.add('closed'); + } + + detailContainer.append(detailText); + + listItem.append(listItemIndicator); + listItem.append(listItemContent); + + listItemContent.append(newStepTitle); + listItemContent.append(detailContainer); + + const handleOpenDetails = (ev) => { + indexOpenedStep = i; + setStepDetails(block, indexOpenedStep); + ev.preventDefault(); + }; + + newStepTitle.addEventListener('click', handleOpenDetails); + listItem.addEventListener('keyup', (ev) => ev.which === 13 && handleOpenDetails(ev)); + }); + + stepsContent.append(list); + + // set this in the next event cycle when scrollHeight has been established + setTimeout(() => { + setStepDetails(block, indexOpenedStep); + }, 0); +} + +export default async function decorate(block) { + const isVideoVariant = block.classList.contains('video'); + const isImageVariant = block.classList.contains('image'); + + const section = block.closest('.section'); + const rows = Array.from(block.children); + + const backgroundRow = block.children[0]; + const backgroundImage = backgroundRow.querySelector('img'); + const backgroundURL = backgroundImage?.src; + const hasBackground = !!backgroundURL; + + if (hasBackground) { + rows.shift(); + } + + if (isVideoVariant || isImageVariant) { + const stepsContent = createTag('div', { class: 'steps-content' }); + + if (hasBackground) { + // So that background image goes beyond container + const stepsContentBackground = createTag('div', { class: 'steps-content-backg' }); + const stepsContentBackgroundImg = createTag('img', { class: 'steps-content-backg-image' }); + stepsContent.append(stepsContentBackground); + stepsContentBackground.append(stepsContentBackgroundImg); + stepsContentBackgroundImg.src = backgroundURL; + } + + if (isVideoVariant) { + const videoData = rows.shift(); + + // remove the added social link from the block DOM + block.removeChild(block.children[0]); + + const videoLink = videoData.querySelector('a'); + const youtubeURL = videoLink?.href; + const url = new URL(youtubeURL); + + const videoContainerEl = createTag('div', { class: 'video-container' }); + + const videoEl = embedYoutube(url); + videoEl.classList.add('video-how-to-steps-accordion'); + + videoContainerEl.append(videoEl); + stepsContent.append(videoContainerEl); + } else { + const imageData = rows.shift(); + const imageEl = imageData.querySelector('picture'); + const imageContainerEl = createTag('div', { class: 'image-container' }); + imageContainerEl.append(imageEl); + stepsContent.append(imageContainerEl); + } + + const heading = section.querySelector('h2, h3, h4'); + block.replaceChildren(heading, stepsContent); + buildAccordion(block, rows, stepsContent); + } +} diff --git a/express/blocks/mobile-fork-button-frictionless/mobile-fork-button-frictionless.css b/express/blocks/mobile-fork-button-frictionless/mobile-fork-button-frictionless.css index 735bb5b86..86b00dc4b 100644 --- a/express/blocks/mobile-fork-button-frictionless/mobile-fork-button-frictionless.css +++ b/express/blocks/mobile-fork-button-frictionless/mobile-fork-button-frictionless.css @@ -1,5 +1,10 @@ main .floating-button-wrapper.mobile-fork-button-frictionless, main .mobile-fork-button-frictionless { z-index: 10; + --mfb-font-size: 16px; +} + +main .mobile-fork-button-frictionless.long-text { + --mfb-font-size: 14px; } main .mobile-gating-text { @@ -8,7 +13,7 @@ main .mobile-gating-text { text-align: left; flex-grow: 1; font-weight: 400; - font-size: var(--body-font-size-m); + font-size: var(--mfb-font-size); } main .mobile-gating-text, main .mobile-fork-button-frictionless .floating-button .mobile-gating-row .icon{ @@ -41,7 +46,7 @@ main .mobile-fork-button-frictionless .floating-button.block { main .mobile-gating-row { display: flex; gap: 10px; - height: 40px; + min-height: 40px; } main .mobile-gating-header { @@ -53,9 +58,10 @@ main .mobile-gating-header { main .mobile-fork-button-frictionless .floating-button a.button:any-link { border-radius: 20px; - font-size: var(--body-font-size-m); - min-width: 136px; - width: 40%; + font-size: var(--mfb-font-size); + min-width: 150px; + margin: auto; + width: 50%; color: var(--color-white); background-color: var(--color-info-accent); border-color: var(--color-info-accent); diff --git a/express/blocks/mobile-fork-button-frictionless/mobile-fork-button-frictionless.js b/express/blocks/mobile-fork-button-frictionless/mobile-fork-button-frictionless.js index 0ffaf68f7..c6e05be46 100644 --- a/express/blocks/mobile-fork-button-frictionless/mobile-fork-button-frictionless.js +++ b/express/blocks/mobile-fork-button-frictionless/mobile-fork-button-frictionless.js @@ -8,6 +8,16 @@ import { createFloatingButton, } from '../shared/floating-cta.js'; +const LONG_TEXT_CUTOFF = 70; + +const getTextWidth = (text, font) => { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + context.font = font; + const metrics = context.measureText(text); + return metrics.width; +}; + function buildAction(entry, buttonType) { const wrapper = createTag('div', { class: 'floating-button-inner-row mobile-gating-row' }); const text = createTag('div', { class: 'mobile-gating-text' }); @@ -94,6 +104,9 @@ function collectFloatingButtonData(eligible) { }); } aTag.textContent = text; + if (getTextWidth(text, 16) > LONG_TEXT_CUTOFF) { + data.longText = true; + } data.tools.push({ icon, anchor: aTag, @@ -122,4 +135,5 @@ export default async function decorate(block) { const linksPopulated = new CustomEvent('linkspopulated', { detail: blockLinks }); document.dispatchEvent(linksPopulated); } + if (data.longText) blockWrapper.classList.add('long-text'); } diff --git a/express/blocks/mobile-fork-button/mobile-fork-button.css b/express/blocks/mobile-fork-button/mobile-fork-button.css index 1e7338b2c..f87cae40c 100644 --- a/express/blocks/mobile-fork-button/mobile-fork-button.css +++ b/express/blocks/mobile-fork-button/mobile-fork-button.css @@ -1,5 +1,10 @@ main .floating-button-wrapper.mobile-fork-button, main .mobile-fork-button { z-index: 10; + --mfb-font-size: 16px; +} + +main .mobile-fork-button.long-text { + --mfb-font-size: 14px; } main .mobile-gating-text { @@ -8,7 +13,7 @@ main .mobile-gating-text { text-align: left; flex-grow: 1; font-weight: 400; - font-size: var(--body-font-size-m); + font-size: var(--mfb-font-size); } main .mobile-gating-text, main .mobile-fork-button .floating-button .mobile-gating-row .icon{ @@ -41,7 +46,7 @@ main .mobile-fork-button .floating-button.block { main .mobile-gating-row { display: flex; gap: 10px; - height: 40px; + min-height: 40px; } main .mobile-gating-header { @@ -53,9 +58,10 @@ main .mobile-gating-header { main .mobile-fork-button .floating-button a.button:any-link { border-radius: 20px; - font-size: var(--body-font-size-m); - min-width: 136px; - width: 40%; + font-size: var(--mfb-font-size); + min-width: 150px; + margin: auto; + width: 50%; color: var(--color-white); background-color: var(--color-info-accent); border-color: var(--color-info-accent); diff --git a/express/blocks/mobile-fork-button/mobile-fork-button.js b/express/blocks/mobile-fork-button/mobile-fork-button.js index 4e136fd7a..f6952b010 100644 --- a/express/blocks/mobile-fork-button/mobile-fork-button.js +++ b/express/blocks/mobile-fork-button/mobile-fork-button.js @@ -8,6 +8,16 @@ import { createFloatingButton, } from '../shared/floating-cta.js'; +const LONG_TEXT_CUTOFF = 70; + +const getTextWidth = (text, font) => { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + context.font = font; + const metrics = context.measureText(text); + return metrics.width; +}; + function buildAction(entry, buttonType) { const wrapper = createTag('div', { class: 'floating-button-inner-row mobile-gating-row' }); const text = createTag('div', { class: 'mobile-gating-text' }); @@ -89,6 +99,9 @@ function collectFloatingButtonData() { } = completeSet; const aTag = createTag('a', { title: text, href }); aTag.textContent = text; + if (getTextWidth(text, 16) > LONG_TEXT_CUTOFF) { + data.longText = true; + } data.tools.push({ icon, anchor: aTag, @@ -121,4 +134,5 @@ export default async function decorate(block) { const linksPopulated = new CustomEvent('linkspopulated', { detail: blockLinks }); document.dispatchEvent(linksPopulated); } + if (data.longText) blockWrapper.classList.add('long-text'); } diff --git a/express/blocks/premium-plan/premium-plan.css b/express/blocks/premium-plan/premium-plan.css deleted file mode 100644 index 3d59d0bf0..000000000 --- a/express/blocks/premium-plan/premium-plan.css +++ /dev/null @@ -1,248 +0,0 @@ -main .premium-plan-container > div { - max-width: 100%; - padding: 0; -} - -main .premium-plan .premium-plan-banner { - display: flex; - align-items: center; - justify-content: center; - background: var(--gradient-highlight-diagonal); -} - -main .premium-plan .premium-plan-banner-desktop { - display: none; -} - -main .premium-plan .premium-plan-banner > div { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding: 40px 20px 80px 20px; - max-width: 320px; -} - -main .premium-plan .premium-plan-banner img { - width: 100%; - margin-top: 20px; -} - -main .premium-plan .premium-plan-banner h2 { - color: white; - font-size: var(--heading-font-size-m); - line-height: var(--heading-line-height); - text-align: left; - max-width: 375px; -} - -main .premium-plan .premium-plan-cards { - display: flex; - justify-content: center; - align-items: center; - background: linear-gradient(to top, var(--body-alt-background-color), var(--body-alt-background-color)) no-repeat 0 80px; - margin-top: -80px; -} - -main .premium-plan .premium-plan-cards > div { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - padding: 0 15px 20px 15px; - box-sizing: border-box; - max-width: 375px; -} - -main .premium-plan .premium-plan-card { - display: flex; - flex-direction: column; - align-items: flex-start; - padding: 20px 30px 40px 30px; - background-color: white; - border-radius: 20px; - margin-bottom: 30px; -} - -main .premium-plan .premium-plan-card-desktop { - display: none; -} - -main .premium-plan .premium-plan-card h3 { - color: var(--color-gray-800); -} - -main .premium-plan .premium-plan-card p { - margin: 0; - text-align: left; - font-size: var(--body-font-size-m); - font-weight: var(--body-font-weight); - max-width: 280px; - color: var(--color-gray-800); -} - -main .premium-plan .premium-plan-card-icon img { - width: 40px; -} - -main .premium-plan .premium-plan-card h3 { - text-align: left; - font-size: var(--heading-font-size-s); - margin: 10px 0; - max-width: 260px; -} - -main .premium-plan .premium-plan-card-image img { - width: 50%; - margin: 20px 0; -} - -main .premium-plan .premium-plan-card ul { - display: flex; - list-style-type: none; - margin: 0; - padding-left: 0; -} - -main .premium-plan .premium-plan-card ul li a svg { - width: 40px; - height: 40px; - margin-right: 10px; - margin-bottom: 0; -} - -@media (min-width: 900px) { - main .premium-plan { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding: 0 20px; - } - - main .premium-plan .premium-plan-banner { - justify-content: center; - align-items: center; - position: relative; - max-width: 1180px; - width: 100%; - border-radius: 15px; - } - - main .premium-plan .premium-plan-banner-mobile { - display: none; - } - - main .premium-plan .premium-plan-banner-desktop { - display: flex; - } - - main .premium-plan .premium-plan-banner > div { - max-width: none; - width: 100%; - align-items: flex-start; - padding: 40px; - } - - main .premium-plan .premium-plan-banner h2 { - max-width: 500px; - } - - main .premium-plan .premium-plan-banner picture { - position: absolute; - top: 50%; - right: 40px; - max-width: 320px; - margin: 0; - transform: translateY(-50%); - } - - main .premium-plan .premium-plan-cards { - max-width: 1180px; - width: 100%; - background: none; - margin-top: 20px; - justify-content: flex-start; - } - - main .premium-plan .premium-plan-cards > div { - max-width: none; - flex-direction: row; - padding: 0; - justify-content: flex-start; - } - - main .premium-plan .premium-plan-card { - position: relative; - width: 415px; - max-width: 475px; - border: 2px #E8E8E8 solid; - padding: 20px 30px; - margin-right: 20px; - min-height: 230px; - } - - main .premium-plan .premium-plan-card h3 { - margin-bottom: 5px; - } - - main .premium-plan .premium-plan-card-desktop { - display: flex; - } - - main .premium-plan .premium-plan-card-mobile { - display: none; - } - - main .premium-plan .premium-plan-card-image { - position: absolute; - top: 45%; - right: 30px; - transform: translateY(-50%); - width: 120px; - } - - main .premium-plan .premium-plan-card-image img { - width: 100%; - } - - main .premium-plan .premium-plan-card ul { - margin-top: auto; - } -} - -@media (min-width: 1020px) { - main .premium-plan .premium-plan-banner picture { - max-width: 480px; - top: -70px; - right: 0; - transform: none; - } -} - -@media (min-width: 1200px) { - main .premium-plan .premium-plan-banner picture { - max-width: 580px; - top: -90px; - transform: none; - } -} - -/* Disable carousel on mobile */ - -@media (max-width: 899px) { - main .premium-plan-cards .carousel-container, - main .premium-plan-cards .carousel-container .carousel-platform { - display: block; - max-height: unset; - } - - main .premium-plan-cards .carousel-container a.button.carousel-arrow { - display: none; - } - - main .premium-plan .premium-plan-cards { - flex-direction: column; - } -} diff --git a/express/blocks/premium-plan/premium-plan.js b/express/blocks/premium-plan/premium-plan.js deleted file mode 100644 index ca8b8f3bb..000000000 --- a/express/blocks/premium-plan/premium-plan.js +++ /dev/null @@ -1,99 +0,0 @@ -import { - createTag, - getIconElement, - // eslint-disable-next-line import/no-unresolved -} from '../../scripts/utils.js'; - -import buildCarousel from '../shared/carousel.js'; - -export default function decorate($block) { - if ($block.children.length) { - const $desktopBanner = $block.children[0]; - if ($desktopBanner) { - $desktopBanner.classList.add('premium-plan-banner', 'premium-plan-banner-desktop'); - const $desktopBannerCta = $desktopBanner.querySelector('a'); - if ($desktopBannerCta) { - $desktopBannerCta.classList.add('dark', 'reverse'); - $desktopBannerCta.classList.remove('accent'); - } - const $desktopImg = $desktopBanner.querySelector('picture:first-child:last-child'); - if ($desktopImg && $desktopImg.parentElement.tagName === 'P') { - // unwrap single picture if wrapped in p tag - const $desktopParentDiv = $desktopImg.closest('div'); - const $desktopParentParagraph = $desktopImg.parentNode; - $desktopParentDiv.insertBefore($desktopImg, $desktopParentParagraph); - $desktopParentParagraph.remove(); - } - $desktopBanner.children[0].remove(); - } - } - if ($block.children.length > 1) { - const $mobileBanner = $block.children[1]; - if ($mobileBanner) { - $mobileBanner.classList.add('premium-plan-banner', 'premium-plan-banner-mobile'); - const $mobileBannerCta = $mobileBanner.querySelector('a'); - if ($mobileBannerCta) { - $mobileBannerCta.classList.add('dark', 'reverse'); - $mobileBannerCta.classList.remove('accent'); - } - const $mobileImg = $mobileBanner.querySelector('picture:first-child:last-child'); - if ($mobileImg && $mobileImg.parentElement.tagName === 'P') { - // unwrap single picture if wrapped in p tag - const $desktopParentDiv = $mobileImg.closest('div'); - const $desktopParentParagraph = $mobileImg.parentNode; - $desktopParentDiv.insertBefore($mobileImg, $desktopParentParagraph); - $desktopParentParagraph.remove(); - } - $mobileBanner.children[0].remove(); - } - } - if ($block.children.length > 2) { - const $container = createTag('div', { class: 'premium-plan-cards' }); - const $cards = createTag('div'); - $container.append($cards); - let failsafe = 20; - while ($block.children.length > 2) { - const device = $block.children[2].children[0].textContent.trim().toLowerCase(); - const $cardDiv = $block.children[2].children[1]; - const $cardLink = $cardDiv.children[0].querySelector('a'); - let $card; - - if ($cardLink) { - $card = createTag('a', { class: 'premium-plan-card', href: $cardLink.href }); - $cardLink.remove(); - $card.innerHTML = $cardDiv.innerHTML; - } else { - $card = $cardDiv; - $card.classList.add('premium-plan-card'); - } - - if (device === 'mobile') { - $card.classList.add('premium-plan-card-mobile'); - } else if (device === 'desktop') { - $card.classList.add('premium-plan-card-desktop'); - } - $cards.append($card); - $block.children[2].remove(); - failsafe -= 1; - if (!failsafe) { // prevent a possible infinite loop. - break; - } - const $images = $card.querySelectorAll('picture'); - if ($images.length) { - $images[0].classList.add('premium-plan-card-icon'); - } - if ($images.length > 1) { - $images[1].classList.add('premium-plan-card-image'); - } - const $links = $card.querySelectorAll('li a'); - if ($links) { - $links.forEach(($link) => { - const iconName = $link.textContent.trim().toLowerCase(); - $link.append(getIconElement(iconName)); - }); - } - } - $block.append($container); - buildCarousel('.premium-plan-card', $container); - } -} diff --git a/express/blocks/puf/check.svg b/express/blocks/puf/check.svg deleted file mode 100644 index 051de2b7a..000000000 --- a/express/blocks/puf/check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/express/blocks/puf/puf.css b/express/blocks/puf/puf.css deleted file mode 100644 index a73b0cc37..000000000 --- a/express/blocks/puf/puf.css +++ /dev/null @@ -1,536 +0,0 @@ -main .section.puf-container { - padding-bottom: 0; -} - -main .puf-container > div { - max-width: none; -} - -main .block.puf .carousel-container { - overflow: visible; - transition: max-height 0.3s, min-height 0.3s; - max-width: 600px; -} - -main .block.puf { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -main .block.puf p { - margin: 0; -} - -main .block.puf .puf-card-container { - display: flex; - justify-content: center; - border-radius: 20px; -} - -main .block.puf .puf-card { - position: relative; - border-radius: 20px; - box-sizing: border-box; - padding: 32px 30px 16px 30px; - background-color: var(--color-white); -} - -main .block.puf .puf-card .puf-card-top { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin-bottom: 10px; -} - -main .block.puf .puf-card .puf-card-top .puf-pricing-container { - display: flex; - align-items: flex-end; -} - -main .block.puf .puf-card .puf-card-top h3 { - display: flex; - align-items: center; - justify-content: center; - font-size: var(--heading-font-size-l); - line-height: var(--heading-line-height); -} - -main .block.puf .puf-card h3 { - width: 100%; -} - -main .block.puf .puf-card .puf-card-top .puf-pricing-container { - margin-top: 20px; - display: flex; - flex-wrap: wrap; - justify-content: center; -} - -main .block.puf .puf-card .puf-pricing-suf-container { - text-align: left; -} - -main .block.puf .puf-card .puf-card-top h2.puf-bp-header { - color: var(--color-gray-600); -} - -main .block.puf .puf-card .puf-card-top h2.puf-bp-header strong { - text-decoration: line-through; -} - -main .block.puf .puf-card .puf-card-top h2 { - display: flex; - font-size: var(--heading-font-size-m); - font-weight: var(--heading-font-weight); - margin: 0; -} - -main .block.puf .puf-card .puf-card-top h2 strong { - font-size: var(--heading-font-size-xxl); - font-weight: 800; - margin-top: -10px; -} - -main .block.puf .puf-card .puf-card-top h2.jpy strong { - margin-right: 10px; -} - -main .block.puf .puf-card .puf-card-top h3 svg { - margin-right: 10px; - width: 32px; - height: 32px; -} - -main .block.puf .puf-card .puf-card-top h3 img { - margin-right: 7px; - width: 37px; - height: 37px; -} - -main .block.puf .puf-card .puf-card-top a { - margin-top: 20px; - width: 100%; - box-sizing: border-box; -} - -main .block.puf .puf-card .puf-card-top p { - font-size: var(--body-font-size-m); - margin-bottom: 20px; - text-align: left; -} - -main .block.puf .puf-card .puf-card-top .puf-card-plans { - display: flex; - justify-content: space-between; - align-items: center; - margin: 0 auto; -} - -main .block.puf .puf-card .puf-card-top .puf-card-plans > div { - color: var(--color-info-accent); -} - -main .block.puf .puf-card .puf-card-top .puf-card-plans > div span { - color: var(--color-gray-800); -} - -main .block.puf .puf-card .puf-card-top .puf-card-plans > div.strong span { - -webkit-text-stroke-width: 0.6px; - -webkit-text-stroke-color: var(--color-gray-800); -} - -main .block.puf .puf-card .puf-card-top .puf-card-switch { - position: relative; - display: inline-block; - width: 58px; - height: 31px; - margin: 0 10px; - min-width: 58px; -} - -main .block.puf .puf-card .puf-card-top .puf-card-switch input { - opacity: 0; - width: 0; - height: 0; -} - -main .block.puf .puf-card .puf-card-top .puf-card-slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: var(--color-info-accent); - -webkit-transition: 0.4s; - transition: 0.4s; - border-radius: 34px; -} - -main .block.puf .puf-card .puf-card-top .puf-card-slider::before { - position: absolute; - content: ""; - height: 23px; - width: 23px; - left: 4px; - bottom: 4px; - background-color: white; - -webkit-transition: 0.4s; - transition: 0.4s; - border-radius: 50%; -} - -main .block.puf .puf-card .puf-card-top .puf-card-switch input:checked + .puf-card-slider { - background-color: var(--color-info-accent); -} - -main .block.puf .puf-card .puf-card-top .puf-card-switch input:focus + .puf-card-slider { - box-shadow: 0 0 1px #2196f3; -} - -main .block.puf .puf-card .puf-card-top .puf-card-switch input:checked + .puf-card-slider:before { - transform: translateX(26px); -} - -main .block.puf .puf-card .puf-card-bottom { - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; -} - -main .block.puf .puf-card .puf-card-bottom h3 { - font-size: var(--body-font-size-xl); - font-weight: 600; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 20px; -} - -main .block.puf .puf-card .puf-card-bottom p { - font-size: var(--body-font-size-l); - margin-bottom: 40px; - text-align: left; - display: flex; - align-items: center; -} - -main .block.puf .puf-card .puf-card-bottom svg { - height: 22px; - min-width: 22px; - margin-right: 8px; -} - -main .block.puf .puf-card .puf-card-bottom h6 { - font-weight: var(--body-font-weight); - font-size: var(--body-font-size-s); - margin: 0; -} - -main .block.puf .puf-card .puf-sup { - font-size: 0.5em; -} - -main .block.puf .puf-card .puf-text-and-sup-wrapper { - display: inline; -} - -main .block.puf .puf-card-banner.recommended ~ .puf-card-bottom .puf-highlighted-text, -main .block.puf .puf-highlighted-text { - background-color: var(--color-info-accent-light); - width: 100%; - border-radius: 12px; - margin-top: 0; - box-sizing: border-box; - padding: 10px; -} - -main .block.puf .puf-divider-text { - box-sizing: border-box; - display: flex; - align-items: center; - gap: 16px; -} - -main .block.puf .puf-divider-text:before, -main .block.puf .puf-divider-text:after { - content: ''; - width: 100%; - height: 0; - border: solid 1px var(--color-gray-300); - flex: 1; -} - -main .block.puf .puf-pricing-footer { - max-width: 1200px; - width: 100%; -} - -main .block.puf .puf-pricing-footer .puf-highlighted-text { - padding: 16px; - margin-top: 24px; -} - -main .block.puf .puf-card-banner.recommended { - color: white; - font-size: var(--body-font-size-l); -} - -main .block.puf .puf-card-banner.recommended .banner-text { - margin: 0 8px; -} - -main .block.puf .puf-card-container .puf-card-border { - border-radius: 20px; - padding: 48px 8px 8px 8px; - margin-top: 20px; - height: auto; - z-index: 0; -} - -main .block.puf .puf-card-container .puf-card-border:has(.recommended) { - background: linear-gradient(90deg, #ff477b 0%, #5c5ce0 52%, #318fff 100%); - box-shadow: 0 0 24px #00000029; -} - -main .block.puf .puf-card-container .puf-card { - border: 1px #e5e5e5 solid; - border-radius: 20px; -} - -main .block.puf .puf-card .puf-card-banner { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - top: -50px; - left: 0; - width: 100%; - height: 50px; - border-radius: 20px 20px 0 0; -} - -main .block.puf .puf-card .puf-card-banner a { - margin-left: 4px; - color: unset; - font-weight: unset; - text-decoration: underline; -} - -main .block.puf .carousel-container .carousel-platform { - position: relative; -} - -main .block.puf .carousel-container .carousel-platform .carousel-left-trigger, -main .block.puf .carousel-container .carousel-platform .carousel-right-trigger { - align-self: stretch; - width: 1px; -} - -main .block.puf .carousel-container .carousel-platform .carousel-left-trigger { - left: 0; -} - -main .block.puf .carousel-container .carousel-platform .carousel-right-trigger { - right: 0; -} - -main .block.puf .carousel-container { - position: relative; -} - -main .block.puf .puf-card .puf-card-top .puf-card-plans, -main .block.puf .carousel-container .carousel-platform .puf-card-container .puf-card .puf-card-top .puf-card-plans { - margin-top: 16px; -} - -main .block.puf .carousel-platform { - gap: 0px; - max-height: unset; - height: 100%; - min-height: 100%; -} - -main .block.puf .puf-card .puf-card-bottom p { - margin-bottom: 24px; -} - -main .block.puf .carousel-container .carousel-platform .puf-card-container { - width: calc(100% - 2px); -} - -main .block.puf .carousel-container .carousel-platform .puf-card-container .puf-card { - max-width: 325px; -} - -main .block.puf .puf-card-banner.recommended ~ .puf-card-bottom .puf-highlighted-text { - display: flex; - align-items: center; - justify-content: center; -} - -main .block.puf .puf-card-container { - align-self: start; -} - -main .block.puf .carousel-container .carousel-fader-left, -main .block.puf .carousel-container .carousel-fader-right { - margin-top: 86px; - height: calc(100% - 172px); -} - -main .block.puf .carousel-container .carousel-fader-left a.button.carousel-arrow, -main .block.puf .carousel-container .carousel-fader-right a.button.carousel-arrow { - position: sticky; - top: calc(50% - 16px); - bottom: calc(50% - 16px); -} - -@media (min-width: 600px) { - main .block.puf .puf-card-banner.recommended { - font-size: var(--body-font-size-xl); - } -} - - -main .block.puf .carousel-container .carousel-platform .puf-card-container .puf-card-border .puf-card { - box-shadow: 0 0 20px #00000029; -} - -@media (min-width: 900px) { - main .block.puf .puf-card .puf-card-top .puf-card-plans { - margin-top: 8px; - } - - main .block.puf .carousel-container .carousel-platform .puf-card-container .puf-card .puf-card-top .puf-card-plans { - margin-top: 0; - } - - main .block.puf .puf-card-container { - margin-left: auto; - margin-right: auto; - } - - main .block.puf .puf-card { - max-width: 610px; - padding: 40px 60px; - } - - main .block.puf .carousel-container .carousel-platform .puf-card-container .puf-card { - max-width: none; - } - - main .block.puf .carousel-container .carousel-platform .puf-card-container .puf-card-border:has(.recommended), - main .block.puf .carousel-container .carousel-platform .puf-card-container .puf-card-border .puf-card { - box-shadow: none; - } - - main .block.puf .carousel-container .carousel-platform .puf-card-container { - align-self: stretch; - max-width: 100%; - width: auto; - } - - main .block.puf .carousel-container .carousel-platform .puf-card-container .puf-card { - height: 100%; - width: 610px; - margin: 0; - padding: 40px 60px; - } - - main .block.puf .carousel-container .carousel-platform .carousel-left-trigger, - main .block.puf .carousel-container .carousel-platform .carousel-right-trigger { - align-self: stretch; - position: relative; - height: auto; - right: auto; - left: auto; - top: auto; - } - - main .block.puf .carousel-container { - max-width: none; - max-height: none !important; - } - - main .block.puf .carousel-container { - margin-bottom: 0; - } - - main .block.puf .carousel-container .carousel-platform { - align-items: start; - } - - main .block.puf .carousel-platform { - align-items: start; - max-height: unset; - max-width: 100%; - margin-left: auto; - margin-right: auto; - overflow: auto; - } - - main .block.puf .puf-card-container:not(:has(.recommended)) .puf-card { - padding-left: 60px; - padding-right: 76px; - } - - main .block.puf .puf-card .puf-card-top .puf-pricing-container { - margin-top: 10px; - gap: 4px; - } - - main .block.puf .puf-card .puf-pricing-suf-container div { - margin: 2px 0; - line-height: 24px; - font-size: var(--body-font-size-xl); - } - - main .block.puf .puf-card .puf-card-top .puf-card-plans { - min-height: 40px; - } - - main .block.puf .puf-card .puf-card-bottom p { - display: flex; - align-items: center; - margin-bottom: 24px; - text-align: left; - } - - main .block.puf .puf-card .puf-card-bottom svg { - display: inline-block; - margin-right: 16px; - width: 44px; - height: 44px; - flex: 0 0 44px; - } - - main .block.puf .puf-card .puf-card-bottom .puf-list-item { - position: relative; - } - - main .block.puf .puf-card .puf-card-bottom .puf-list-item::before { - position: absolute; - display: block; - content: ""; - left: -30px; - top: 50%; - transform: translateY(-50%); - width: 13px; - height: 11.5px; - background-color: var(--color-info-accent); - -webkit-mask-image: url("/express/blocks/puf/check.svg"); - mask-image: url("/express/blocks/puf/check.svg"); - -webkit-mask-repeat: no-repeat; - -webkit-mask-position: center; - -webkit-mask-size: contain; - mask-repeat: no-repeat; - mask-position: center; - mask-size: contain; - } - -} diff --git a/express/blocks/puf/puf.js b/express/blocks/puf/puf.js deleted file mode 100644 index bd3021533..000000000 --- a/express/blocks/puf/puf.js +++ /dev/null @@ -1,449 +0,0 @@ -import buildCarousel from '../shared/carousel.js'; -import { addPublishDependencies, createTag, getMetadata } from '../../scripts/utils.js'; -import { buildUrl, fetchPlan } from '../../scripts/utils/pricing.js'; - -let invisContainer; -let parent; - -function pushPricingAnalytics(adobeEventName, sparkEventName, plan) { - const url = new URL(window.location.href); - const sparkTouchpoint = url.searchParams.get('touchpointName'); - - /* eslint-disable no-underscore-dangle */ - /* global digitalData _satellite */ - digitalData._set('primaryEvent.eventInfo.eventName', adobeEventName); - digitalData._set('spark.eventData.eventName', sparkEventName); - digitalData._set( - 'spark.eventData.contextualData4', - `billingFrequency:${plan.frequency}`, - ); - digitalData._set( - 'spark.eventData.contextualData6', - `commitmentType:${plan.frequency}`, - ); - digitalData._set( - 'spark.eventData.contextualData7', - `currencyCode:${plan.currency}`, - ); - digitalData._set( - 'spark.eventData.contextualData9', - `offerId:${plan.offerId}`, - ); - digitalData._set('spark.eventData.contextualData10', `price:${plan.price}`); - digitalData._set( - 'spark.eventData.contextualData12', - `productName:${plan.name} - ${plan.frequency}`, - ); - digitalData._set('spark.eventData.contextualData14', 'quantity:1'); - digitalData._set('spark.eventData.trigger', sparkTouchpoint); - - _satellite.track('event', { - digitalData: digitalData._snapshot(), - }); - - digitalData._delete('primaryEvent.eventInfo.eventName'); - digitalData._delete('spark.eventData.eventName'); - digitalData._delete('spark.eventData.contextualData4'); - digitalData._delete('spark.eventData.contextualData6'); - digitalData._delete('spark.eventData.contextualData7'); - digitalData._delete('spark.eventData.contextualData9'); - digitalData._delete('spark.eventData.contextualData10'); - digitalData._delete('spark.eventData.contextualData12'); - digitalData._delete('spark.eventData.contextualData14'); -} - -async function selectPlan(card, planUrl, sendAnalyticEvent) { - const plan = await fetchPlan(planUrl); - - if (plan) { - const pricingCta = card.querySelector('.puf-card-top a'); - const pricingHeader = card.querySelector('.puf-pricing-header'); - const pricingSuf = card.querySelector('.puf-pricing-suf'); - const pricingVat = card.querySelector('.puf-vat-info'); - const pricingBase = card.querySelector('.puf-bp-header'); - - if (pricingHeader) { - pricingHeader.innerHTML = plan.formatted || ''; - pricingHeader.classList.add(plan.currency.toLowerCase()); - } - - if (pricingBase) { - pricingBase.innerHTML = plan.formattedBP || ''; - } - - if (pricingSuf) pricingSuf.textContent = plan.suffix || ''; - if (pricingVat) pricingVat.textContent = plan.vatInfo || ''; - - if (pricingCta) { - pricingCta.href = buildUrl(plan.url, plan.country, plan.language, plan.offerId); - pricingCta.dataset.planUrl = planUrl; - pricingCta.id = plan.stringId; - } - } - - if (sendAnalyticEvent) { - const adobeEventName = 'adobe.com:express:pricing:commitmentType:selected'; - const sparkEventName = 'pricing:commitmentTypeSelected'; - pushPricingAnalytics(adobeEventName, sparkEventName, plan); - } -} - -function displayPlans(card, plans) { - const planContainer = card.querySelector('.puf-card-plans'); - const cardSwitch = createTag('label', { class: 'puf-card-switch' }); - const checkbox = createTag('input', { - type: 'checkbox', - class: 'puf-card-checkbox', - }); - const slider = createTag('span', { class: 'puf-card-slider' }); - const defaultPlan = createTag('div', { class: 'strong' }); - const secondPlan = createTag('div'); - - defaultPlan.innerHTML = plans[0].text.replace( - plans[0].plan, - `${plans[0].plan}`, - ); - secondPlan.innerHTML = plans[1].text.replace( - plans[1].plan, - `${plans[1].plan}`, - ); - - planContainer.append(defaultPlan); - planContainer.append(cardSwitch); - cardSwitch.append(checkbox); - cardSwitch.append(slider); - planContainer.append(secondPlan); - - checkbox.addEventListener('change', () => { - if (checkbox.checked) { - defaultPlan.classList.remove('strong'); - secondPlan.classList.add('strong'); - selectPlan(card, plans[1].url, true); - } else { - defaultPlan.classList.add('strong'); - secondPlan.classList.remove('strong'); - selectPlan(card, plans[0].url, true); - } - }); - - return planContainer; -} - -function buildPlans(plansElement) { - const plans = []; - - plansElement.forEach((plan) => { - const planLink = plan.querySelector('a'); - - if (planLink) { - plans.push({ - url: planLink.href, - plan: planLink.textContent.trim(), - text: plan.textContent.trim(), - }); - } - }); - - return plans; -} - -async function decorateCard(block, cardClass = '') { - const cardClassName = `puf-card ${cardClass}`.trim(); - const cardContainer = createTag('div', { class: 'puf-card-container' }); - const cardBorder = createTag('div', { class: 'puf-card-border' }); - const card = createTag('div', { class: cardClassName }); - const cardBanner = block.children[0].children[0]; - const cardTop = block.children[1].children[0]; - const cardBottom = block.children[2].children[0]; - const cardHeader = cardTop.querySelector('h3, p:first-of-type'); - const cardHeaderIcon = cardTop.querySelector('svg') || cardTop.querySelector('img'); - const cardPricingContainer = createTag('div', { - class: 'puf-pricing-container', - }); - const cardBasePriceHeader = createTag('h2', { class: 'puf-bp-header' }); - const cardPricingHeader = createTag('h2', { class: 'puf-pricing-header' }); - const cardPricingSufContainer = createTag('div', { - class: 'puf-pricing-suf-container', - }); - const cardPricingSuf = createTag('div', { class: 'puf-pricing-suf' }); - const cardVat = createTag('div', { class: 'puf-vat-info' }); - const cardAdditionalContext = createTag('div', { - class: 'puf-pricing-context', - }); - const cardPlansContainer = createTag('div', { class: 'puf-card-plans' }); - const cardCta = createTag('a', { class: 'button large' }); - const plansElement = cardTop.querySelectorAll('li'); - const listItems = cardBottom.querySelectorAll('svg'); - const plans = buildPlans(plansElement); - - if (cardClass === 'puf-left' - && !['off', 'no', 'false'].includes(getMetadata('puf-left-reverse')?.toLowerCase())) { - cardCta.classList.add('reverse', 'accent'); - } - - let formattedHeader = createTag('h3'); - if (cardHeader?.tagName === 'P') { - formattedHeader.textContent = cardHeader.lastChild.data; - } else if (cardHeader?.tagName === 'H3') { - formattedHeader = cardHeader; - } - - cardBanner.classList.add('puf-card-banner'); - cardTop.classList.add('puf-card-top'); - cardBottom.classList.add('puf-card-bottom'); - - cardPricingContainer.append( - cardBasePriceHeader, - cardPricingHeader, - cardPricingSufContainer, - ); - cardPricingSufContainer.append(cardPricingSuf, cardVat); - cardTop.prepend( - cardHeader, - cardPricingContainer, - cardAdditionalContext, - cardPlansContainer, - cardCta, - ); - card.append(cardBanner, cardTop, cardBottom); - - if (!cardBanner.textContent.trim()) { - cardBanner.style.display = 'none'; - } else { - cardBanner.innerHTML = createTag( - 'span', - { class: 'banner-text' }, - cardBanner.innerHTML, - ).outerHTML; - cardBanner.classList.add('recommended'); - } - - if (cardHeaderIcon) formattedHeader.prepend(cardHeaderIcon); - - const ctaTextContainer = cardTop.querySelector('strong'); - if (ctaTextContainer) { - cardCta.textContent = ctaTextContainer.textContent.trim(); - ctaTextContainer.parentNode.remove(); - } else { - cardCta.textContent = 'Start your trial'; - } - - if (plans.length) { - await selectPlan(card, plans[0].url, false); - - if (plans.length > 1) { - displayPlans(card, plans); - } - } - - cardTop.querySelector('ul') - ?.remove(); - - const pricingContextContainer = cardTop.querySelector('em'); - if (pricingContextContainer) { - cardAdditionalContext.textContent = pricingContextContainer.textContent.trim(); - pricingContextContainer.parentNode.remove(); - } else { - cardAdditionalContext.remove(); - } - - // cardContainer.append(card); - cardContainer.append(cardBorder); - cardBorder.append(card); - - if (listItems) { - listItems.forEach((listItem) => { - listItem.parentNode.classList.add('puf-list-item'); - }); - } - - return cardContainer; -} - -function wrapTextAndSup(block) { - const supTags = block.getElementsByTagName('sup'); - Array.from(supTags) - .forEach((supTag) => { - supTag.classList.add('puf-sup'); - }); - - const listItems = block.querySelectorAll('.puf-list-item'); - listItems.forEach((listItem) => { - const { childNodes } = listItem; - - const filteredChildren = Array.from(childNodes) - .filter((node) => { - const isSvg = node.tagName && node.tagName.toLowerCase() === 'svg'; - const isTextNode = node.nodeType === Node.TEXT_NODE; - return !isSvg && (isTextNode || node.nodeType === Node.ELEMENT_NODE); - }); - - const filteredChildrenExceptFirstText = filteredChildren.slice(1); - - const textAndSupWrapper = createTag('div', { - class: 'puf-text-and-sup-wrapper', - }); - textAndSupWrapper.append(...filteredChildrenExceptFirstText); - listItem.append(textAndSupWrapper); - }); -} - -function formatTextElements(block) { - const highlightRegex = /^\(\(.*\)\)$/; - const dividerRegex = /^--.*--$/; - const blockElements = Array.from(block.querySelectorAll('*')); - - if ( - !blockElements.some( - (el) => highlightRegex.test(el.textContent) - || dividerRegex.test(el.textContent), - ) - ) { - return; - } - - const highlightedEls = blockElements.filter((el) => highlightRegex.test(el.textContent)); - - highlightedEls.forEach((el) => { - el.classList.add('puf-highlighted-text'); - el.textContent = el.textContent - .replace(/^\(\(/, '') - .replace(/\)\)$/, ''); - }); - - const dividerElements = blockElements.filter((el) => dividerRegex.test(el.textContent)); - - dividerElements.forEach((el) => { - el.classList.add('puf-divider-text'); - el.textContent = el.textContent - .replace(/^--/, '') - .replace(/--$/, ''); - }); -} - -function decorateFooter(block) { - if (block?.children?.[3]) { - const footer = createTag('div', { class: 'puf-pricing-footer' }); - footer.append(block.children[3]); - return footer; - } else { - return ''; - } -} - -function matchTwoElementsHeight(el1, el2) { - // ^ not the clearest name (sets the shortest of two elements to the tallest element's height) - if (el1 && el2) { - const maxHeight = Math.max( - el1.getBoundingClientRect().height, - el2.getBoundingClientRect().height, - ); - el1.style.height = `${maxHeight}px`; - el2.style.height = `${maxHeight}px`; - } -} - -function alignContent() { - const block = document.querySelector('.puf'); - const rightDescription = block.querySelector( - '.puf-card.puf-right > .puf-card-top > p:last-of-type', - ); - const leftDescription = block.querySelector( - '.puf-card.puf-left > .puf-card-top > p:last-of-type', - ); - const rightHeading = block.querySelector( - '.puf-card.puf-right > .puf-card-bottom > h3', - ); - const leftHeading = block.querySelector( - '.puf-card.puf-left > .puf-card-bottom > h3', - ); - - if (rightDescription) rightDescription.style.height = 'auto'; - if (leftDescription) leftDescription.style.height = 'auto'; - if (rightHeading) rightHeading.style.height = 'auto'; - if (leftHeading) leftHeading.style.height = 'auto'; - - if (window.innerWidth >= 900) { - matchTwoElementsHeight(rightDescription, leftDescription); - matchTwoElementsHeight(rightHeading, leftHeading); - } -} - -const resizeObserver = new ResizeObserver((entries) => { - entries.forEach(() => { - alignContent(); - }); -}); - -async function build1ColDesign(block) { - const pricingCard = await decorateCard(block); - const footer = decorateFooter(block); - - block.innerHTML = ''; - block.append(pricingCard); - - addPublishDependencies('/express/system/offers-new.json?limit=5000'); - wrapTextAndSup(block); - block.append(footer); - formatTextElements(block); -} - -async function build2ColDesign(block) { - invisContainer = createTag('div'); - parent = block.parentElement; - invisContainer.style.visibility = 'hidden'; - invisContainer.style.display = 'block'; - invisContainer.append(block); - const main = document.body.querySelector('main'); - main.append(invisContainer); - const leftCard = await decorateCard(block, 'puf-left'); - const rightCard = await decorateCard(block, 'puf-right'); - const footer = decorateFooter(block); - block.innerHTML = ''; - block.append(leftCard, rightCard); - await buildCarousel('.puf-card-container', block); - parent.append(block); - invisContainer.remove(); - resizeObserver.observe(rightCard); - const options = { - root: document.querySelector('.carousel-platform'), - rootMargin: '0px', - threshold: 0.55, - }; - const carouselContainer = block.querySelector('.carousel-container'); - const callback = (entries) => { - entries.forEach((entry) => { - if (!entry.isIntersecting) return; - if (window.innerWidth >= 900) { - carouselContainer.style.maxHeight = 'none'; - } else { - carouselContainer.style.maxHeight = `${entry.target.clientHeight}px`; - } - }); - }; - - const intersectionObserver = new IntersectionObserver(callback, options); - intersectionObserver.observe(rightCard); - intersectionObserver.observe(leftCard); - addPublishDependencies('/express/system/offers-new.json?limit=5000'); - wrapTextAndSup(block); - block.append(footer); - formatTextElements(block); -} -async function buildPUF(block) { - const colCount = block?.children[1]?.children?.length; - switch (colCount) { - case 1: - await build1ColDesign(block); - break; - case 2: - await build2ColDesign(block); - break; - default: - break; - } -} - -export default async function decorate(block) { - await buildPUF(block); -} diff --git a/express/blocks/shared/basic-carousel.js b/express/blocks/shared/basic-carousel.js index 74517f86d..d0841adaf 100644 --- a/express/blocks/shared/basic-carousel.js +++ b/express/blocks/shared/basic-carousel.js @@ -169,33 +169,30 @@ function initializeCarousel(selector, parent) { e.changedTouches[0].clientX, e.changedTouches[0].clientY, ); - const parentElement = tappedElement.closest('.template.basic-carousel-element'); + const isBtn = tappedElement.querySelector('.button-container.singleton-hover'); + const isCard = tappedElement.closest('.template.basic-carousel-element'); if (tappedElement) { const shareIconWrapper = tappedElement.closest('.share-icon-wrapper'); - const linkHref = parentElement.querySelector('a').href; - if (linkHref) { - if (shareIconWrapper) { - e.stopPropagation(); - navigator.clipboard.writeText(linkHref).then(() => { - const tooltip = shareIconWrapper.querySelector('.shared-tooltip'); - if (tooltip) { - tooltip.classList.add('display-tooltip'); - setTimeout(() => tooltip.classList.remove('display-tooltip'), 2000); - } - }).catch((err) => { - console.error('Failed to copy link:', err); - }); - - return; - } + const linkHref = isCard?.querySelector('a')?.href || isBtn?.querySelector('a')?.href; + if (linkHref && shareIconWrapper) { + e.stopPropagation(); + navigator.clipboard.writeText(linkHref).then(() => { + const tooltip = shareIconWrapper.querySelector('.shared-tooltip'); + if (tooltip) { + tooltip.classList.add('display-tooltip'); + setTimeout(() => tooltip.classList.remove('display-tooltip'), 2000); + } + }).catch((err) => { + window.lana?.log('Failed to copy link:', err); + }); + return; } - - if (parentElement) { - const isHoverActive = parentElement.querySelector('.button-container.singleton-hover'); + if (isCard && linkHref) { + const isHoverActive = isCard?.querySelector('.button-container.singleton-hover'); if (isHoverActive && linkHref) { window.location.href = linkHref; } - const tappedIndex = Array.from(elements).indexOf(parentElement); + const tappedIndex = Array.from(elements).indexOf(isCard); if (tappedIndex !== -1) { if (tappedIndex < currentIndex) { currentIndex = Math.max(0, tappedIndex); @@ -204,7 +201,7 @@ function initializeCarousel(selector, parent) { currentIndex = Math.min(elements.length - 1, tappedIndex); updateCarousel(); } else { - const btnContainer = parentElement.querySelector('.button-container'); + const btnContainer = isCard.querySelector('.button-container'); if (btnContainer) { btnContainer.dispatchEvent(new Event('carouseltapstart')); setTimeout(() => { @@ -213,6 +210,8 @@ function initializeCarousel(selector, parent) { } } } + } else if (isBtn && linkHref) { + window.location.href = linkHref; } } }); diff --git a/express/scripts/utils.js b/express/scripts/utils.js index 82d02a3c9..f03f41f95 100644 --- a/express/scripts/utils.js +++ b/express/scripts/utils.js @@ -238,19 +238,6 @@ export function sampleRUM(checkpoint, data = {}, forceSampleRate) { } } -export function addPublishDependencies(url) { - if (!Array.isArray(url)) { - // eslint-disable-next-line no-param-reassign - url = [url]; - } - window.hlx = window.hlx || {}; - if (window.hlx.dependencies && Array.isArray(window.hlx.dependencies)) { - window.hlx.dependencies.concat(url); - } else { - window.hlx.dependencies = url; - } -} - export function toClassName(name) { return name && typeof name === 'string' ? name.trim().toLowerCase().replace(/[^0-9a-z]/gi, '-') diff --git a/express/styles/styles.css b/express/styles/styles.css index 034bb8a05..40e867f23 100644 --- a/express/styles/styles.css +++ b/express/styles/styles.css @@ -1065,7 +1065,7 @@ body[data-device="mobile"] main .floating-button-wrapper[data-audience="mobile"] display: block; } -main > div:not(.banner-container) .block:not(.pricing-summary, .pricing-cards, .pricing-table, .simplified-pricing-cards, .puf, .split-action, .link-list, .wayfinder, .ratings, .ribbon-banner) a.button.same-fcta { +main > div:not(.banner-container) .block:not(.pricing-summary, .pricing-cards, .pricing-table, .simplified-pricing-cards, .split-action, .link-list, .wayfinder, .ratings, .ribbon-banner) a.button.same-fcta { display: none; } @@ -1105,7 +1105,7 @@ main .long-form.grid-width-6-desktop.narrow .default-content-wrapper { text-align: left; } - main > div:not(.banner-container) .block:not(.pricing-summary, .pricing-cards, .pricing-table, .puf, .link-list, .wayfinder, .ratings) a.button.same-fcta { + main > div:not(.banner-container) .block:not(.pricing-summary, .pricing-cards, .pricing-table, .link-list, .wayfinder, .ratings) a.button.same-fcta { display: inline-block; } } diff --git a/test/unit/blocks/premium-plan/premium-plan.test.js b/test/unit/blocks/premium-plan/premium-plan.test.js deleted file mode 100644 index 35dea5820..000000000 --- a/test/unit/blocks/premium-plan/premium-plan.test.js +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-env mocha */ -/* eslint-disable no-unused-vars */ - -const { default: decorate } = await import('../../../../express/blocks/premium-plan/premium-plan.js'); diff --git a/test/unit/blocks/puf/puf.test.js b/test/unit/blocks/puf/puf.test.js deleted file mode 100644 index c32a0a192..000000000 --- a/test/unit/blocks/puf/puf.test.js +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-env mocha */ -/* eslint-disable no-unused-vars */ - -const { default: decorate } = await import('../../../../express/blocks/puf/puf.js'); diff --git a/tools/sidekick/config.json b/tools/sidekick/config.json index 0e35c5450..022c9bdcd 100644 --- a/tools/sidekick/config.json +++ b/tools/sidekick/config.json @@ -2,6 +2,7 @@ "project": "Express", "host": "www.adobe.com", "byocdn": true, + "trustedHosts": ["milo.adobe.com"], "plugins": [ { "id": "metadata",