From f7b660b8d9e805a4254151b37365f6dbeb6fdeca Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:16:48 +0100 Subject: [PATCH 01/41] Update release.yml --- .github/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release.yml b/.github/release.yml index 6809eb9b..4b96087a 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -3,7 +3,7 @@ changelog: exclude: labels: - - ignore-for-release + - Ignore for release categories: - title: Functional requirements labels: From 92186bd8d4c7185147d6fc5512a1d3b5f52a0d17 Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Mon, 27 Nov 2023 19:59:47 +0530 Subject: [PATCH 02/41] Social Block #67 (#261) --- blocks/v2-social-block/v2-social-block.css | 107 +++++++++++++++++++++ blocks/v2-social-block/v2-social-block.js | 87 +++++++++++++++++ icons/link.svg | 3 + placeholder.json | 4 + styles/styles.css | 104 ++++++++++++++++++++ 5 files changed, 305 insertions(+) create mode 100644 blocks/v2-social-block/v2-social-block.css create mode 100644 blocks/v2-social-block/v2-social-block.js create mode 100644 icons/link.svg diff --git a/blocks/v2-social-block/v2-social-block.css b/blocks/v2-social-block/v2-social-block.css new file mode 100644 index 00000000..b3f0b6e0 --- /dev/null +++ b/blocks/v2-social-block/v2-social-block.css @@ -0,0 +1,107 @@ +.redesign-v2 .section.v2-social-block-container { + padding: 40px 16px; +} + +.v2-social-block-container { + --social-block-padding: 24px 16px; + --social-block-gap: 16px; + --social-block-list-gap: 12px; +} + +.v2-social-block { + --social-link-color: var(--c-main-black); + --social-text-color: var(--c-main-black); +} + +.redesign-v2 .section .v2-social-block-wrapper { + padding: var(--social-block-padding); + border-radius: 8px; +} + +.v2-social-block-wrapper--black { + background-color: var(--c-main-black); +} + +.v2-social-block--black { + --social-link-color: var(--c-white); + --social-text-color: var(--c-grey-2); +} + +.v2-social-block-wrapper--gray { + background-color: var(--c-grey-50); +} + +.v2-social-block__list-wrapper { + display: flex; + flex-flow: column; + gap: var(--social-block-gap); + align-items: center; +} + +.v2-social-block__title { + font-family: var(--font-family-body); + font-size: var(--f-heading-5-font-size); + letter-spacing: var(--f-heading-5-letter-spacing); + line-height: var(--f-heading-5-line-height); + margin-bottom: 0; + color: var(--social-text-color); +} + +.v2-social-block__list { + list-style-type: none; + display: flex; +} + +.v2-social-block__button:any-link { + color: var(--social-link-color); + background-color: transparent; + align-items: center; + display: inline-flex; + font: var(--f-caption-font-size) / var(--f-caption-line-height) var(--font-family-body); + letter-spacing: 0.4px; + justify-content: center; + max-width: 21.5em; + height: 48px; + padding: 0 var(--social-block-gap); + border-radius: 4px; +} + +.v2-social-block__button:focus-visible { + outline: 2px solid var(--light-border-focus); + outline-offset: 1px; +} + +.v2-social-block__button .icon svg { + height: 24px; + width: 24px; +} + +@media screen and (min-width: 744px) { + .redesign-v2 .section.v2-social-block-container { + padding: 40px 32px; + } + + .v2-social-block-container { + --social-block-padding: 32px; + } + + .v2-social-block__list-wrapper { + max-width: 680px; + margin: auto; + } + + .v2-social-block__list { + gap: 16px; + } +} + +@media screen and (min-width: 1200px) { + .redesign-v2 .section.v2-social-block-container { + padding: 48px 0; + } + + .v2-social-block-container { + --social-block-padding: 48px 40px; + --social-block-gap: 32px; + } +} \ No newline at end of file diff --git a/blocks/v2-social-block/v2-social-block.js b/blocks/v2-social-block/v2-social-block.js new file mode 100644 index 00000000..3537ee72 --- /dev/null +++ b/blocks/v2-social-block/v2-social-block.js @@ -0,0 +1,87 @@ +import { getTextLabel, unwrapDivs, variantsClassesToBEM } from '../../scripts/common.js'; + +export default async function decorate(block) { + const blockName = 'v2-social-block'; + const variantClasses = ['black', 'gray']; + + if (block.classList.contains('black')) { + block.parentElement.classList.add('v2-social-block-wrapper--black'); + } else if (block.classList.contains('gray')) { + block.parentElement.classList.add('v2-social-block-wrapper--gray'); + } + + variantsClassesToBEM(block.classList, variantClasses, blockName); + + const headings = block.querySelectorAll('h1, h2, h3, h4, h5, h6'); + [...headings].forEach((heading) => heading.classList.add(`${blockName}__title`)); + + const socialWrapper = block.querySelector(':scope > div'); + socialWrapper.classList.add(`${blockName}__list-wrapper`); + + const list = socialWrapper.querySelector('ul'); + let copyAnchor; + + list.classList.add(`${blockName}__list`); + list.classList.remove('cta-list'); + + [...list.children].forEach((item) => { + item.className = ''; + + const anchor = item.querySelector('a'); + if (anchor) { + anchor.className = `${blockName}__button`; + } + + const copyLink = item.querySelector('.icon-link'); + if (copyLink) { + copyAnchor = anchor; + anchor.dataset.tooltip = getTextLabel('Copied'); + + anchor.addEventListener('click', async (e) => { + e.preventDefault(); + try { + await navigator.clipboard.writeText(`${anchor.href}`); + anchor.classList.add('show'); + + setTimeout(() => { + anchor.classList.remove('show'); + }, 1000); + } catch (err) { + /* eslint-disable-next-line no-console */ + console.error('Failed to copy: ', err); + } + }); + anchor.classList.add('tooltip', 'tooltip--top'); + } + }); + unwrapDivs(block); + + const mobileMQ = window.matchMedia('(max-width: 743px)'); + const tabletMQ = window.matchMedia('(min-width: 744px)'); + + function onMobileChange() { + copyAnchor.classList.remove('tooltip--right'); + copyAnchor.classList.add('tooltip--top'); + } + + function onTabletChange() { + copyAnchor.classList.remove('tooltip--top'); + copyAnchor.classList.add('tooltip--right'); + } + + if (mobileMQ.matches) { + onMobileChange(mobileMQ); + } + + if (tabletMQ.matches) { + onTabletChange(tabletMQ); + } + + window.addEventListener('resize', () => { + if (mobileMQ.matches) { + onMobileChange(); + } else if (tabletMQ.matches) { + onTabletChange(); + } + }); +} diff --git a/icons/link.svg b/icons/link.svg new file mode 100644 index 00000000..1c454912 --- /dev/null +++ b/icons/link.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/placeholder.json b/placeholder.json index b933674a..dd304528 100644 --- a/placeholder.json +++ b/placeholder.json @@ -142,6 +142,10 @@ { "Key": "vinformat-length", "Text": "Please fill out this field" + }, + { + "Key": "Copied", + "Text": "Copied!" } ], ":type": "sheet" diff --git a/styles/styles.css b/styles/styles.css index 37e2d0bd..00cecfbd 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1490,3 +1490,107 @@ main.blue-contract .section.section-with-title p { width: 1px; white-space: nowrap; } + +/* generic tooltip styles starts here */ +.redesign-v2 .tooltip { + position: relative; +} + +.redesign-v2 .tooltip::before, +.redesign-v2 .tooltip::after { + position: absolute; + opacity: 0; + visibility: hidden; + transition: opacity var(--duration-small) linear, visibility var(--duration-small) linear; +} + +.redesign-v2 .tooltip.show::before, +.redesign-v2 .tooltip.show::after { + opacity: 1; + visibility: visible; +} + +.redesign-v2 .tooltip::before { + content: attr(data-tooltip); + z-index: 2; + padding: 4px 8px 5px; + white-space: nowrap; + color: var(--c-main-black); + background: var(--c-white); + border-radius: 4px; + box-shadow: 0 2px 4px 0 rgb(0 0 0 / 20%), 0 0.3px 0.5px 0 rgb(0 0 0 / 10%); +} + +.redesign-v2 .tooltip::after { + content: ""; + width: 0; + height: 0; + z-index: 4; + filter: drop-shadow(0 6px 4px rgb(0 0 0 / 10%)); +} + +/* tooltip position top */ +.redesign-v2 .tooltip--top::before, +.redesign-v2 .tooltip--top::after { + bottom: 60%; + left: 50%; + transform: translate(-50%); + margin-bottom: 15px; +} + +.redesign-v2 .tooltip--top::after { + margin-bottom: 8.5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 7px solid var(--c-white); +} + +/* tooltip position right */ +.redesign-v2 .tooltip--right::before, +.redesign-v2 .tooltip--right::after { + top: 50%; + left: 60%; + transform: translate(0, -50%); + margin-left: 15px; +} + +.redesign-v2 .tooltip--right::after { + margin-left: 8.5px; + border-top: 5px solid transparent; + border-right: 7px solid var(--c-white); + border-bottom: 5px solid transparent; +} + +/* tooltip position left */ +.redesign-v2 .tooltip--left::before, +.redesign-v2 .tooltip--left::after { + top: 50%; + right: 60%; + transform: translate(0, -50%); + margin-right: 15px; +} + +.redesign-v2 .tooltip--left::after { + margin-right: 8.5px; + border-top: 5px solid transparent; + border-left: 7px solid var(--c-white); + border-bottom: 5px solid transparent; +} + +/* tooltip position bottom */ +.redesign-v2 .tooltip--bottom::before, +.redesign-v2 .tooltip--bottom::after { + top: 60%; + left: 50%; + transform: translate(-50%); + margin-top: 15px; +} + +.redesign-v2 .tooltip--bottom::after { + margin-top: 8.5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 7px solid var(--c-white); +} + +/* generic tooltip styles ends here */ \ No newline at end of file From e7e0448dc285f28fa375e6317e4bfce6ad76be46 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:30:11 +0100 Subject: [PATCH 03/41] Inpage navigation is in reversed order on Firefox #262 (#265) * fix Firefox issue with sorting * fix naming issues * refactor focus state * refactor button styles --- .../v2-inpage-navigation.css | 52 +++++++++---------- .../v2-inpage-navigation.js | 10 ++-- scripts/scripts.js | 11 ++-- styles/styles.css | 6 +++ 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/blocks/v2-inpage-navigation/v2-inpage-navigation.css b/blocks/v2-inpage-navigation/v2-inpage-navigation.css index 98095912..54fa31a2 100644 --- a/blocks/v2-inpage-navigation/v2-inpage-navigation.css +++ b/blocks/v2-inpage-navigation/v2-inpage-navigation.css @@ -90,10 +90,23 @@ then we change it for the next section item in main */ .v2-inpage-navigation__items-close { background: none; border: 0; + border-radius: 4px; color: var(--c-main-black); cursor: pointer; display: block; margin: 0 0 0 auto; + padding: 0; + width: 44px; + height: 44px; +} + +.v2-inpage-navigation__items-close svg { + display: block; + margin: auto; +} + +.v2-inpage-navigation__items-close:focus-visible { + outline: 2px solid var(--light-border-focus); } .v2-inpage-navigation__dropdown-title { @@ -125,6 +138,11 @@ then we change it for the next section item in main */ letter-spacing: var(--f-subtitle-2-letter-spacing); margin: 0; width: 100%; + border-radius: 1px; + } + +.v2-inpage-navigation__item button:focus-visible { + outline-offset: 5px; } /* stylelint-disable-next-line no-descending-specificity */ @@ -164,30 +182,10 @@ then we change it for the next section item in main */ /* END Customization when dropdown is open */ -/* Blue button */ -.v2-inpage-navigation__cta:any-link { - text-transform: uppercase; - padding: 9px 21px; - font-family: var(--font-family-body); - font-size: var(--f-button-font-size); +.redesign-v2 a.button.v2-inpage-navigation__cta:any-link { + height: auto; + padding: 8px 20px; line-height: var(--f-button-line-height); - letter-spacing: var(--f-button-letter-spacing); - border: 0; - background-color: var(--c-cta-blue-default); - color: var(--c-white); - align-items: center; - display: flex; - text-decoration: none; - border-radius: 4px; -} - -.v2-inpage-navigation__cta:hover, -.v2-inpage-navigation__cta:focus { - background-color: var(--c-cta-blue-hover); -} - -.v2-inpage-navigation__cta:active { - background-color: var(--c-cta-blue-active); } @media (min-width: 744px) { @@ -250,6 +248,9 @@ then we change it for the next section item in main */ padding: 0; display: flex; align-items: center; + + /* fix overlap issue for focus state of CTA */ + background-color: transparent; } .v2-inpage-navigation__items { @@ -289,8 +290,7 @@ then we change it for the next section item in main */ background-color: var(--c-main-black); } - /* Blue button */ - .v2-inpage-navigation__cta:any-link { - padding: 11px 21px; + .redesign-v2 a.button.v2-inpage-navigation__cta:any-link { + padding: 10px 20px; } } diff --git a/blocks/v2-inpage-navigation/v2-inpage-navigation.js b/blocks/v2-inpage-navigation/v2-inpage-navigation.js index f50d5013..40869152 100644 --- a/blocks/v2-inpage-navigation/v2-inpage-navigation.js +++ b/blocks/v2-inpage-navigation/v2-inpage-navigation.js @@ -28,7 +28,7 @@ const inpageNavigationButton = () => { const title = getMetadata('inpage-button'); const url = getMetadata('inpage-link'); const link = createElement('a', { - classes: `${blockName}__cta`, + classes: ['button', 'marketing-cta', `${blockName}__cta`], props: { href: url, title, @@ -85,7 +85,7 @@ const updateActive = (id) => { // Remove focus position document.activeElement.blur(); - // check active id is equal to id dont do anything + // check active id is equal to id don't do anything const selectedItem = document.querySelector(`.${blockName}__selected-item`); activeItemInList?.classList.remove(`${blockName}__item--active`); const itemsButton = document.querySelectorAll(`.${blockName}__items button`); @@ -119,7 +119,7 @@ const addHeaderScrollBehaviour = (header) => { }; export default async function decorate(block) { - const redButton = inpageNavigationButton(); + const ctaButton = inpageNavigationButton(); const wrapper = block.querySelector(':scope > div'); wrapper.classList.add(`${blockName}__wrapper`); @@ -182,8 +182,8 @@ export default async function decorate(block) { itemsWrapper.remove(); - if (redButton) { - wrapper.appendChild(redButton); + if (ctaButton) { + wrapper.appendChild(ctaButton); } list.addEventListener('click', gotoSection); diff --git a/scripts/scripts.js b/scripts/scripts.js index 39e0800a..f4dc5389 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -473,13 +473,10 @@ const createInpageNavigation = (main) => { // Sort the object by order const sortedObject = tabItemsObj.slice().sort((obj1, obj2) => { - if (!obj1.order) { - return 1; // Move 'a' to the end - } - if (!obj2.order) { - return -1; // Move 'b' to the end - } - return obj1.order - obj2.order; + const order1 = obj1.order ?? Infinity; // Fallback to a large number if 'order' is not present + const order2 = obj2.order ?? Infinity; + + return order1 - order2; }); // From the array of objects create the DOM diff --git a/styles/styles.css b/styles/styles.css index 00cecfbd..d839cfb8 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -608,7 +608,13 @@ main p img { .redesign-v2 a.button:focus, .redesign-v2 button:focus { + outline: 0; +} + +.redesign-v2 a.button:focus-visible, +.redesign-v2 button:focus-visible { outline: 2px solid var(--light-border-focus); + outline-offset: 2px; } /* sections */ From 06af513f3250b6d7385e3769ab0fdb972ef9fd66 Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Mon, 27 Nov 2023 20:02:13 +0530 Subject: [PATCH 04/41] Content carousel visual issues #258 (#263) * fix disable functionality for previous/next buttons * fix colour of gradient * add border radius to button for focus state --- .../v2-static-content-carousel.css | 3 ++- .../v2-static-content-carousel.js | 6 +++--- styles/styles.css | 3 +++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/blocks/v2-static-content-carousel/v2-static-content-carousel.css b/blocks/v2-static-content-carousel/v2-static-content-carousel.css index 7dbaac86..e00c4dec 100644 --- a/blocks/v2-static-content-carousel/v2-static-content-carousel.css +++ b/blocks/v2-static-content-carousel/v2-static-content-carousel.css @@ -96,6 +96,7 @@ .v2-static-content-carousel__button:disabled:hover { background: transparent; border: none; + border-radius: 4px; } .v2-static-content-carousel__button .icon-arrow-left svg, @@ -168,7 +169,7 @@ position: absolute; top: 0; pointer-events: none; - background: linear-gradient(90deg, rgb(255 255 255 / 0%) 0%, rgb(255 255 255 / 100%) 100%); + background: linear-gradient(90deg, rgb(255 255 255 / 0%) 0%, var(--background-gradient) 100%); right: 0; } } \ No newline at end of file diff --git a/blocks/v2-static-content-carousel/v2-static-content-carousel.js b/blocks/v2-static-content-carousel/v2-static-content-carousel.js index b8ad7ed0..4b1e307d 100644 --- a/blocks/v2-static-content-carousel/v2-static-content-carousel.js +++ b/blocks/v2-static-content-carousel/v2-static-content-carousel.js @@ -8,7 +8,7 @@ const updateActiveClass = (elements, targetElement, carousel) => { elements.forEach((el, index) => { if (el === targetElement) { el.classList.add('active'); - let arrowControl = document.querySelector(`.${blockName}__button:disabled`); + let arrowControl = carousel.nextElementSibling.querySelector(`.${blockName}__button:disabled`); if (arrowControl) { arrowControl.disabled = false; @@ -16,9 +16,9 @@ const updateActiveClass = (elements, targetElement, carousel) => { } // disable arrow buttons if (index === 0) { - arrowControl = document.querySelector(`.${blockName}__button-prev`); + arrowControl = carousel.nextElementSibling.querySelector(`.${blockName}__button-prev`); } else if (index === el.parentNode.children.length - 1) { - arrowControl = document.querySelector(`.${blockName}__button-next`); + arrowControl = carousel.nextElementSibling.querySelector(`.${blockName}__button-next`); } if (arrowControl) { arrowControl.disabled = true; diff --git a/styles/styles.css b/styles/styles.css index d839cfb8..da6f3e44 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1003,6 +1003,7 @@ main.blue-contract .section.section-with-title p { --card-text: var(--c-main-black); --text-color: var(--c-main-black); --text-subtle: var(--c-grey-4); + --background-gradient: rgb(255 255 255 / 100%); font-size: 16px; } @@ -1056,6 +1057,7 @@ main.blue-contract .section.section-with-title p { --card-text: var(--c-white); --c-cta-blue-default: #44A1FF; --text-subtle: var(--c-grey-2); + --background-gradient: rgb(20 20 20 / 100%); color: var(--text-color); background-color: var(--c-main-black); @@ -1065,6 +1067,7 @@ main.blue-contract .section.section-with-title p { .section--gray-background { --card-background: var(--c-white); --card-text: var(--c-main-black); + --background-gradient: rgb(247 247 247 / 100%); background-color: var(--c-grey-50); } From 699d2d4aeb73b34e4904655aa7595dcb6b2f1f66 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:46:26 +0100 Subject: [PATCH 05/41] Modal functionality #76 (#256) * Add modal top bar * Add opening the links with modal link * Refactor the modal background color * Improve modal accessibility --------- Authored-by: Tomasz Dziezyk --- .../v2-stories-carousel.css | 2 +- blocks/v2-truck-lineup/v2-truck-lineup.css | 7 +- blocks/v2-video/v2-video.css | 2 +- common/modal/modal.css | 112 ++++++++++++++++-- common/modal/modal.js | 100 ++++++++++++++-- placeholder.json | 22 ++-- scripts/scripts.js | 54 ++++++++- scripts/video-helper.js | 6 +- styles/styles.css | 48 +++++++- 9 files changed, 319 insertions(+), 34 deletions(-) diff --git a/blocks/v2-stories-carousel/v2-stories-carousel.css b/blocks/v2-stories-carousel/v2-stories-carousel.css index ed858458..f0a57053 100644 --- a/blocks/v2-stories-carousel/v2-stories-carousel.css +++ b/blocks/v2-stories-carousel/v2-stories-carousel.css @@ -5,7 +5,7 @@ } /* Full width block */ -main .section.v2-stories-carousel-container .v2-stories-carousel-wrapper { +body .section.v2-stories-carousel-container .v2-stories-carousel-wrapper { margin: 0; padding-left: 0; padding-right: 0; diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index c1d623d1..3162b8ab 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -23,7 +23,7 @@ } /* Full width block */ -main .section.v2-truck-lineup-container .v2-truck-lineup-wrapper { +body .section.v2-truck-lineup-container .v2-truck-lineup-wrapper { margin: 0; padding: 0; width: 100%; @@ -303,12 +303,14 @@ ul.v2-truck-lineup__navigation { max-width: 931px; } + div:where([role="dialog"]):not(.truck-lineup-content-center) .v2-truck-lineup__content .default-content-wrapper, main:not(.truck-lineup-content-center) .v2-truck-lineup__content .default-content-wrapper { align-items: flex-start; display: grid; grid-template-columns: 1fr 567px 80px 430px 1fr; } + div:where([role="dialog"]):not(.truck-lineup-content-center) .v2-truck-lineup__text, main:not(.truck-lineup-content-center) .v2-truck-lineup__text { width: 567px; max-width: none; @@ -318,11 +320,14 @@ ul.v2-truck-lineup__navigation { grid-column: 2; } + div:where([role="dialog"]):not(.truck-lineup-content-center) .v2-truck-lineup__buttons-container, main:not(.truck-lineup-content-center) .v2-truck-lineup__buttons-container, + div:where([role="dialog"]):not(.truck-lineup-content-center) .v2-truck-lineup__buttons-container .button-container, main:not(.truck-lineup-content-center) .v2-truck-lineup__buttons-container .button-container { margin-top: 0; } + div:where([role="dialog"]):not(.truck-lineup-content-center) .v2-truck-lineup__buttons-container, main:not(.truck-lineup-content-center) .v2-truck-lineup__buttons-container { justify-content: start; grid-column: 4; diff --git a/blocks/v2-video/v2-video.css b/blocks/v2-video/v2-video.css index fd7e44ec..bc921eb5 100644 --- a/blocks/v2-video/v2-video.css +++ b/blocks/v2-video/v2-video.css @@ -7,7 +7,7 @@ margin-top: -40px; } -main .section.v2-video-container .v2-video-wrapper { +body .section.v2-video-container .v2-video-wrapper { padding: 0; color: var(--c-white); margin: 0; diff --git a/common/modal/modal.css b/common/modal/modal.css index a5cd03f2..98a4728a 100644 --- a/common/modal/modal.css +++ b/common/modal/modal.css @@ -4,10 +4,32 @@ left: 0; width: 100vw; height: 100vh; - background: white; + background: var(--background-color); + color: var(--text-color); z-index: 1051; - transition: opacity 0.15s linear, visibility 0.15s linear; + transition: opacity var(--duration-small) var(--easing-entrance), + visibility var(--duration-small) var(--easing-entrance); opacity: 1; + + --background-color: var(--c-white); + --top-bar-height: 64px; + --text-color: var(--c-black); +} + +@supports (height: 1svh) { + .modal-background { + height: 100svh; + } +} + +.modal--black { + --background-color: var(--c-black); + --text-color: var(--c-white); + --color-icon: var(--c-white); +} + +.modal--gray { + --background-color: var(--c-grey-50); } .modal-hidden { @@ -29,6 +51,65 @@ background-color: var(--c-grey-1); } +.modal-content--wide { + margin: 0; + overflow: auto; + height: calc(100% - var(--top-bar-height)); + background-color: var(--background-color); +} + +.modal-top-bar { + display: flex; + justify-content: center; +} + +.modal-top-bar-content { + display: flex; + justify-content: space-between; + margin: 0 16px; + gap: 10px; + min-height: var(--top-bar-height); + align-items: flex-start; + width: 100%; + padding: 32px 0; +} + +.modal-top-bar-heading { + font-family: var(--ff-volvo-novum); + font-size: var(--f-subtitle-1-font-size); + line-height: var(--f-subtitle-1-line-height); + letter-spacing: var(--f-subtitle-1-letter-spacing); + align-self: center; + margin: 0; +} + +.modal-top-bar .modal-close-button { + padding: 0; + margin: 0; + border: 0; + background: inherit; + display: flex; + border-radius: 1px; + min-height: 44px; + min-width: 44px; + justify-content: center; + align-items: center; +} + +.modal-top-bar .modal-close-button:focus { + outline: 0; +} + +.modal-top-bar .modal-close-button:focus-visible { + outline: 2px solid var(--light-border-focus); + outline-offset: 5px; +} + +.modal-top-bar svg { + height: 24px; + width: 24px; +} + .modal-content .modal-video { width: 100%; height: 100%; @@ -38,7 +119,7 @@ .modal-before-banner { display: flex; justify-content: center; - background: white; + background: var(--background-color); } .modal-before-banner button.modal-close-button { @@ -52,24 +133,41 @@ } @media (min-width: 768px) { - .modal-content { + :root:not(.redesign-v2) .modal-content { margin: 100px auto; width: 726px; } } @media (min-width: 992px) { - .modal-content { + :root:not(.redesign-v2) .modal-content { width: 930px; } } @media (min-width: 1300px) { - .modal-content { + :root:not(.redesign-v2) .modal-content { width: 1170px; } } +@media (min-width: 744px) { + .modal-top-bar-content { + margin: 0 32px; + } + + .modal-top-bar-heading { + font-size: var(--f-heading-6-font-size); + line-height: var(--f-heading-6-line-height); + letter-spacing: var(--f-heading-6-letter-spacing); + } +} + +@media (min-width: 1200px) { + .modal-top-bar-content { + max-width: 1170px; + } +} /* adjustments for soundcloud variant of modal, e.g. https://www.volvotrucks.us/trucks/powertrain/i-torque/ */ .modal-content .modal-soundcloud { @@ -81,7 +179,7 @@ gap: 2%; } -.modal-content:has(.modal-soundcloud) { +.modal-content:has(.modal-soundcloud) { aspect-ratio: unset; } diff --git a/common/modal/modal.js b/common/modal/modal.js index 8b57050b..853f3957 100644 --- a/common/modal/modal.js +++ b/common/modal/modal.js @@ -1,3 +1,4 @@ +import { createElement, getTextLabel } from '../../scripts/common.js'; import { loadCSS } from '../../scripts/lib-franklin.js'; // eslint-disable-next-line import/no-cycle import { createIframe, isLowResolutionVideoUrl } from '../../scripts/video-helper.js'; @@ -7,11 +8,33 @@ const styles$ = new Promise((r) => { }); const HIDE_MODAL_CLASS = 'modal-hidden'; +let currentModalClasses = null; +let currentInvokeContext = null; + +const createModalTopBar = (parentEl) => { + const topBar = document.createRange().createContextualFragment(` + + `); + + parentEl.prepend(...topBar.children); + // eslint-disable-next-line no-use-before-define + parentEl.querySelector('.modal-close-button').addEventListener('click', () => hideModal()); + parentEl.querySelector('.modal-top-bar').addEventListener('click', (event) => event.stopPropagation()); +}; const createModal = () => { - const modalBackground = document.createElement('div'); + const modalBackground = createElement('div', { classes: ['modal-background', HIDE_MODAL_CLASS] }); + modalBackground.setAttribute('role', 'dialog'); - modalBackground.classList.add('modal-background', HIDE_MODAL_CLASS); modalBackground.addEventListener('click', () => { // eslint-disable-next-line no-use-before-define hideModal(); @@ -24,8 +47,8 @@ const createModal = () => { } }; - const modalContent = document.createElement('div'); - modalContent.classList.add('modal-content'); + const modalContent = createElement('div', { classes: ['modal-content'] }); + createModalTopBar(modalBackground); modalBackground.appendChild(modalContent); // preventing initial animation when added to DOM modalBackground.style = 'display: none'; @@ -36,25 +59,66 @@ const createModal = () => { event.stopPropagation(); }); - async function showModal(newUrl, beforeBanner, beforeIframe) { + const clearModalContent = () => { + modalContent.innerHTML = ''; + modalContent.className = 'modal-content'; + modalBackground.querySelector('.modal-top-bar-heading').textContent = ''; + }; + + const handleNewContent = (newContent) => { + clearModalContent(); + + const firstSection = newContent[0]; + + // checking if the first section contains one heading only + if ( + firstSection.children.length === 1 + && firstSection.children[0].children.length === 1 + && /^H[1-6]$/.test(newContent[0].children[0].children[0].tagName) + ) { + const headingContent = firstSection.children[0].children[0].textContent; + + modalBackground.querySelector('.modal-top-bar-heading').textContent = headingContent; + firstSection.style.display = 'none'; + modalBackground.setAttribute('aria-labelledby', 'modal-heading'); + } else { + modalBackground.removeAttribute('aria-labelledby'); + } + + modalContent.classList.add('modal-content--wide'); + modalContent.append(...newContent); + }; + + async function showModal(newContent, { + beforeBanner, beforeIframe, modalClasses = [], invokeContext, + }) { + currentInvokeContext = invokeContext; + // disabling focus for header, footer and main elements when modal is open + document.querySelectorAll('header, footer, main').forEach((el) => { + el.setAttribute('inert', 'inert'); + }); await styles$; modalBackground.style = ''; + modalBackground.classList.add(...modalClasses); + currentModalClasses = modalClasses; window.addEventListener('keydown', keyDownAction); - if (newUrl) { + if (newContent && (typeof newContent !== 'string')) { + handleNewContent(newContent); + } else if (newContent) { let videoOrIframe = null; - if (isLowResolutionVideoUrl(newUrl)) { + if (isLowResolutionVideoUrl(newContent)) { // We can't use the iframe for videos, because if the Content-Type // `application/octet-stream` is returned instead of `video/mp4`, the // file is downloaded instead of displayed. So we use the video element instead. videoOrIframe = document.createElement('video'); - videoOrIframe.setAttribute('src', newUrl); + videoOrIframe.setAttribute('src', newContent); videoOrIframe.setAttribute('controls', ''); videoOrIframe.setAttribute('autoplay', ''); videoOrIframe.classList.add('modal-video'); modalContent.append(videoOrIframe); } else { - videoOrIframe = createIframe(newUrl, { parentEl: modalContent, classes: 'modal-video' }); + videoOrIframe = createIframe(newContent, { parentEl: modalContent, classes: 'modal-video' }); } if (beforeBanner) { @@ -82,19 +146,35 @@ const createModal = () => { modalContent.classList.add('modal-content-fade-in'); modalBackground.classList.remove(HIDE_MODAL_CLASS); + modalBackground.querySelector('.modal-top-bar .modal-close-button').focus(); + modalBackground.setAttribute('aria-hidden', 'false'); // disable page scrolling document.body.classList.add('disable-scroll'); } function hideModal() { + // restoring focus for header, footer and main elements when modal is close + document.querySelectorAll('header, footer, main').forEach((el) => { + el.removeAttribute('inert'); + }); + if (currentInvokeContext) { + currentInvokeContext.focus(); + } + currentInvokeContext = null; modalBackground.classList.add(HIDE_MODAL_CLASS); modalContent.classList.remove('modal-content-fade-in'); window.removeEventListener('keydown', keyDownAction); document.body.classList.remove('disable-scroll'); - modalContent.querySelector('iframe, video').remove(); + modalContent.querySelector('iframe, video')?.remove(); modalContent.querySelector('.modal-before-banner')?.remove(); modalContent.querySelector('.modal-before-iframe')?.remove(); + modalBackground.setAttribute('aria-hidden', 'true'); + + if (currentModalClasses.length) { + modalBackground.classList.remove(currentModalClasses); + currentModalClasses = null; + } } return { diff --git a/placeholder.json b/placeholder.json index dd304528..55fdc4a7 100644 --- a/placeholder.json +++ b/placeholder.json @@ -1,7 +1,7 @@ { - "total": 30, + "total": 36, "offset": 0, - "limit": 30, + "limit": 36, "data": [ { "Key": "Low resolution video message", @@ -54,11 +54,11 @@ { "Key": "vinlabel", "Text": "Enter your 17-digit VIN (Vehicle Identification Number)" - }, + }, { "Key": "submit", "Text": "Submit" - }, + }, { "Key": "vinformat", "Text": "The VIN entered is not valid for Volvo Trucks, Prevost, Nova Bus, or Volvo Bus vehicles" @@ -66,7 +66,7 @@ { "Key": "result text", "Text": "${count} recalls available for \"${vin}\" VIN" - }, + }, { "Key": "recalls", "Text": "Recalls" @@ -86,7 +86,7 @@ { "Key": "remedy_description", "Text": "Remedy" - }, + }, { "Key": "published_info", "Text": "Information last updated" @@ -102,11 +102,11 @@ { "Key": "recall-incomplete", "Text": "Recall Incomplete" - }, + }, { "Key": "recall-incomplete-no-remedy", "Text": "Recall In Complete, remedy not available" - }, + }, { "Key": "loading recalls", "Text": " Loading Recalls ....." @@ -118,7 +118,7 @@ { "Key": "mfr_notes", "Text": "Next Steps" - }, + }, { "Key": "mfr_recall_number", "Text": "Brand Recall Number" @@ -143,6 +143,10 @@ "Key": "vinformat-length", "Text": "Please fill out this field" }, + { + "Key": "Close", + "Text": "Close" + }, { "Key": "Copied", "Text": "Copied!" diff --git a/scripts/scripts.js b/scripts/scripts.js index f4dc5389..2383881c 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -11,6 +11,7 @@ import { getMetadata, toClassName, getHref, + loadBlocks, } from './lib-franklin.js'; import { @@ -390,6 +391,49 @@ function decorateHyperlinkImages(container) { }); } +document.addEventListener('open-modal', (event) => { + // eslint-disable-next-line import/no-cycle + import('../common/modal/modal.js').then((modal) => { + const variantClasses = ['black', 'gray']; + const modalClasses = [...event.detail.target.closest('.section').classList].filter((el) => el.startsWith('modal-')); + // changing the modal variants classes to BEM naming + variantClasses.forEach((variant) => { + const index = modalClasses.findIndex((el) => el === `modal-${variant}`); + + if (index >= 0) { + modalClasses[index] = modalClasses[index].replace('modal-', 'modal--'); + } + }); + + modal.showModal(event.detail.content, { modalClasses, invokeContext: event.detail.target }); + }); +}); + +const handleModalLinks = (link) => { + link.addEventListener('click', async (event) => { + event.preventDefault(); + const modalContentLink = link.getAttribute('data-modal-content'); + const resp = await fetch(`${modalContentLink}.plain.html`); + const main = document.createElement('main'); + + if (resp.ok) { + main.innerHTML = await resp.text(); + // eslint-disable-next-line no-use-before-define + decorateMain(main, main); + await loadBlocks(main); + } + + const modalEvent = new CustomEvent('open-modal', { + detail: { + content: main.children, + target: event.target, + }, + }); + + document.dispatchEvent(modalEvent, { bubbles: true }); + }); +}; + export function decorateLinks(block) { [...block.querySelectorAll('a')] .filter(({ href }) => !!href) @@ -402,6 +446,14 @@ export function decorateLinks(block) { if (isSoundcloudLink(link)) { addSoundcloudShowHandler(link); } + if (link.getAttribute('href').startsWith('/modal-content=')) { + const href = link.getAttribute('href'); + link.setAttribute('data-modal-content', href.split('modal-content=')[1]); + // removing the `/modal-content=` so the link can be opened in the other tab by coping link + link.setAttribute('href', link.getAttribute('href').replace('/modal-content=', '')); + handleModalLinks(link); + return; + } const url = new URL(link.href); const external = !url.host.match('volvotrucks.(us|ca)') && !url.host.match('.hlx.(page|live)') && !url.host.match('localhost'); @@ -581,7 +633,7 @@ export const MEDIA_BREAKPOINTS = { DESKTOP: 'DESKTOP', }; -export function getImageForBreakpoint(imagesList, onChange = () => {}) { +export function getImageForBreakpoint(imagesList, onChange = () => { }) { const mobileMQ = window.matchMedia('(max-width: 743px)'); const tabletMQ = window.matchMedia('(min-width: 744px) and (max-width: 1199px)'); const desktopMQ = window.matchMedia('(min-width: 1200px)'); diff --git a/scripts/video-helper.js b/scripts/video-helper.js index d4b880e3..54a104dd 100644 --- a/scripts/video-helper.js +++ b/scripts/video-helper.js @@ -2,7 +2,7 @@ import { createElement, getTextLabel } from './common.js'; /* video helpers */ export function isLowResolutionVideoUrl(url) { - return url.split('?')[0].endsWith('.mp4'); + return (typeof url === 'string') && url.split('?')[0].endsWith('.mp4'); } export function isVideoLink(link) { @@ -49,7 +49,7 @@ export function showVideoModal(linkUrl) { beforeBanner = createLowResolutionBanner(); } - modal.showModal(linkUrl, beforeBanner); + modal.showModal(linkUrl, { beforeBanner }); }); } @@ -91,7 +91,7 @@ export function addSoundcloudShowHandler(link) { episodeInfo.querySelector('h2').innerText = title?.innerText || ''; episodeInfo.querySelector('p').innerText = text?.innerText || ''; - modal.showModal(link.getAttribute('href'), null, episodeInfo); + modal.showModal(link.getAttribute('href'), { beforeIframe: episodeInfo }); }); }); } diff --git a/styles/styles.css b/styles/styles.css index da6f3e44..c5919308 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -348,10 +348,12 @@ header .sub-nav.block { } /* specific page styles */ +div:where([role="dialog"]).center, main.center { text-align: center; } +div:where([role="dialog"]).center :where(.block), main.center :where(.block) { text-align: initial; } @@ -538,7 +540,7 @@ input[type='text'], select, textarea { } /* commont elements */ - +div:where([role="dialog"]) input, main input { font-size: 1.25rem; width: 100%; @@ -553,10 +555,12 @@ main input { background-color: var(--c-white); } +div:where([role="dialog"]) input:hover, main input:hover { border: 1px solid var(--text-color); } +div:where([role="dialog"]) pre, main pre { background-color: var(--overlay-background-color); padding: 1em; @@ -565,6 +569,7 @@ main pre { white-space: pre; } +div:where([role="dialog"]) blockquote, main blockquote { font-style: italic; margin: 3rem; @@ -572,11 +577,13 @@ main blockquote { hanging-punctuation: first; } +div:where([role="dialog"]) blockquote p::before, main blockquote p::before { content: "“"; line-height: 0; } +div:where([role="dialog"]) blockquote p::after, main blockquote p::after { content: "”"; line-height: 0; @@ -589,12 +596,14 @@ hr { border-bottom: 1px solid var(--overlay-background-color); } +div:where([role="dialog"]) img, main img { max-width: 100%; height: auto; vertical-align: middle } +div:where([role="dialog"]) p img, main p img { display: block; margin: auto; @@ -619,12 +628,14 @@ main p img { /* sections */ +div:where([role="dialog"]) .section > div, main .section > div { width: var(--wrapper-width); padding: 0 16px; margin: 48px auto; } +div:where([role="dialog"]) .section-with-background, main .section-with-background { --text-color: white; @@ -638,16 +649,21 @@ main .section-with-background { padding: 48px 0; } +div:where([role="dialog"]) .section-with-background > div:first-of-type, main .section-with-background > div:first-of-type, +div:where([role="dialog"]) .section-with-background > div:first-of-type > :first-child, main .section-with-background > div:first-of-type > :first-child { margin-top: 0; } +div:where([role="dialog"]) .section-with-background > div:last-of-type, main .section-with-background > div:last-of-type, +div:where([role="dialog"]) .section-with-background > div:last-of-type > :last-child, main .section-with-background > div:last-of-type > :last-child { margin-bottom: 0; } +div:where([role="dialog"]) .section-with-background > picture:last-of-type img, main .section-with-background > picture:last-of-type img { width: var(--wrapper-width); height: 100%; @@ -660,16 +676,20 @@ main .section-with-background > picture:last-of-type img { object-fit: cover; } +div:where([role="dialog"]) .section:first-child > :first-child, main .section:first-child > :first-child { margin-top: 0; } /* progressive section appearance */ +div:where([role="dialog"]) .section[data-section-status='loading'], main .section[data-section-status='loading'], +div:where([role="dialog"]) .section[data-section-status='initialized'], main .section[data-section-status='initialized'] { display: none; } +div:where([role="dialog"]) .section.highlight, main .section.highlight { background-color: var(--highlight-background-color); } @@ -699,21 +719,25 @@ ul.cta-list li { margin-top: -10px; } +div:where([role="dialog"]) .section.tabbed-container-full-width > div, main .section.tabbed-container-full-width > div { padding: 0; width: unset; } +div:where([role="dialog"]) .section.tabbed-container-full-width, main .section.tabbed-container-full-width { --horizontal-padding: calc((100% - var(--wrapper-width)) / 2); } +div:where([role="dialog"]) .section.tabbed-container ul.tab-navigation, main .section.tabbed-container ul.tab-navigation { padding: 0; margin: 25px; text-align: center; } +div:where([role="dialog"]) .section.tabbed-container ul.tab-navigation li, main .section.tabbed-container ul.tab-navigation li { margin: 0 auto; padding: 0 1% 8px; @@ -721,6 +745,7 @@ main .section.tabbed-container ul.tab-navigation li { width: 35%; } +div:where([role="dialog"]) .section.tabbed-container ul.tab-navigation button, main .section.tabbed-container ul.tab-navigation button { background: 0 0; border: none; @@ -881,6 +906,7 @@ div.block div.pager .pagination .scroll li a { } @media screen and (min-width: 480px) { + div:where([role="dialog"]) .section.tabbed-container ul.tab-navigation li, main .section.tabbed-container ul.tab-navigation li { margin: 0 3%; display: inline-block; @@ -902,6 +928,7 @@ div.block div.pager .pagination .scroll li a { --sub-nav-height: 48px; } + :root:not(.redesign-v2) div:where([role="dialog"]):not(.remove-top-margin) .section:first-child > :first-child, :root:not(.redesign-v2) main:not(.remove-top-margin) .section:first-child > :first-child { margin-top: 30px; } @@ -911,20 +938,24 @@ div.block div.pager .pagination .scroll li a { } } +div:where([role="dialog"]).blue-contract .section, main.blue-contract .section { padding: 40px 0; } +div:where([role="dialog"]).blue-contract .section > div, main.blue-contract .section > div { padding: 0 16px; margin: auto; } @media screen and (min-width: 768px) { + div:where([role="dialog"]).blue-contract .section, main.blue-contract .section { padding: 80px 0; } + div:where([role="dialog"]).blue-contract .section > div, main.blue-contract .section > div { padding: 0 32px; margin: auto; @@ -932,6 +963,7 @@ main.blue-contract .section > div { } @media screen and (min-width: 1024px) { + div:where([role="dialog"]).blue-contract .section > div, main.blue-contract .section > div { padding: 0 135px; } @@ -945,12 +977,15 @@ main.blue-contract .section > div { /* Sections variant styling */ @media (min-width: 1440px) { + :root:not(html.redesign-v2) div:where([role="dialog"]).blue-contract, :root:not(html.redesign-v2) main.blue-contract { --wrapper-width: 1440px; } } +div:where([role="dialog"]).blue-contract .section.section-with-title h1, main.blue-contract .section.section-with-title h1, +div:where([role="dialog"]).blue-contract .section.section-with-title h2, main.blue-contract .section.section-with-title h2 { font-size: var(--heading-ml-font-size); line-height: var(--heading-ml-line-height); @@ -958,6 +993,7 @@ main.blue-contract .section.section-with-title h2 { margin: 0 0 16px; } +div:where([role="dialog"]).blue-contract .section.section-with-title p, main.blue-contract .section.section-with-title p { font-size: var(--body-regular-font-size); line-height: var(--body-regular-line-height); @@ -966,32 +1002,39 @@ main.blue-contract .section.section-with-title p { } @media screen and (min-width: 768px) { + div:where([role="dialog"]).blue-contract .section.section-with-title h1, main.blue-contract .section.section-with-title h1, + div:where([role="dialog"]).blue-contract .section.section-with-title h2, main.blue-contract .section.section-with-title h2 { font-size: var(--heading-l-font-size); line-height: var(--heading-l-line-height); letter-spacing: var(--heading-l-letter-spacing); } + div:where([role="dialog"]).blue-contract .section.section-with-title p, main.blue-contract .section.section-with-title p { font-size: var(--heading-m-font-size); line-height: var(--heading-m-line-height); letter-spacing: var(--heading-m-letter-spacing); } + div:where([role="dialog"]).blue-contract .section.section-with-title > p:last-of-type, main.blue-contract .section.section-with-title > p:last-of-type { margin: 0 0 40px; } } @media screen and (min-width: 1024px) { + div:where([role="dialog"]).blue-contract .section.section-with-title h1, main.blue-contract .section.section-with-title h1, + div:where([role="dialog"]).blue-contract .section.section-with-title h2, main.blue-contract .section.section-with-title h2 { font-size: var(--heading-xxl-font-size); line-height: var(--heading-xxl-line-height); letter-spacing: var(--heading-xxl-letter-spacing); } + div:where([role="dialog"]).blue-contract .section.section-with-title > p:last-of-type, main.blue-contract .section.section-with-title > p:last-of-type { margin: 0 0 80px; } @@ -1373,6 +1416,7 @@ main.blue-contract .section.section-with-title p { } /* common elements */ +.redesign-v2 div:where([role="dialog"]) label, .redesign-v2 main label { color: var(--c-grey-4); font-family: var(--font-family-heading); @@ -1381,6 +1425,7 @@ main.blue-contract .section.section-with-title p { line-height: var(--f-subtitle-2-line-height); } +.redesign-v2 div:where([role="dialog"]) input, .redesign-v2 main input { color: var(--c-grey-4); padding: 16px; @@ -1392,6 +1437,7 @@ main.blue-contract .section.section-with-title p { line-height: var(--f-subtitle-2-line-height); } +.redesign-v2 div:where([role="dialog"]) input:hover, .redesign-v2 main input:hover { border-color: var(--c-grey-2); } From 9f010be1d254d2481620aa16e98fb31e977173da Mon Sep 17 00:00:00 2001 From: manuel-vara <116451851+manuel-vara@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:44:19 +0100 Subject: [PATCH 06/41] Subscribe to newsletter form response #245 (#253) * Pardot form block - callback response * Updated with placeholders Thank you & Error messages * Include honeypot pardot extra field --- blocks/pardot-form/pardot-form.css | 24 ++ blocks/pardot-form/pardot-form.js | 394 ++++++++++++++++++++++++ blocks/pardot-form/responses/error.js | 2 + blocks/pardot-form/responses/success.js | 2 + blocks/v2-forms/responses/error.js | 2 + blocks/v2-forms/responses/success.js | 2 + blocks/v2-forms/v2-forms.js | 59 +++- blocks/v2-newsletter/v2-newsletter.css | 6 +- blocks/v2-newsletter/v2-newsletter.js | 47 ++- 9 files changed, 527 insertions(+), 11 deletions(-) create mode 100644 blocks/pardot-form/pardot-form.css create mode 100644 blocks/pardot-form/pardot-form.js create mode 100644 blocks/pardot-form/responses/error.js create mode 100644 blocks/pardot-form/responses/success.js create mode 100644 blocks/v2-forms/responses/error.js create mode 100644 blocks/v2-forms/responses/success.js diff --git a/blocks/pardot-form/pardot-form.css b/blocks/pardot-form/pardot-form.css new file mode 100644 index 00000000..660283cb --- /dev/null +++ b/blocks/pardot-form/pardot-form.css @@ -0,0 +1,24 @@ +.pardot-form__floating-label-group { + margin-bottom: 35px; + margin-top: 35px; + position: relative; + } + +.pardot-form__floating-label { + left: var(--input-padding); + pointer-events: none; + position: absolute; + top: 50%; + transform: translateY(-50%); + transition: + top var(--duration-small) var(--easing-standard), + left var(--duration-small) var(--easing-standard); + } + +.pardot-form__floating-label-group input:focus ~ .pardot-form__floating-label, +.pardot-form__floating-label-group input:not(:placeholder-shown) ~ .pardot-form__floating-label, +.pardot-form__floating-label-group input:not(:focus):valid ~ .pardot-form__floating-label { + bottom: 0; + left: 0; + top: 3px; + } \ No newline at end of file diff --git a/blocks/pardot-form/pardot-form.js b/blocks/pardot-form/pardot-form.js new file mode 100644 index 00000000..65ce162b --- /dev/null +++ b/blocks/pardot-form/pardot-form.js @@ -0,0 +1,394 @@ +import { loadScript, sampleRUM } from '../../scripts/lib-franklin.js'; +import { getTextLabel } from '../../scripts/common.js'; + +const successMessage = `

${getTextLabel('Successful submission title')}

+

${getTextLabel('Successful submission text')}

+`; + +const errorMessage = `

${getTextLabel('Error submission title')}

+

${getTextLabel('Error submission text')}

+`; + +// Form Block identifies the submit endpoint via these rules and in order +// 1. action property on the submit button +// 2. SUBMIT_ACTION constant +// 3. the path of the spreadsheet +const SUBMIT_ACTION = ''; + +async function submissionSuccess() { + sampleRUM('form:submit'); + const successDiv = document.createElement('div'); + successDiv.innerHTML = successMessage; + const form = document.querySelector('form[data-submitting=true]'); + form.setAttribute('data-submitting', 'false'); + form.replaceWith(successDiv); +} + +async function submissionFailure() { + const errorDiv = document.createElement('div'); + errorDiv.innerHTML = errorMessage; + const form = document.querySelector('form[data-submitting=true]'); + form.setAttribute('data-submitting', 'false'); + form.querySelector('button[type="submit"]').disabled = false; + form.replaceWith(errorDiv); +} + +function serialize(obj) { + const str = Object.keys(obj).map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`); + return str.join('&'); +} + +window.logResult = function logResult(json) { + if (json.result === 'success') { + submissionSuccess(); + } else if (json.result === 'error') { + submissionFailure(); + } +}; + +function generateUnique() { + return new Date().valueOf() + Math.random(); +} + +function constructPayload(form) { + const payload = { __id__: generateUnique() }; + [...form.elements].forEach((fe) => { + if (fe.name) { + if (fe.type === 'radio') { + if (fe.checked) payload[fe.name] = fe.value; + } else if (fe.type === 'checkbox') { + if (fe.checked) payload[fe.name] = payload[fe.name] ? `${payload[fe.name]},${fe.value}` : fe.value; + } else if (fe.type !== 'file') { + payload[fe.name] = fe.value; + } + } + }); + payload.callback = 'logResult'; + return { payload }; +} + +async function prepareRequest(form) { + const { payload } = constructPayload(form); + const url = form.dataset.action; + + const serializedData = serialize(payload); + loadScript(`${url}?${serializedData}`, { type: 'text/javascript', charset: 'UTF-8' }); +} + +async function handleSubmit(form) { + if (form.getAttribute('data-submitting') !== 'true') { + form.setAttribute('data-submitting', 'true'); + await prepareRequest(form); + } +} + +function setPlaceholder(element, fd) { + if (fd.Placeholder) { + element.setAttribute('placeholder', fd.Placeholder); + } +} + +const constraintsDef = Object.entries({ + 'email|text': [['Max', 'maxlength'], ['Min', 'minlength']], + 'number|range|date': ['Max', 'Min', 'Step'], + file: ['Accept', 'Multiple'], + fieldset: ['Max', 'Min'], +}).flatMap(([types, constraintDef]) => types.split('|') + .map((type) => [type, constraintDef.map((cd) => (Array.isArray(cd) ? cd : [cd, cd]))])); + +const constraintsObject = Object.fromEntries(constraintsDef); + +function setConstraints(element, fd) { + const constraints = constraintsObject[fd.Type]; + if (constraints) { + constraints + .filter(([nm]) => fd[nm]) + .forEach(([nm, htmlNm]) => { + element.setAttribute(htmlNm, fd[nm]); + }); + } +} + +function createLabel(fd, tagName = 'label') { + const label = document.createElement(tagName); + label.setAttribute('for', fd.Id); + label.className = 'field-label'; + label.textContent = fd.Label || ''; + if (fd.Tooltip) { + label.title = fd.Tooltip; + } + return label; +} + +function createHelpText(fd) { + const div = document.createElement('div'); + div.className = 'field-description'; + div.setAttribute('aria-live', 'polite'); + div.innerText = fd.Description; + div.id = `${fd.Id}-description`; + return div; +} + +function createFieldWrapper(fd, tagName = 'div') { + const fieldWrapper = document.createElement(tagName); + const nameStyle = fd.Name ? ` form-${fd.Name}` : ''; + const fieldId = `form-${fd.Type}-wrapper${nameStyle}`; + fieldWrapper.className = fieldId; + if (fd.Fieldset) { + fieldWrapper.dataset.fieldset = fd.Fieldset; + } + if (fd.Mandatory.toLowerCase() === 'true') { + fieldWrapper.dataset.required = ''; + } + fieldWrapper.classList.add('field-wrapper'); + fieldWrapper.append(createLabel(fd)); + return fieldWrapper; +} + +function createButton(fd) { + const wrapper = createFieldWrapper(fd); + const button = document.createElement('button'); + button.textContent = fd.Label; + button.type = fd.Type; + if (button.type === 'submit' && fd.Action) { + button.formAction = fd.Action; + } + button.classList.add('button'); + button.dataset.redirect = fd.Extra || ''; + button.id = fd.Id; + button.name = fd.Name; + wrapper.replaceChildren(button); + return wrapper; +} +function createSubmit(fd) { + const wrapper = createButton(fd); + return wrapper; +} + +function createInput(fd) { + const input = document.createElement('input'); + input.type = fd.Type; + setPlaceholder(input, fd); + setConstraints(input, fd); + return input; +} + +const withFieldWrapper = (element) => (fd) => { + const wrapper = createFieldWrapper(fd); + wrapper.append(element(fd)); + return wrapper; +}; + +const createTextArea = withFieldWrapper((fd) => { + const input = document.createElement('textarea'); + setPlaceholder(input, fd); + return input; +}); + +const createSelect = withFieldWrapper((fd) => { + const select = document.createElement('select'); + if (fd.Placeholder) { + const ph = document.createElement('option'); + ph.textContent = fd.Placeholder; + ph.setAttribute('selected', ''); + ph.setAttribute('disabled', ''); + select.append(ph); + } + fd.Options.split(',').forEach((o) => { + const option = document.createElement('option'); + option.textContent = o.trim(); + option.value = o.trim(); + select.append(option); + }); + return select; +}); + +function createRadio(fd) { + const wrapper = createFieldWrapper(fd); + wrapper.insertAdjacentElement('afterbegin', createInput(fd)); + return wrapper; +} + +const createOutput = withFieldWrapper((fd) => { + const output = document.createElement('output'); + output.name = fd.Name; + output.dataset.fieldset = fd.Fieldset ? fd.Fieldset : ''; + output.innerText = fd.Value; + return output; +}); + +function createHidden(fd) { + const input = document.createElement('input'); + input.type = 'hidden'; + input.id = fd.Id; + input.name = fd.Name; + input.value = fd.Value; + return input; +} + +function createLegend(fd) { + return createLabel(fd, 'legend'); +} + +function createFieldSet(fd) { + const wrapper = createFieldWrapper(fd, 'fieldset'); + wrapper.name = fd.Name; + wrapper.replaceChildren(createLegend(fd)); + if (fd.Repeatable && fd.Repeatable.toLowerCase() === 'true') { + setConstraints(wrapper, fd); + wrapper.dataset.repeatable = 'true'; + } + return wrapper; +} + +function groupFieldsByFieldSet(form) { + const fieldsets = form.querySelectorAll('fieldset'); + fieldsets?.forEach((fieldset) => { + const fields = form.querySelectorAll(`[data-fieldset="${fieldset.name}"`); + fields?.forEach((field) => { + fieldset.append(field); + }); + }); +} + +function createPlainText(fd) { + const paragraph = document.createElement('p'); + const nameStyle = fd.Name ? `form-${fd.Name}` : ''; + paragraph.className = nameStyle; + paragraph.dataset.fieldset = fd.Fieldset ? fd.Fieldset : ''; + paragraph.textContent = fd.Label; + return paragraph; +} + +const getId = (function getId() { + const ids = {}; + return (name) => { + ids[name] = ids[name] || 0; + const idSuffix = ids[name] ? `-${ids[name]}` : ''; + ids[name] += 1; + return `${name}${idSuffix}`; + }; +}()); + +const fieldRenderers = { + radio: createRadio, + checkbox: createRadio, + textarea: createTextArea, + select: createSelect, + button: createButton, + submit: createSubmit, + output: createOutput, + hidden: createHidden, + fieldset: createFieldSet, + plaintext: createPlainText, +}; + +function renderField(fd) { + const renderer = fieldRenderers[fd.Type]; + let field; + if (typeof renderer === 'function') { + field = renderer(fd); + } else { + field = createFieldWrapper(fd); + field.append(createInput(fd)); + } + if (fd.Description) { + field.append(createHelpText(fd)); + } + return field; +} + +async function fetchData(url) { + const resp = await fetch(url); + const json = await resp.json(); + return json.data.map((fd) => ({ + ...fd, + Id: fd.Id || getId(fd.Name), + Value: fd.Value || '', + })); +} + +async function fetchForm(pathname) { + // get the main form + const jsonData = await fetchData(pathname); + return jsonData; +} + +function showError(evnt) { + const field = evnt.target; + const fieldWrapper = field.parentNode; + fieldWrapper.classList.add('invalid'); + let errorSpan = fieldWrapper.querySelector('span.error'); + if (!errorSpan) { + errorSpan = document.createElement('span'); + errorSpan.classList.add('error'); + fieldWrapper.append(errorSpan); + } + errorSpan.innerText = field.validationMessage; + // eslint-disable-next-line no-use-before-define + field.addEventListener('blur', hideError); +} + +function hideError(evnt) { + const field = evnt.target; + const fieldWrapper = field.parentNode; + // to avoid showing error messages on blur + if (field.checkValidity()) { + fieldWrapper.classList.remove('invalid'); + } else { + fieldWrapper.classList.add('invalid'); + } +} + +function decorateValidation(form) { + form.setAttribute('novalidate', ''); + form.querySelectorAll('input,textarea,select').forEach((el) => { + el.addEventListener('invalid', showError); + }); +} + +async function createForm(formURL) { + const { pathname } = new URL(formURL); + const data = await fetchForm(pathname); + const form = document.createElement('form'); + data.forEach((fd) => { + const el = renderField(fd); + const input = el.querySelector('input,textarea,select'); + if (fd.Mandatory && fd.Mandatory.toLowerCase() === 'true') { + input.setAttribute('required', 'required'); + } + if (input) { + input.id = fd.Id; + input.name = fd.Name; + input.value = fd.Value; + if (fd.Description) { + input.setAttribute('aria-describedby', `${fd.Id}-description`); + } + } + form.append(el); + }); + groupFieldsByFieldSet(form); + // eslint-disable-next-line prefer-destructuring + form.addEventListener('submit', (e) => { + let isValid = true; + if (form.hasAttribute('novalidate')) { + isValid = form.checkValidity(); + } + e.preventDefault(); + if (isValid) { + e.submitter.setAttribute('disabled', ''); + form.dataset.action = e.submitter.formAction || SUBMIT_ACTION || pathname.split('.json')[0]; + handleSubmit(form); + } + }); + decorateValidation(form); + return form; +} + +export default async function decorate(block) { + const formLink = block.querySelector('a[href$=".json"]'); + if (formLink) { + const form = await createForm(formLink.href); + formLink.replaceWith(form); + } +} diff --git a/blocks/pardot-form/responses/error.js b/blocks/pardot-form/responses/error.js new file mode 100644 index 00000000..b16cf5c6 --- /dev/null +++ b/blocks/pardot-form/responses/error.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-undef +logResult({ result: 'error' }); diff --git a/blocks/pardot-form/responses/success.js b/blocks/pardot-form/responses/success.js new file mode 100644 index 00000000..372e7d71 --- /dev/null +++ b/blocks/pardot-form/responses/success.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-undef +logResult({ result: 'success' }); diff --git a/blocks/v2-forms/responses/error.js b/blocks/v2-forms/responses/error.js new file mode 100644 index 00000000..b16cf5c6 --- /dev/null +++ b/blocks/v2-forms/responses/error.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-undef +logResult({ result: 'error' }); diff --git a/blocks/v2-forms/responses/success.js b/blocks/v2-forms/responses/success.js new file mode 100644 index 00000000..372e7d71 --- /dev/null +++ b/blocks/v2-forms/responses/success.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-undef +logResult({ result: 'success' }); diff --git a/blocks/v2-forms/v2-forms.js b/blocks/v2-forms/v2-forms.js index ec869470..e0be3890 100644 --- a/blocks/v2-forms/v2-forms.js +++ b/blocks/v2-forms/v2-forms.js @@ -1,3 +1,4 @@ +import { loadScript } from '../../scripts/lib-franklin.js'; import { createElement } from '../../scripts/common.js'; // cache contains the form element that should be reused @@ -5,6 +6,43 @@ const formCache = new Map(); const blockName = 'v2-forms'; +function serialize(obj) { + const str = Object.keys(obj).map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`); + return str.join('&'); +} + +function constructPayload(form) { + const payload = {}; + [...form.elements].forEach((fe) => { + if (fe.name) { + if (fe.type === 'radio') { + if (fe.checked) payload[fe.name] = fe.value; + } else if (fe.type === 'checkbox') { + if (fe.checked) payload[fe.name] = payload[fe.name] ? `${payload[fe.name]},${fe.value}` : fe.value; + } else if (fe.type !== 'file') { + payload[fe.name] = fe.value; + } + } + }); + payload.callback = 'logResult'; + return { payload }; +} + +async function prepareRequest(form) { + const { payload } = constructPayload(form); + const url = form.dataset.action; + + const serializedData = serialize(payload); + loadScript(`${url}?${serializedData}`, { type: 'text/javascript', charset: 'UTF-8' }); +} + +async function handleSubmit(form) { + if (form.getAttribute('data-submitting') !== 'true') { + form.setAttribute('data-submitting', 'true'); + await prepareRequest(form); + } +} + const addForm = async (block) => { const displayValue = block.style.display; block.style.display = 'none'; @@ -21,9 +59,9 @@ const addForm = async (block) => { action="${formAction}" >${formContent.default} -
- - + `; @@ -41,6 +79,21 @@ const addForm = async (block) => { formCache.set(formName, formWrapper); block.style.display = displayValue; + + const formObj = document.querySelector('form'); + // eslint-disable-next-line prefer-destructuring + formObj.addEventListener('submit', (e) => { + let isValid = true; + if (formObj.hasAttribute('novalidate')) { + isValid = formObj.checkValidity(); + } + e.preventDefault(); + if (isValid) { + e.submitter.setAttribute('disabled', ''); + formObj.dataset.action = e.currentTarget.action; + handleSubmit(formObj); + } + }); }; export default async function decorate(block) { diff --git a/blocks/v2-newsletter/v2-newsletter.css b/blocks/v2-newsletter/v2-newsletter.css index 3e71cbbb..d63436cf 100644 --- a/blocks/v2-newsletter/v2-newsletter.css +++ b/blocks/v2-newsletter/v2-newsletter.css @@ -45,8 +45,10 @@ margin: 0; } -.v2-newsletter__form-container .button { - text-transform: uppercase; +.v2-newsletter__form-container p { + margin: 0; + max-width: var(--text-block-max-width); + text-align: center; } @media screen and (min-width: 744px) { diff --git a/blocks/v2-newsletter/v2-newsletter.js b/blocks/v2-newsletter/v2-newsletter.js index 533a327f..2db5b598 100644 --- a/blocks/v2-newsletter/v2-newsletter.js +++ b/blocks/v2-newsletter/v2-newsletter.js @@ -1,14 +1,49 @@ import { - loadBlock, + loadBlock, sampleRUM, } from '../../scripts/lib-franklin.js'; -import { - createElement, -} from '../../scripts/common.js'; +import { getTextLabel, createElement } from '../../scripts/common.js'; const blockName = 'v2-newsletter'; +//* init response handling * +const successTitle = getTextLabel('Success newsletter title'); +const successText = getTextLabel('Success newsletter text'); + +async function submissionSuccess() { + sampleRUM('form:submit'); + const form = document.querySelector('form[data-submitting=true]'); + form.setAttribute('data-submitting', 'false'); + const title = document.querySelector(`.${blockName}__title`); + const message = document.createElement('p'); + message.textContent = successText; + title.textContent = successTitle; + form.replaceWith(message); +} + +const errorTitle = getTextLabel('Error submission title'); +const errorText = getTextLabel('Error submission text'); + +async function submissionFailure() { + const form = document.querySelector('form[data-submitting=true]'); + form.setAttribute('data-submitting', 'false'); + const title = document.querySelector(`.${blockName}__title`); + const message = document.createElement('p'); + message.textContent = errorText; + title.textContent = errorTitle; + form.replaceWith(message); +} +//* end response handling * + +window.logResult = function logResult(json) { + if (json.result === 'success') { + submissionSuccess(); + } else if (json.result === 'error') { + submissionFailure(); + } +}; + export default async function decorate(block) { - const formLimk = block.firstElementChild.innerText.trim(); + const formLink = block.firstElementChild.innerText.trim(); const html = block.firstElementChild.nextElementSibling.firstElementChild.innerHTML; const container = createElement('div', { classes: `${blockName}__container` }); @@ -26,7 +61,7 @@ export default async function decorate(block) {
subscribe
-
${formLimk}
+
${formLink}
`); From a5fbbfed2ace323f8c8b99bd80271d2da7fca7b1 Mon Sep 17 00:00:00 2001 From: taimurCognizant <150666850+taimurCognizant@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:49:11 +0100 Subject: [PATCH 07/41] Countdown in hero #66 (#264) * add countdown timer in hero block * refactor gaps, UI improvements for all variants * fix button padding for tablet * better text wrapping * fix hero container takes at least the height of the viewport, and in case of overflow content, the height expands * update hero gradient * add variant to right align background in hero * refactor better class names for positioning * fix hero background * add play icon --------- Co-authored-by: Syb Wartna --- blocks/v2-hero/v2-hero.css | 98 +++++++++++++++++++++++++++++++------- blocks/v2-hero/v2-hero.js | 81 ++++++++++++++++++++++++++++--- icons/play.svg | 3 ++ placeholder.json | 16 +++++++ 4 files changed, 174 insertions(+), 24 deletions(-) create mode 100644 icons/play.svg diff --git a/blocks/v2-hero/v2-hero.css b/blocks/v2-hero/v2-hero.css index a9dc5d94..7f6f5162 100644 --- a/blocks/v2-hero/v2-hero.css +++ b/blocks/v2-hero/v2-hero.css @@ -26,24 +26,27 @@ --scroll-dot-width: 9px; --v2-hero-padding: 32px; - height: calc(100vh - var(--nav-height)); + min-height: calc(100vh - var(--nav-height)); overflow: hidden; position: relative; + background-color: var(--c-grey-1); } /* Reduce the Hero height by the inpage-navigation height */ .v2-inpage-navigation-wrapper + .v2-hero-container .v2-hero { - height: calc(100vh - (var(--nav-height) + var(--inpage-navigation-height) - 2px)); + min-height: calc(100vh - (var(--nav-height) + var(--inpage-navigation-height) - 2px)); } @supports (height: 1svh) { .v2-hero { - height: calc(100svh - var(--nav-height)); + min-height: calc(100svh - var(--nav-height)); } - .v2-inpage-navigation-wrapper + .v2-hero-container .v2-hero { - height: calc(100svh - (var(--nav-height) + var(--inpage-navigation-height) - 2px)); + .v2-inpage-navigation-wrapper + .v2-hero-container .v2-hero, + .v2-inpage-navigation-wrapper + .v2-hero-container .v2-hero__content-wrapper { + min-height: calc(100svh - (var(--nav-height) + var(--inpage-navigation-height) - 2px)); } + } .section.v2-hero-container { @@ -58,15 +61,19 @@ width: 100%; } +.v2-hero--background-right .v2-hero__image, .v2-hero--background-right .v2-hero__video { + object-position: center right; +} + .v2-hero__content-wrapper { color: var(--c-main-black); position: relative; display: flex; flex-direction: column; - height: 100%; + min-height: calc(100svh - var(--nav-height)); padding: var(--v2-hero-padding); padding-bottom: calc((var(--v2-hero-padding) * 2) + var(--scroll-icon-height)); - background: linear-gradient(180deg, rgb(255 255 255 / 50%) 0%, rgb(255 255 255 / 0%) 100%); + background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 0%) 100%); } .v2-hero--centered .v2-hero__content-wrapper { @@ -75,18 +82,22 @@ text-align: center; } -.v2-hero--left .v2-hero__content-wrapper { +.v2-hero--left-center .v2-hero__content-wrapper { justify-content: center; } -.v2-hero--bottom .v2-hero__content-wrapper { +.v2-hero--center-bottom .v2-hero__content-wrapper { align-items: center; justify-content: flex-end; text-align: center; } +.v2-hero--dark { + background-color: var(--c-grey-4); +} + .v2-hero--dark .v2-hero__content-wrapper { - background: linear-gradient(180deg, rgb(0 0 0 / 50%) 0%, rgb(0 0 0 / 0%) 100%); + background: linear-gradient(180deg, rgb(0 0 0 / 40%) 0%, rgb(0 0 0 / 0%) 100%); color: var(--c-white); } @@ -95,11 +106,11 @@ } .v2-hero__content > * { - max-width: 600px; + max-width: var(--text-block-max-width); } .v2-hero--centered .v2-hero__content > *, -.v2-hero--bottom .v2-hero__content > * { +.v2-hero--center-bottom .v2-hero__content > * { margin-left: auto; margin-right: auto; } @@ -109,18 +120,27 @@ font-size: 70px; line-height: 85%; letter-spacing: 5px; + margin-bottom: 0; + text-wrap: balance; +} + +.v2-hero__title + p { + margin-top: 8px; } -.v2-hero__title, .v2-hero__content p { - margin: 10px 0 0; + margin: 0; } .v2-hero__buttons-wrapper { display: inline-flex; flex-flow: column wrap; - gap: 8px; - margin-top: 24px; + gap: 24px; + padding: 24px 0 0; +} + +.v2-hero__buttons-wrapper .button { + width: max-content; } /* Scroll icon */ @@ -146,6 +166,48 @@ width: var(--scroll-dot-width); } +/* START - Countdown */ + +.v2-hero__countdown { + display: flex; + gap: 32px; + padding: 24px 0; +} + +.v2-hero--centered .v2-hero__countdown, .v2-hero--center-bottom .v2-hero__countdown { + justify-content: center; +} + +.v2-hero__countdown-segment { + position: relative; + flex-basis: 50px; + text-align: center; +} + +.v2-hero__countdown-segment + .v2-hero__countdown-segment::before { + content: ":"; + position: absolute; + font: 400 var(--f-heading-3-5-font-size) / var(--f-heading-3-5-line-height) var(--ff-volvo-novum-medium); + top: 0; + left: -16px; + transform: translate(-50%, 0); +} + +.v2-hero__countdown-number { + font: 400 var(--f-heading-3-5-font-size) / var(--f-heading-3-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: 0.25px; + font-feature-settings: 'tnum'; +} + +.v2-hero__countdown-label { + font: 400 var(--f-caption-font-size) / var(--f-caption-line-height) var(--ff-volvo-novum); + letter-spacing: var(--f-caption-letter-spacing); + text-transform: capitalize; +} + +/* END - Countdown */ + + @media (min-width: 744px) { .v2-hero { --v2-hero-padding: 50px; @@ -158,7 +220,7 @@ .v2-hero__title { font-size: 92px; line-height: 100%; - letter-spacing: -1.15px; + letter-spacing: 6.5px; } .v2-hero__buttons-wrapper { @@ -175,6 +237,6 @@ .v2-hero__title { font-size: 100px; line-height: 85%; - letter-spacing: 2px; + letter-spacing: 7px; } } diff --git a/blocks/v2-hero/v2-hero.js b/blocks/v2-hero/v2-hero.js index cf168b4f..d58b7ea0 100644 --- a/blocks/v2-hero/v2-hero.js +++ b/blocks/v2-hero/v2-hero.js @@ -3,16 +3,49 @@ import { createVideo, } from '../../scripts/video-helper.js'; import { - createElement, - removeEmptyTags, - variantsClassesToBEM, + createElement, getTextLabel, removeEmptyTags, variantsClassesToBEM, } from '../../scripts/common.js'; -const variantClasses = ['centered', 'left', 'bottom', 'dark']; +const variantClasses = ['centered', 'left-center', 'center-bottom', 'dark', 'background-right']; +let intervalId = null; +const blockName = 'v2-hero'; -export default async function decorate(block) { - const blockName = 'v2-hero'; +function updateCountdownElement(block, elementId, value, label) { + const element = block.querySelector(`#${elementId}`); + element.textContent = value.toString().padStart(2, '0'); + element.nextElementSibling.textContent = label; +} + +function updateCountdown(eventTime, block) { + const now = new Date(); + const diff = eventTime - now; + + // Check if the event time has passed + if (diff <= 0) { + block.querySelector(`.${blockName}__countdown-wrapper`)?.remove(); + clearInterval(intervalId); + return; + } + + // Calculate time left + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); + const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = Math.floor((diff % (1000 * 60)) / 1000); + + // Format labels + const dayLabel = days === 1 ? getTextLabel('day') : `${getTextLabel('day')}s`; + const hourLabel = hours === 1 ? getTextLabel('hour') : `${getTextLabel('hour')}s`; + const minuteLabel = minutes === 1 ? getTextLabel('minute') : `${getTextLabel('minute')}s`; + const secondLabel = seconds === 1 ? getTextLabel('second') : `${getTextLabel('second')}s`; + updateCountdownElement(block, 'days', days, dayLabel); + updateCountdownElement(block, 'hours', hours, hourLabel); + updateCountdownElement(block, 'minutes', minutes, minuteLabel); + updateCountdownElement(block, 'seconds', seconds, secondLabel); +} + +export default async function decorate(block) { // add Hero variant classnames variantsClassesToBEM(block.classList, variantClasses, blockName); @@ -45,6 +78,38 @@ export default async function decorate(block) { const content = block.querySelector(':scope > div > div'); content.classList.add(`${blockName}__content`); + // Countdown timer + const blockSection = block.parentElement?.parentElement; + if (blockSection && blockSection.dataset?.countdownDate) { + const countDownWrapper = createElement('div', { classes: `${blockName}__countdown-wrapper` }); + countDownWrapper.innerHTML = `
+
+
00
+
Days
+
+
+
00
+
Hours
+
+
+
00
+
Minutes
+
+
+
00
+
Seconds
+
+
`; + content.prepend(countDownWrapper); + + const eventTimeIso = blockSection.dataset.countdownDate; + const eventTime = new Date(eventTimeIso); + updateCountdown(eventTime, block); + intervalId = setInterval(() => { + updateCountdown(eventTime, block); + }, 1000); + } + // convert all headings to h1 const headings = [...content.querySelectorAll('h1, h2, h3, h4, h5, h6')]; headings.forEach((heading) => { @@ -58,6 +123,10 @@ export default async function decorate(block) { } }); + // render all paragraph as H6 with the class + const paragraphs = [...content.querySelectorAll('p')]; + paragraphs.forEach((paragraph) => paragraph.classList.add('h6')); + const buttonsWrapper = createElement('div', { classes: `${blockName}__buttons-wrapper` }); const ctaButtons = content.querySelectorAll('.button-container > a'); [...ctaButtons].forEach((b, i) => { diff --git a/icons/play.svg b/icons/play.svg new file mode 100644 index 00000000..8ede56fa --- /dev/null +++ b/icons/play.svg @@ -0,0 +1,3 @@ + + + diff --git a/placeholder.json b/placeholder.json index 55fdc4a7..90b1a8ae 100644 --- a/placeholder.json +++ b/placeholder.json @@ -143,6 +143,22 @@ "Key": "vinformat-length", "Text": "Please fill out this field" }, + { + "Key": "day", + "Text": "day" + }, + { + "Key": "hour", + "Text": "hour" + }, + { + "Key": "minute", + "Text": "minute" + }, + { + "Key": "second", + "Text": "second" + }, { "Key": "Close", "Text": "Close" From 76e626c59632ac183779675cab1053bf76646266 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:10:33 +0100 Subject: [PATCH 08/41] Video Module Block New Variant #178 (#271) * add variant for expanding animation * refactor alignment of content * refactor typography * refactor overlay: removed when text not present --- blocks/v2-video/v2-video.css | 61 +++++++++++++++++++++++------------- blocks/v2-video/v2-video.js | 35 +++++++++++++++------ styles/styles.css | 9 +----- 3 files changed, 66 insertions(+), 39 deletions(-) diff --git a/blocks/v2-video/v2-video.css b/blocks/v2-video/v2-video.css index bc921eb5..a21bf0b9 100644 --- a/blocks/v2-video/v2-video.css +++ b/blocks/v2-video/v2-video.css @@ -19,17 +19,22 @@ body .section.v2-video-container .v2-video-wrapper { .v2-video { --video-inset: inset(16px 16px round 8px); --video-transition: all var(--duration-large) ease-in-out; + --video-overlay: linear-gradient(0deg, rgb(0 0 0 / 50%) 20%, rgb(0 0 0 / 0%) 60%); position: relative; overflow: hidden; display: flex; justify-content: center; height: 100vh; + background: var(--c-grey-4); +} + +.v2-video--expanding { clip-path: var(--video-inset); transition: var(--video-transition); } -.v2-video.v2-video--full-width { +.v2-video--expanding.v2-video--full-width { --video-inset: inset(0 0 round 0); } @@ -47,23 +52,23 @@ body .section.v2-video-container .v2-video-wrapper { display: flex; flex-direction: column; justify-content: flex-end; - background: linear-gradient(0deg, rgb(0 0 0 / 50%) 0%, rgb(0 0 0 / 0%) 100%); width: 100%; - position: relative; align-items: center; } +.v2-video .v2-video__content-wrapper::before { + content: ''; + position: absolute; + inset: 0; + background: var(--video-overlay); +} + .v2-video .v2-video__content { margin: 48px; position: relative; display: flex; flex-direction: column; gap: 8px; - font-family: var(--font-family-body); - font-size: var(--f-body-font-size); - font-style: normal; - line-height: var(--f-body-line-height); - letter-spacing: var(--f-body-letter-spacing); text-align: center; transition: var(--video-transition); } @@ -72,9 +77,13 @@ body .section.v2-video-container .v2-video-wrapper { margin: 0; } -.v2-video .v2-video__content p { - font-family: var(--font-family-body); - font-style: normal; +.v2-video .v2-video__heading { + font: var(--f-heading-5-font-size)/var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: var(--f-heading-5-letter-spacing); +} + +.v2-video .button-container { + margin-top: 8px; } @media (min-width: 744px) { @@ -84,17 +93,12 @@ body .section.v2-video-container .v2-video-wrapper { .v2-video { --video-inset: inset(32px 32px round 8px); + --video-overlay: linear-gradient(0deg, rgb(0 0 0 / 50%) 20%, rgb(0 0 0 / 0%) 50%); } .v2-video .v2-video__content { margin: 64px; - max-width: 400px; - } - - .v2-video .v2-video__heading { - font-size: var(--f-heading-4-font-size); - line-height: var(--f-heading-4-line-height); - letter-spacing: var(--f-heading-4-letter-spacing); + max-width: var(--text-block-max-width); } } @@ -105,20 +109,35 @@ body .section.v2-video-container .v2-video-wrapper { .v2-video { --video-inset: inset(136px calc((100vw - var(--wrapper-width)) / 2) round 8px); + --video-overlay: linear-gradient(90deg, rgb(0 0 0 / 60%) 0%, rgb(0 0 0 / 0%) 50%); } .v2-video .v2-video__content-wrapper { + max-width: var(--wrapper-width); justify-content: center; align-items: flex-start; } .v2-video .v2-video__content { - margin: auto calc((100vw - 1200px) / 2); - transform: translateX(135px); + margin: 120px; + max-width: 456px; + gap: 16px; text-align: left; } .v2-video.v2-video--full-width .v2-video__content { - transform: translateX(0); + transform: translateX(-120px); + } + + .v2-video .v2-video__heading { + font-size: var(--f-heading-4-font-size); + line-height: var(--f-heading-4-line-height); + letter-spacing: var(--f-heading-4-letter-spacing); + } + + .v2-video .v2-video__content p { + font-size: var(--f-body-2-font-size); + line-height: var(--f-body-2-line-height); + letter-spacing: var(--f-body-2-letter-spacing); } } diff --git a/blocks/v2-video/v2-video.js b/blocks/v2-video/v2-video.js index ccfcba9e..394617c8 100644 --- a/blocks/v2-video/v2-video.js +++ b/blocks/v2-video/v2-video.js @@ -1,3 +1,4 @@ +import { removeEmptyTags, variantsClassesToBEM } from '../../scripts/common.js'; import { createVideo, setPlaybackControls } from '../../scripts/video-helper.js'; const onHoverOrScroll = (element, handler) => { @@ -30,32 +31,36 @@ const onHoverOrScroll = (element, handler) => { }); }; +const variantClasses = ['expanding']; + export default async function decorate(block) { - const blockClass = 'v2-video'; + const blockName = 'v2-video'; const videoLink = block.querySelector('a'); const headings = block.querySelectorAll('h1, h2, h3, h4, h5, h6'); const ctaButtons = block.querySelectorAll('.button-container a'); const contentWrapper = block.querySelector(':scope > div'); const content = block.querySelector(':scope > div > div'); + variantsClassesToBEM(block.classList, variantClasses, blockName); + if (!videoLink) { // eslint-disable-next-line no-console console.warn('Video for v2-video block is required and not provided. The block will not render!'); block.innerHTML = ''; } - const video = createVideo(videoLink.getAttribute('href'), `${blockClass}__video`, { + const video = createVideo(videoLink.getAttribute('href'), `${blockName}__video`, { muted: true, autoplay: true, loop: true, playsinline: true, }); - contentWrapper.classList.add(`${blockClass}__content-wrapper`); - content.classList.add(`${blockClass}__content`); - [...headings].forEach((heading) => heading.classList.add(`${blockClass}__heading`)); + contentWrapper.classList.add(`${blockName}__content-wrapper`); + content.classList.add(`${blockName}__content`); + [...headings].forEach((heading) => heading.classList.add(`${blockName}__heading`)); [...ctaButtons].forEach((button) => { - button.classList.add(`${blockClass}__button`, 'tertiary', 'dark'); + button.classList.add(`${blockName}__button`, 'tertiary', 'dark'); button.classList.remove('primary'); }); @@ -65,9 +70,19 @@ export default async function decorate(block) { setPlaybackControls(); - onHoverOrScroll(block.querySelector(`.${blockClass}__content-wrapper`), (val) => { - const action = val ? 'add' : 'remove'; + removeEmptyTags(block); - block.classList[action](`${blockClass}--full-width`); - }); + if (contentWrapper.innerHTML.trim().length === 0) { + contentWrapper.remove(); + } + + if (block.classList.contains(`${blockName}--expanding`)) { + onHoverOrScroll(block, (val) => { + const action = val ? 'add' : 'remove'; + + block.classList[action](`${blockName}--full-width`); + }); + } else { + block.classList.add(`${blockName}--full-width`); + } } diff --git a/styles/styles.css b/styles/styles.css index c5919308..da7ece6a 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1463,13 +1463,6 @@ main.blue-contract .section.section-with-title p { padding: 0; } -.v2-video .v2-video__heading { - font-family: var(--font-family-heading); - font-size: var(--f-heading-5-font-size); - font-style: normal; - line-height: var(--f-heading-5-line-height); -} - .v2-video__playback-button .icon-play-video { display: none; justify-content: center; @@ -1648,4 +1641,4 @@ main.blue-contract .section.section-with-title p { border-bottom: 7px solid var(--c-white); } -/* generic tooltip styles ends here */ \ No newline at end of file +/* generic tooltip styles ends here */ From 203b741062110e02e03f88036284cef4516cbfd2 Mon Sep 17 00:00:00 2001 From: Marko V <139957716+markovukiceviccn@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:13:17 +0100 Subject: [PATCH 09/41] Volvo trucks PDP Resource gallery (#257) * Resources gallery #87 --- .../v2-resources-gallery.css | 193 ++++++++++++++++++ .../v2-resources-gallery.js | 123 +++++++++++ icons/documents.svg | 3 + placeholder.json | 12 ++ scripts/common.js | 27 ++- scripts/video-helper.js | 14 +- styles/styles.css | 70 +++---- test/scripts/video-helper.test.js | 7 - 8 files changed, 382 insertions(+), 67 deletions(-) create mode 100644 blocks/v2-resources-gallery/v2-resources-gallery.css create mode 100644 blocks/v2-resources-gallery/v2-resources-gallery.js create mode 100644 icons/documents.svg diff --git a/blocks/v2-resources-gallery/v2-resources-gallery.css b/blocks/v2-resources-gallery/v2-resources-gallery.css new file mode 100644 index 00000000..910d39b0 --- /dev/null +++ b/blocks/v2-resources-gallery/v2-resources-gallery.css @@ -0,0 +1,193 @@ +.v2-resources-gallery { + display: flex; + gap: 40px; + flex-flow: column; +} + +.v2-resources-gallery__heading-wrapper { + margin-bottom: 0; + display: flex; + flex-flow: column; + flex-wrap: wrap; + gap: 8px; +} + +.v2-resources-gallery__button { + display: flex; + font-size: var(--f-button-font-size); + background: none; + outline: none; + align-self: flex-start; +} + +.v2-resources-gallery__button .icon-minus { + display: none; +} + +.v2-resources-gallery__button[aria-expanded="true"] .icon-minus { + display: block; +} + +.v2-resources-gallery__button[aria-expanded="true"] .icon-plus { + display: none; +} + +.v2-resources-gallery__heading { + font: var(--f-heading-5-font-size) / var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: 0.25px; + margin-bottom: 0; +} + +.v2-resources-gallery__heading-wrapper .v2-resources-gallery__heading { + text-transform: capitalize; +} + +.v2-resources-gallery__video-list { + display: grid; + list-style-type: none; + gap: 8px; + grid-template-columns: repeat(6, calc(100% - 100px)); + overflow-x: scroll; + scroll-snap-type: x mandatory; + scroll-snap-align: start; + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +/* Hide scrollbar for Chrome, Safari and Opera */ +.v2-resources-gallery__video-list::-webkit-scrollbar { + display: none; +} + +.v2-resources-gallery__video-list-item { + scroll-snap-align: start; + display: flex; + flex-direction: column; +} + +.v2-resources-gallery__video-title { + font: var(--f-body-2-font-size) / var(--f-body-2-line-height) var(--ff-volvo-novum); + letter-spacing: var(--f-body-2-letter-spacing); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: calc(100% - 10px); + margin-bottom: 8px; +} + +.v2-resources-gallery__video-list p { + font: var(--f-body-font-size) / var(--f-body-line-height) var(--ff-volvo-novum); + letter-spacing: var(--f-body-letter-spacing); + color: var(--text-subtle); + margin: 0; +} + +.v2-resources-gallery__video-list .v2-resources-gallery__video-image { + margin: 0 0 16px; +} + +.v2-resources-gallery__video-image img { + border-radius: 8px; + aspect-ratio: 255/143; + object-fit: cover; + width: 100%; + height: 100%; +} + +.v2-resources-gallery__document-list { + list-style-type: none; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 40px 32px; +} + +.v2-resources-gallery__document-link-wrapper { + display: flex; + flex-flow: column; + gap: 8px; + margin: 0; +} + +.v2-resources-gallery__document-list a.button.standalone-link { + text-decoration: underline; + text-underline-offset: 4px; + text-transform: capitalize; + display: flex; + gap: 8px; +} + +.v2-resources-gallery__heading-wrapper button.v2-resources-gallery__button.tertiary:hover { + background-color: transparent; + outline: none; + text-decoration: none; +} + +.v2-resources-gallery__document-link-wrapper p { + margin: 0; + color: var(--text-subtle); + padding-left: 32px; +} + +.v2-resources-gallery__document-list-item--hide, +.v2-resources-gallery__video-list-item--hide { + display: none; +} + +.v2-resources-gallery__list--expand .v2-resources-gallery__video-list { + grid-template-columns: minmax(0, 1fr); + grid-auto-flow: row; + grid-row-gap: 40px; +} + +.v2-resources-gallery__list--expand .v2-resources-gallery__document-list-item--hide, +.v2-resources-gallery__list--expand .v2-resources-gallery__video-list-item--hide { + display: flex; +} + +@media (min-width: 744px) { + .v2-resources-gallery__heading-wrapper { + flex-flow: row; + gap: 40px; + align-items: center; + } + + .v2-resources-gallery__heading { + flex: 1; + } + + .v2-resources-gallery__video-list { + grid-template-columns: repeat(6, calc(100% / 2.80)); + gap:16px; + } + + .v2-resources-gallery__document-list { + grid-template-columns: repeat(3, 1fr); + grid-column-gap: inherit; + } + + .v2-resources-gallery__list--expand .v2-resources-gallery__video-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (min-width: 1200px) { + .v2-resources-gallery__video-list { + gap: 40px; + overflow-x: unset; + } + + .v2-resources-gallery__video-list, + .v2-resources-gallery__list--expand .v2-resources-gallery__video-list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .v2-resources-gallery__document-link-wrapper { + flex-flow: row; + align-items: center; + } + + .v2-resources-gallery__document-link-wrapper p { + padding-left: 0; + } +} + diff --git a/blocks/v2-resources-gallery/v2-resources-gallery.js b/blocks/v2-resources-gallery/v2-resources-gallery.js new file mode 100644 index 00000000..d6634273 --- /dev/null +++ b/blocks/v2-resources-gallery/v2-resources-gallery.js @@ -0,0 +1,123 @@ +import { + wrapImageWithVideoLink, + selectVideoLink, + isVideoLink, + addVideoShowHandler, +} from '../../scripts/video-helper.js'; +import { createElement, getTextLabel, unwrapDivs } from '../../scripts/common.js'; + +const blockName = 'v2-resources-gallery'; + +export default function decorate(block) { + const blockHeading = block.querySelector('div:first-child'); + blockHeading.classList.add(`${blockName}__heading-wrapper`); + const title = blockHeading.querySelector('h4'); + title?.classList.add(`${blockName}__heading`); + unwrapDivs(blockHeading); + + const viewAllButton = createElement('button', { + classes: [`${blockName}__button`, 'tertiary'], + props: { 'aria-expanded': false }, + }); + viewAllButton.innerHTML = ` + + + + + + + + + + + ${getTextLabel('view all')} + `; + + blockHeading.append(viewAllButton); + + const videoWrapper = createElement('div', { classes: `${blockName}__video-list` }); + const documentWrapper = createElement('div', { classes: `${blockName}__document-list` }); + const rows = block.querySelectorAll(':scope > div > div'); + + rows.forEach((row) => { + const picture = row.querySelector('picture'); + const document = row.querySelector('.icon-documents'); + + if (picture) { + const listEle = createElement('div', { classes: `${blockName}__video-list-item` }); + listEle.innerHTML = row.innerHTML; + + const videos = [...listEle.querySelectorAll('a')].filter((link) => isVideoLink(link)); + + if (videos.length) { + // display image as link with play icon + const selectedLink = selectVideoLink(videos); + if (selectedLink) { + addVideoShowHandler(selectedLink); + wrapImageWithVideoLink(selectedLink, picture); + selectedLink.parentElement.classList.add(`${blockName}__video-image`, 'image'); + } + + if (videoWrapper.children.length > 5) { + listEle.classList.add(`${blockName}__video-list-item--hide`); + listEle.setAttribute('aria-hidden', true); + } + + const videoTitle = listEle.querySelector('h3'); + videoTitle.classList.add(`${blockName}__video-title`); + + // remove all the videos links and exclude the selected one + videos.forEach((link) => link !== selectedLink && link.parentElement.remove()); + + videoWrapper.append(listEle); + listEle.firstElementChild.remove(); + row.innerHTML = ''; + } + } else if (document) { + const item = createElement('div', { classes: `${blockName}__document-list-item` }); + item.innerHTML = row.innerHTML; + const links = item.querySelectorAll('a'); + [...links].forEach((link) => { + link.classList.add('standalone-link'); + link.classList.remove('primary'); + const wrapper = link.parentElement; + wrapper.className = `${blockName}__document-link-wrapper`; + wrapper.append(wrapper.nextElementSibling); + }); + + if (documentWrapper.children.length > 5) { + item.classList.add(`${blockName}__document-list-item--hide`); + item.setAttribute('aria-hidden', true); + } + + documentWrapper.append(item); + row.innerHTML = ''; + } + }); + + block.append(videoWrapper); + block.append(documentWrapper); + + function toggleListEle(ariaValue) { + [...block.querySelectorAll(`li[aria-hidden="${ariaValue}"]`)].forEach((li) => { + li.setAttribute('aria-hidden', !ariaValue); + }); + } + + viewAllButton.addEventListener('click', () => { + const buttonText = viewAllButton.lastElementChild; + if (viewAllButton.ariaExpanded === 'true') { + viewAllButton.ariaExpanded = 'false'; + buttonText.innerText = getTextLabel('view all'); + block.classList.remove(`${blockName}__list--expand`); + toggleListEle(false); + } else { + viewAllButton.ariaExpanded = 'true'; + buttonText.innerText = getTextLabel('view less'); + block.classList.add(`${blockName}__list--expand`); + toggleListEle(true); + } + }); + + unwrapDivs(block); +} diff --git a/icons/documents.svg b/icons/documents.svg new file mode 100644 index 00000000..3cd852fd --- /dev/null +++ b/icons/documents.svg @@ -0,0 +1,3 @@ + + + diff --git a/placeholder.json b/placeholder.json index 90b1a8ae..6b482db4 100644 --- a/placeholder.json +++ b/placeholder.json @@ -166,6 +166,18 @@ { "Key": "Copied", "Text": "Copied!" + }, + { + "Key": "view all", + "Text": "View all" + }, + { + "Key": "toggle list", + "Text": "Toggle resources list" + }, + { + "Key": "view less", + "Text": "View less" } ], ":type": "sheet" diff --git a/scripts/common.js b/scripts/common.js index f6cff806..93b0013d 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -157,17 +157,26 @@ export const removeEmptyTags = (block) => { }; export const unwrapDivs = (element) => { - Array.from(element.children).forEach((node) => { - if (node.tagName === 'DIV' && node.attributes.length === 0) { - while (node.firstChild) { - element.insertBefore(node.firstChild, node); + const stack = [element]; + + while (stack.length > 0) { + const currentElement = stack.pop(); + + let i = 0; + while (i < currentElement.children.length) { + const node = currentElement.children[i]; + + if (node.tagName === 'DIV' && node.attributes.length === 0) { + while (node.firstChild) { + currentElement.insertBefore(node.firstChild, node); + } + node.remove(); + } else { + stack.push(node); + i += 1; } - node.remove(); - unwrapDivs(element); - } else { - unwrapDivs(node); } - }); + } }; export const variantsClassesToBEM = (blockClasses, expectedVariantsNames, blockName) => { diff --git a/scripts/video-helper.js b/scripts/video-helper.js index 54a104dd..425e74aa 100644 --- a/scripts/video-helper.js +++ b/scripts/video-helper.js @@ -97,10 +97,15 @@ export function addSoundcloudShowHandler(link) { } export function addPlayIcon(parent) { - const iconWrapper = createElement('div', { classes: 'video-icon-wrapper' }); - const icon = createElement('i', { classes: ['fa', 'fa-play', 'video-icon'] }); - iconWrapper.appendChild(icon); - parent.appendChild(iconWrapper); + const playButton = document.createRange().createContextualFragment(` + + + + + + `); + + parent.appendChild(playButton); } export function wrapImageWithVideoLink(videoLink, image) { @@ -108,7 +113,6 @@ export function wrapImageWithVideoLink(videoLink, image) { videoLink.appendChild(image); videoLink.classList.add('link-with-video'); videoLink.classList.remove('button', 'primary', 'text-link-with-video'); - addPlayIcon(videoLink); } diff --git a/styles/styles.css b/styles/styles.css index da7ece6a..661d859c 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -759,47 +759,20 @@ main .section.tabbed-container ul.tab-navigation button { border-bottom: 3px solid var(--c-cta-blue-default); } -/* image as video link */ -.link-with-video { - position: relative; - display: block; -} - -.video-icon-wrapper { - background: rgb(255 255 255 / 90%); - color: var(--volvo-text-gray); - width: 50px; - height: 50px; - font-size: 25px; - border: 0; - border-radius: 40px; - transition: .3s; - text-align: center; - overflow: hidden; - vertical-align: middle; - margin-right: 7.5px; - cursor: pointer; - box-shadow: 0 0 10px 5px rgb(0 0 0 / 30%); - position: absolute; - top: 50%; - left: 50%; - transform: translateX(-50%) translateY(-50%); - z-index: 1; - line-height: 50px; +.redesign-v2 .icon { display: flex; - align-items: center; - justify-content: center; } -.video-icon-wrapper:hover { - background: rgb(230 230 230 / 90%); - color: var(--volvo-text-gray); - font-size: 30px; - opacity: .8; +.redesign-v2 .icon svg { + fill: currentcolor; + height: 1em; + width: 1em; } -.video-icon-wrapper .video-icon::before { - padding-left: 5px; +/* image as video link */ +.link-with-video { + position: relative; + display: block; } a.button.text-link-with-video { @@ -812,6 +785,21 @@ a.button.text-link-with-video { padding: 5px; } +.icon-play-video { + position: absolute; + top: 50%; + right: 50%; + transform: translate(50%, -50%); + width: 48px; + height: 48px; +} + +.link-with-video .icon-play-video svg, +.redesign-v2 .link-with-video .icon-play-video svg { + width: 48px; + height: 48px; +} + a.button.text-link-with-video:hover { color: #030304; background: transparent; @@ -1336,16 +1324,6 @@ main.blue-contract .section.section-with-title p { display: none; } -.redesign-v2 .icon { - display: flex; -} - -.redesign-v2 .icon svg { - fill: currentcolor; - height: 1em; - width: 1em; -} - /* Headings classes styles */ .redesign-v2 .h1 { font-family: var(--ff-volvo-novum); diff --git a/test/scripts/video-helper.test.js b/test/scripts/video-helper.test.js index 02091d42..2e8957ad 100644 --- a/test/scripts/video-helper.test.js +++ b/test/scripts/video-helper.test.js @@ -184,13 +184,6 @@ describe('wrapImageWithVideoLink', () => { image = document.createElement('img'); }); - it('should set video link inner text to empty', () => { - videoHelper.wrapImageWithVideoLink(videoLink, image); - - expect(videoLink.innerText).to.equal(''); - expect(videoLink.childNodes[0]).to.equal(image); - }); - it('should add the "link-with-video" class to the video link', () => { videoHelper.wrapImageWithVideoLink(videoLink, image); From 82ba94e542a735d0b268910f6366d748907daf85 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:23:39 +0100 Subject: [PATCH 10/41] Implement event notifications #68 (#269) * Event notifications block #68 * Calendar invite #72 --- blocks/footer/footer.css | 8 +- blocks/v2-event-notify/v2-event-notify.css | 176 ++++++++++++++ blocks/v2-event-notify/v2-event-notify.js | 188 +++++++++++++++ blocks/v2-forms/forms/event-notify.js | 77 ++++++ blocks/v2-forms/v2-forms.js | 18 +- .../v2-inpage-navigation.css | 2 +- .../v2-media-with-tabs/v2-media-with-tabs.css | 10 +- blocks/v2-social-block/v2-social-block.css | 27 +-- blocks/v2-social-block/v2-social-block.js | 6 - .../v2-static-content-carousel.css | 8 +- .../v2-stories-carousel.css | 2 +- blocks/v2-truck-lineup/v2-truck-lineup.css | 4 +- common/modal/modal.css | 2 +- icons/checkbox.svg | 3 + placeholder.json | 30 ++- styles/styles.css | 224 +++++++++++++----- 16 files changed, 679 insertions(+), 106 deletions(-) create mode 100644 blocks/v2-event-notify/v2-event-notify.css create mode 100644 blocks/v2-event-notify/v2-event-notify.js create mode 100644 blocks/v2-forms/forms/event-notify.js create mode 100644 icons/checkbox.svg diff --git a/blocks/footer/footer.css b/blocks/footer/footer.css index 9e8122f4..1e7e9a4d 100644 --- a/blocks/footer/footer.css +++ b/blocks/footer/footer.css @@ -184,7 +184,7 @@ .footer-list .footer__title:not(.expand):focus, .footer-list-item a:focus, .footer-bar a:focus { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); } button.v2-scroll-to-top:focus { @@ -192,7 +192,7 @@ button.v2-scroll-to-top:focus { } .v2-scroll-to-top:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 1px; } @@ -230,7 +230,7 @@ button.v2-scroll-to-top:focus { gap: 20px } - .footer-list, + .footer-list, .footer-list-item { display: flex; flex-direction: column; @@ -247,7 +247,7 @@ button.v2-scroll-to-top:focus { max-height: 100%; overflow: auto; } - + .footer-list-item a { gap: 8px; align-items: center; diff --git a/blocks/v2-event-notify/v2-event-notify.css b/blocks/v2-event-notify/v2-event-notify.css new file mode 100644 index 00000000..1d3cbf3f --- /dev/null +++ b/blocks/v2-event-notify/v2-event-notify.css @@ -0,0 +1,176 @@ +.v2-event-notify__container > * { + max-width: 694px; + margin: auto; +} + +.v2-event-notify__container .v2-event-notify__text-wrapper { + display: flex; + flex-direction: column; + gap: 8px; + text-align: center; + max-width: var(--text-block-max-width); + margin: 40px auto; +} + +.v2-event-notify__container .v2-event-notify__title { + font-size: var(--f-heading-4-font-size); + letter-spacing: var(--f-heading-4-letter-spacing); + line-height: var(--f-heading-4-line-height); + margin: 0; +} + +.v2-event-notify__container .v2-event-notify__text-wrapper p { + margin: 0; +} + +.v2-event-notify__container .event-notify__wrapper { + display: flex; + flex-direction: column; + gap: 16px; +} + +.v2-event-notify__container .event-notify__field-wrapper { + display: flex; + flex-direction: column; + gap: 8px; +} + +.v2-event-notify__container .v2-forms__container input { + background: var(--c-primary-white); + font-size: var(--f-body-font-size); + letter-spacing: var(--f-body-letter-spacing); + line-height: var(--f-body-line-height); + margin: 0; +} + +.v2-event-notify__container .event-notify__buttons { + gap: 24px; + display: flex; + flex-direction: column; + align-items: center; +} + +.v2-event-notify__container .event-notify__agreement-section { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 40px; +} + +.v2-event-notify__container .event-notify__error-message { + color: var(--c-error-red); + font-size: var(--f-caption-font-size); + line-height: var(--f-caption-line-height); + letter-spacing: var(--f-caption-letter-spacing); +} + +.v2-event-notify__container .v2-event-notify__message-text { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + padding: 40px 0; + text-align: center; + max-width: 506px; +} + +.v2-event-notify__container .v2-event-notify__message-text p { + margin: 0; +} + +.v2-event-notify__container .v2-event-notify__button-wrapper { + display: flex; + justify-content: center; + margin-bottom: 40px; +} + +.v2-event-notify__container .v2-social-block { + max-width: unset; +} + +.v2-event-notify__container input:user-invalid, +.v2-event-notify__container input:user-invalid:hover, +.v2-event-notify__container input:user-invalid:focus { + border-color: var(--c-error-red); +} + +.v2-event-notify__container .event-notify__add-event-button { + cursor: pointer; +} + +.event-notify__policy { + margin: 16px 0 32px; +} + +@media (min-width: 744px) { + .v2-event-notify__container .event-notify__wrapper { + display: flex; + flex-flow: row wrap; + } + + .v2-event-notify__container .event-notify__wrapper > * { + width: calc(50% - 8px); + } + + .v2-event-notify__container .v2-event-notify__text-wrapper { + gap: 16px; + } + + .v2-event-notify__container .event-notify__agreement-section { + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + } + + .v2-event-notify__container .event-notify__buttons { + flex-direction: row; + justify-content: center; + } + + .v2-event-notify__container .v2-event-notify__message-text { + gap: 16px; + } +} + +@media (min-width: 1200px) { + .v2-event-notify__container .v2-event-notify__text-wrapper { + margin: 48px auto; + gap: 24px; + } + + .v2-event-notify__container .v2-event-notify__title { + font-size: var(--f-heading-3-font-size); + letter-spacing: var(--f-heading-3-letter-spacing); + line-height: var(--f-heading-3-line-height); + margin: 0; + } + + .v2-event-notify__container .v2-event-notify__text-wrapper p { + font-size: var(--f-body-2-font-size); + letter-spacing: var(--f-body-2-letter-spacing); + line-height: var(--f-body-2-line-height); + } + + .v2-event-notify__container .event-notify__wrapper { + column-gap: 20px; + row-gap: 24px; + } + + .v2-event-notify__container .event-notify__wrapper > * { + width: calc(50% - 10px); + } + + .v2-event-notify__container .event-notify__agreement-section { + margin-top: 48px; + } + + .v2-event-notify__container .v2-event-notify__message-text { + gap: 24px; + padding: 48px 0; + } + + .v2-event-notify__container .v2-event-notify__button-wrapper { + margin-bottom: 48px; + } +} diff --git a/blocks/v2-event-notify/v2-event-notify.js b/blocks/v2-event-notify/v2-event-notify.js new file mode 100644 index 00000000..baeea051 --- /dev/null +++ b/blocks/v2-event-notify/v2-event-notify.js @@ -0,0 +1,188 @@ +import { + loadBlock, sampleRUM, +} from '../../scripts/lib-franklin.js'; +import { + createElement, +} from '../../scripts/common.js'; + +const blockName = 'v2-event-notify'; + +let successText; +let errorText; +let socialsLinks; + +const onSuccess = async () => { + sampleRUM('form:submit'); + const block = document.querySelector(`.${blockName}__container`); + const addToEventButton = block.querySelector('.event-notify__add-event-button'); + + block.innerHTML = ''; + const buttonWrapper = createElement('div', { classes: `${blockName}__button-wrapper` }); + addToEventButton.classList.remove('secondary'); + addToEventButton.classList.add('primary'); + + const socialsLinksBlock = document.createRange().createContextualFragment(` +
+
+
+
+
`); + + const socialLinkBlockEl = socialsLinksBlock.children[0]; + socialLinkBlockEl.querySelector(':scope > div > div').innerHTML = socialsLinks.innerHTML; + + await loadBlock(socialLinkBlockEl); + + buttonWrapper.append(addToEventButton); + block.append(successText, buttonWrapper, socialLinkBlockEl); +}; + +const onError = (error) => { + // eslint-disable-next-line no-console + console.error(error); + + const block = document.querySelector(`.${blockName}__container`); + + block.innerHTML = ''; + block.append(errorText); +}; + +// Convert date to ICS format (e.g., 20231210T120000Z) +function formatDateToICS(date) { + return date.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, ''); +} + +// Generate UID (e.g., 20231210T120000Z-sdfijk@exmaple.com) +function generateUID() { + const timestamp = formatDateToICS(new Date()); + const uniqueString = Math.random().toString(36).substr(2, 6); + const domain = window.location.hostname; + return `${timestamp}-${uniqueString}@${domain}`; +} + +function generateICS(event) { + if (!event.summary || !event.startDate || !event.endDate || !event.description) { + throw new Error('Missing required event details'); + } + const icsData = [ + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'PRODID:-//Volvo Trucks//Volvo Trucks US website//EN', + `UID:${generateUID()}`, + 'BEGIN:VEVENT', + `SUMMARY:${event.summary}`, + `DTSTART:${formatDateToICS(event.startDate)}`, + `DTEND:${formatDateToICS(event.endDate)}`, + `DESCRIPTION:${event.description}`, + 'LOCATION:online', + 'END:VEVENT', + 'END:VCALENDAR', + ].join('\r\n'); + return icsData; +} + +function downloadICSFile(icsData, filename) { + const blob = new Blob([icsData], { type: 'text/calendar' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); +} + +window.logResult = function logResult(json) { + if (json.result === 'success') { + onSuccess(); + } else if (json.result === 'error') { + onError(json.log); + } +}; + +export default async function decorate(block) { + const rows = [...block.querySelectorAll(':scope > div')]; + + const contentData = rows.reduce((data, row) => { + const [name, content] = row.querySelectorAll(':scope > div'); + const key = name.innerText.toLowerCase().trim(); + + return { ...data, [key]: content }; + }, {}); + + const formLink = contentData.link.innerText.trim(); + const beforeFormText = contentData['before form']; + const policyText = contentData.policy; + socialsLinks = contentData.socials; + socialsLinks.classList.add(`${blockName}__socials`); + errorText = contentData['error message']; + errorText.classList.add(`${blockName}__message-text`); + successText = contentData['success message']; + successText.classList.add(`${blockName}__message-text`); + + // Calendar event meta data + const blockSection = block.parentElement?.parentElement; + const calendarEventData = { + summary: blockSection?.dataset.eventSummary, + startDate: new Date(blockSection?.dataset.eventStartDate), + endDate: new Date(blockSection?.dataset.eventEndDate), + description: blockSection?.dataset.eventDescription, + }; + + const container = createElement('div', { classes: `${blockName}__container` }); + const formContainer = createElement('div', { classes: `${blockName}__form-container` }); + const form = document.createRange().createContextualFragment(` +
+
+
event-notify
+
+
+
${formLink}
+
+
`); + + if (beforeFormText) { + container.append(beforeFormText); + beforeFormText.classList.add(`${blockName}__text-wrapper`); + + const headingSelector = 'h1, h2, h3, h4, h5, h6'; + const headings = [ + ...beforeFormText.querySelectorAll(headingSelector), + ...errorText.querySelectorAll(headingSelector), + ...successText.querySelectorAll(headingSelector), + ]; + headings.forEach((heading) => heading.classList.add(`${blockName}__title`)); + } + formContainer.append(...form.children); + container.appendChild(formContainer); + + block.replaceWith(container); + + // we can inject the policy content when form content loaded + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + const formRef = [...mutation.addedNodes]; + const policyEl = formRef.find((el) => el.querySelector('.event-notify__policy'))?.querySelector('.event-notify__policy'); + const calendarButtonEl = formRef.find((el) => el.querySelector('.event-notify__add-event-button'))?.querySelector('.event-notify__add-event-button'); + + if (formRef) { + policyEl.append(policyText); + calendarButtonEl.addEventListener('click', () => { + const icsFileContent = generateICS(calendarEventData); + downloadICSFile(icsFileContent, 'event.ics'); + }); + + observer.disconnect(); + } + }); + }); + + observer.observe(container, { + childList: true, + attributes: false, + subtree: true, + }); + + await loadBlock(formContainer.firstElementChild); +} diff --git a/blocks/v2-forms/forms/event-notify.js b/blocks/v2-forms/forms/event-notify.js new file mode 100644 index 00000000..32447b30 --- /dev/null +++ b/blocks/v2-forms/forms/event-notify.js @@ -0,0 +1,77 @@ +import { getTextLabel } from '../../../scripts/common.js'; + +const formName = 'event-notify'; +const formContent = ` +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
+
+
+ +
+ + ${getTextLabel('event-notify:add-event')} +
+`; + +const checkFieldValidity = (field) => { + const errorMessageEl = field.parentElement.querySelector(`:scope > .${formName}__error-message`); + + if (errorMessageEl) { + const isUserInvalid = field.parentElement.querySelector(':scope:user-invalid') === field; + errorMessageEl.innerText = isUserInvalid ? '' : field.validationMessage; + errorMessageEl.classList[isUserInvalid ? 'add' : 'remove'](`${formName}__error-message--hidden`); + } +}; + +export const postLoad = (form) => { + form.setAttribute('novalidate', 'novalidate'); + + const fields = [...form.querySelectorAll('input')]; + + fields.forEach((field) => { + field.addEventListener('input', () => { + checkFieldValidity(field); + }); + }); +}; + +export const onSubmit = async (form, handleSubmit) => { + const fields = [...form.querySelectorAll('input')]; + + fields.forEach(checkFieldValidity); + + if (form.checkValidity()) { + await handleSubmit(form); + } +}; + +export default formContent; diff --git a/blocks/v2-forms/v2-forms.js b/blocks/v2-forms/v2-forms.js index e0be3890..1bd7b495 100644 --- a/blocks/v2-forms/v2-forms.js +++ b/blocks/v2-forms/v2-forms.js @@ -33,13 +33,18 @@ async function prepareRequest(form) { const url = form.dataset.action; const serializedData = serialize(payload); - loadScript(`${url}?${serializedData}`, { type: 'text/javascript', charset: 'UTF-8' }); + + return loadScript(`${url}?${serializedData}`, { type: 'text/javascript', charset: 'UTF-8' }); } async function handleSubmit(form) { if (form.getAttribute('data-submitting') !== 'true') { form.setAttribute('data-submitting', 'true'); - await prepareRequest(form); + try { + await prepareRequest(form); + } catch (error) { + window.logResult({ result: 'success', log: error }); + } } } @@ -83,6 +88,12 @@ const addForm = async (block) => { const formObj = document.querySelector('form'); // eslint-disable-next-line prefer-destructuring formObj.addEventListener('submit', (e) => { + if (formContent.onSubmit) { + e.preventDefault(); + formObj.dataset.action = e.currentTarget.action; + formContent.onSubmit(formObj, handleSubmit); + } + let isValid = true; if (formObj.hasAttribute('novalidate')) { isValid = formObj.checkValidity(); @@ -91,9 +102,12 @@ const addForm = async (block) => { if (isValid) { e.submitter.setAttribute('disabled', ''); formObj.dataset.action = e.currentTarget.action; + handleSubmit(formObj); } }); + + formContent.postLoad?.(formObj); }; export default async function decorate(block) { diff --git a/blocks/v2-inpage-navigation/v2-inpage-navigation.css b/blocks/v2-inpage-navigation/v2-inpage-navigation.css index 54fa31a2..df929f04 100644 --- a/blocks/v2-inpage-navigation/v2-inpage-navigation.css +++ b/blocks/v2-inpage-navigation/v2-inpage-navigation.css @@ -106,7 +106,7 @@ then we change it for the next section item in main */ } .v2-inpage-navigation__items-close:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); } .v2-inpage-navigation__dropdown-title { diff --git a/blocks/v2-media-with-tabs/v2-media-with-tabs.css b/blocks/v2-media-with-tabs/v2-media-with-tabs.css index eeec93ba..6b37f917 100644 --- a/blocks/v2-media-with-tabs/v2-media-with-tabs.css +++ b/blocks/v2-media-with-tabs/v2-media-with-tabs.css @@ -85,7 +85,7 @@ button.v2-media-with-tabs__tab:focus { } button.v2-media-with-tabs__tab:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 5px; } @@ -110,7 +110,7 @@ button.v2-media-with-tabs__tab:focus-visible { .v2-media-with-tabs__tabs-section { padding: 32px 0; } - + .v2-media-with-tabs__header-section { width: 50%; } @@ -143,7 +143,7 @@ button.v2-media-with-tabs__tab:focus-visible { .v2-media-with-tabs.v2-media-with-tabs--media-right { padding: 0 32px 0 64px; } - + .v2-media-with-tabs--media-right .v2-media-with-tabs__images-section { order: 2; } @@ -162,7 +162,7 @@ button.v2-media-with-tabs__tab:focus-visible { margin-bottom: 48px; font-size: var(--f-body-2-font-size); } - + .v2-media-with-tabs__image img { max-height: unset; } @@ -182,5 +182,5 @@ button.v2-media-with-tabs__tab:focus-visible { order: 1; width: 100%; } - + } diff --git a/blocks/v2-social-block/v2-social-block.css b/blocks/v2-social-block/v2-social-block.css index b3f0b6e0..46773046 100644 --- a/blocks/v2-social-block/v2-social-block.css +++ b/blocks/v2-social-block/v2-social-block.css @@ -2,32 +2,29 @@ padding: 40px 16px; } -.v2-social-block-container { - --social-block-padding: 24px 16px; - --social-block-gap: 16px; - --social-block-list-gap: 12px; +.redesign-v2 .section.v2-social-block-container .v2-social-block-wrapper { + padding: 0; } .v2-social-block { + --social-block-padding: 24px 16px; + --social-block-gap: 16px; + --social-block-list-gap: 12px; --social-link-color: var(--c-main-black); --social-text-color: var(--c-main-black); -} -.redesign-v2 .section .v2-social-block-wrapper { padding: var(--social-block-padding); border-radius: 8px; } -.v2-social-block-wrapper--black { +.v2-social-block--black { background-color: var(--c-main-black); -} -.v2-social-block--black { --social-link-color: var(--c-white); --social-text-color: var(--c-grey-2); } -.v2-social-block-wrapper--gray { +.v2-social-block--gray { background-color: var(--c-grey-50); } @@ -67,7 +64,7 @@ } .v2-social-block__button:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 1px; } @@ -81,7 +78,7 @@ padding: 40px 32px; } - .v2-social-block-container { + .v2-social-block { --social-block-padding: 32px; } @@ -100,8 +97,8 @@ padding: 48px 0; } - .v2-social-block-container { + .v2-social-block { --social-block-padding: 48px 40px; --social-block-gap: 32px; - } -} \ No newline at end of file + } +} diff --git a/blocks/v2-social-block/v2-social-block.js b/blocks/v2-social-block/v2-social-block.js index 3537ee72..b523ceb4 100644 --- a/blocks/v2-social-block/v2-social-block.js +++ b/blocks/v2-social-block/v2-social-block.js @@ -4,12 +4,6 @@ export default async function decorate(block) { const blockName = 'v2-social-block'; const variantClasses = ['black', 'gray']; - if (block.classList.contains('black')) { - block.parentElement.classList.add('v2-social-block-wrapper--black'); - } else if (block.classList.contains('gray')) { - block.parentElement.classList.add('v2-social-block-wrapper--gray'); - } - variantsClassesToBEM(block.classList, variantClasses, blockName); const headings = block.querySelectorAll('h1, h2, h3, h4, h5, h6'); diff --git a/blocks/v2-static-content-carousel/v2-static-content-carousel.css b/blocks/v2-static-content-carousel/v2-static-content-carousel.css index e00c4dec..f71795c3 100644 --- a/blocks/v2-static-content-carousel/v2-static-content-carousel.css +++ b/blocks/v2-static-content-carousel/v2-static-content-carousel.css @@ -86,7 +86,7 @@ /* stylelint-disable-next-line no-descending-specificity */ .v2-static-content-carousel__button:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 1px; } @@ -100,12 +100,12 @@ } .v2-static-content-carousel__button .icon-arrow-left svg, -.v2-static-content-carousel__button .icon-arrow-right svg { +.v2-static-content-carousel__button .icon-arrow-right svg { width: 48px; height: 48px; } -.v2-static-content-carousel__button:disabled .icon-arrow-right svg path, +.v2-static-content-carousel__button:disabled .icon-arrow-right svg path, .v2-static-content-carousel__button:disabled .icon-arrow-left svg path { fill: var(--c-grey-2) } @@ -172,4 +172,4 @@ background: linear-gradient(90deg, rgb(255 255 255 / 0%) 0%, var(--background-gradient) 100%); right: 0; } -} \ No newline at end of file +} diff --git a/blocks/v2-stories-carousel/v2-stories-carousel.css b/blocks/v2-stories-carousel/v2-stories-carousel.css index f0a57053..35725ce5 100644 --- a/blocks/v2-stories-carousel/v2-stories-carousel.css +++ b/blocks/v2-stories-carousel/v2-stories-carousel.css @@ -168,7 +168,7 @@ ul.v2-stories-carousel-meta { } .v2-stories-carousel-arrowcontrols button:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 1px; } diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index 3162b8ab..096ea65c 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -199,7 +199,7 @@ ul.v2-truck-lineup__navigation { } .v2-truck-lineup__navigation button:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 5px; } @@ -259,7 +259,7 @@ ul.v2-truck-lineup__navigation { } .v2-truck-lineup__arrow-controls button:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 1px; } diff --git a/common/modal/modal.css b/common/modal/modal.css index 98a4728a..dd3148ed 100644 --- a/common/modal/modal.css +++ b/common/modal/modal.css @@ -101,7 +101,7 @@ } .modal-top-bar .modal-close-button:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 5px; } diff --git a/icons/checkbox.svg b/icons/checkbox.svg new file mode 100644 index 00000000..2b2fa40b --- /dev/null +++ b/icons/checkbox.svg @@ -0,0 +1,3 @@ + + + diff --git a/placeholder.json b/placeholder.json index 6b482db4..8ea82b1d 100644 --- a/placeholder.json +++ b/placeholder.json @@ -166,7 +166,35 @@ { "Key": "Copied", "Text": "Copied!" - }, + }, + { + "Key": "event-notify:first-name", + "Text": "First name" + }, + { + "Key": "event-notify:last-name", + "Text": "Last name" + }, + { + "Key": "event-notify:zip", + "Text": "ZIP" + }, + { + "Key": "event-notify:email", + "Text": "Email" + }, + { + "Key": "event-notify:agreement", + "Text": "I agree to receive email updates from Volvo Trucks North America" + }, + { + "Key": "event-notify:notify", + "Text": "Notify me" + }, + { + "Key": "event-notify:add-event", + "Text": "Add event to calendar" + }, { "Key": "view all", "Text": "View all" diff --git a/styles/styles.css b/styles/styles.css index 661d859c..bd1498b4 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -6,7 +6,7 @@ */ @font-face { font-family: 'VolvoNovum Fallback'; - src: local("arial"); + src: local('arial'); size-adjust: 102%; ascent-override: 95%; descent-override: normal; @@ -29,7 +29,7 @@ font-weight: 400; ascent-override: 120.58%; descent-override: 23.83%; - line-gap-override: 0.00%; + line-gap-override: 0%; size-adjust: 69.25%; } @@ -40,19 +40,22 @@ src: local('Arial'); ascent-override: 93.75%; descent-override: 6.25%; - line-gap-override: 0.00%; - size-adjust: 100.00%; + line-gap-override: 0%; + size-adjust: 100%; } :root { /* fonts */ - --ff-volvo-novum-medium: 'VolvoNovum-Medium', 'VolvoNovum-Medium Fallback', sans-serif; + --ff-volvo-novum-medium: 'VolvoNovum-Medium', 'VolvoNovum-Medium Fallback', + sans-serif; --ff-volvo-novum: 'VolvoNovum', 'VolvoNovum Fallback', sans-serif; - --ff-volvo-broadprodigital: 'VolvoBroadProDigital', 'VolvoBroadProDigital Fallback', sans-serif; + --ff-volvo-broadprodigital: 'VolvoBroadProDigital', + 'VolvoBroadProDigital Fallback', sans-serif; --ff-fontawesome: 'fontawesome', 'fontawesome Fallback'; --font-family-body: var(--ff-volvo-novum); --font-family-heading: var(--ff-volvo-novum-medium); - --font-family-fixed: 'Roboto Mono', menlo, consolas, 'Liberation Mono', monospace; + --font-family-fixed: 'Roboto Mono', menlo, consolas, 'Liberation Mono', + monospace; /* REDESIGN */ --border-radius: 8px; @@ -141,32 +144,32 @@ --c-black: #000; --c-white: #fff; --c-volvo-blue: #182871; - --c-dark-blue: #202A44; - --c-leaf-1: #C8E691; - --c-leaf-2: #A8D46B; - --c-leaf-3: #8FC54E; - --c-leaf-4: #78B833; - --c-teal-1: #B8DeD8; - --c-teal-2: #8DC9BF; - --c-teal-3: #66B3A6; - --c-teal-4: #50A294; - --c-flow-1: #C3D2D6; - --c-flow-2: #96B0B6; - --c-flow-3: #678C96; + --c-dark-blue: #202a44; + --c-leaf-1: #c8e691; + --c-leaf-2: #a8d46b; + --c-leaf-3: #8fc54e; + --c-leaf-4: #78b833; + --c-teal-1: #b8ded8; + --c-teal-2: #8dc9bf; + --c-teal-3: #66b3a6; + --c-teal-4: #50a294; + --c-flow-1: #c3d2d6; + --c-flow-2: #96b0b6; + --c-flow-3: #678c96; --c-flow-4: #396976; - --c-cta-blue-default: #004FBC; - --c-cta-blue-hover: #2B8EDE; - --c-cta-blue-active: #104E9B; + --c-cta-blue-default: #004fbc; + --c-cta-blue-hover: #2b8ede; + --c-cta-blue-active: #104e9b; --c-cta-blue-visited: #346559; /* Greyscale */ - --c-grey-50: #F7F7F7; - --c-grey-100: #EDEDED; - --c-grey-200: #E1E1E1; - --c-grey-300: #D0D0D0; - --c-grey-400: #A9A9A9; - --c-grey-500: #8D8D8D; - --c-grey-600: #6E6E6E; + --c-grey-50: #f7f7f7; + --c-grey-100: #ededed; + --c-grey-200: #e1e1e1; + --c-grey-300: #d0d0d0; + --c-grey-400: #a9a9a9; + --c-grey-500: #8d8d8d; + --c-grey-600: #6e6e6e; --c-grey-700: #575757; --c-grey-800: #424242; --c-grey-900: #323232; @@ -174,12 +177,16 @@ --c-main-black: #141414; /* Success / error colors */ - --c-error-red: #C4001A; - --c-success-green: #47962D; - --c-warning-yellow: #F7D302; + --c-error-red: #BF2012; + --c-success-green: #1A6C31; + --c-warning-yellow: #FFA000; /* Global colors */ - --light-border-focus: #2B8EDE; + --border-subtle: var(--c-grey-200); + --border-strong: var(--c-grey-400); + --border-hover: #2b8ede; + --border-active: #004FBC; + --border-focus: #2b8ede; /* Motions */ --duration-small: 160ms; @@ -206,10 +213,10 @@ --btn-border-hover: #030333; /* Greyscale */ - --c-grey-1: #E1DFDD; - --c-grey-2: #A7A8A9; - --c-grey-3: #888B8D; - --c-grey-4: #53565A; + --c-grey-1: #e1dfdd; + --c-grey-2: #a7a8a9; + --c-grey-3: #888b8d; + --c-grey-4: #53565a; /* buttons */ --btn-padding: 12px 32px; @@ -230,7 +237,7 @@ --heading-font-size-l: calc(4.8rem * var(--heading-font-size-scale)); --heading-font-size-ml: calc(3.2rem * var(--heading-font-size-scale)); --heading-font-size-m: calc(2.4rem * var(--heading-font-size-scale)); - --heading-font-size-s: calc(2.0rem * var(--heading-font-size-scale)); + --heading-font-size-s: calc(2rem * var(--heading-font-size-scale)); --heading-font-size-xs: calc(1.8rem * var(--heading-font-size-scale)); /* @@ -333,7 +340,7 @@ header.sub-nav-wrapper { header .sub-nav.block { height: var(--sub-nav-height); - background-color: #7F7F7F; + background-color: #7f7f7f; z-index: 1020; left: 0; right: 0; @@ -358,8 +365,16 @@ main.center :where(.block) { text-align: initial; } -h1, h2, h3, -h4, h5, h6 { +main.center :where(.block) { + text-align: initial; +} + +h1, +h2, +h3, +h4, +h5, +h6 { font-family: var(--font-family-heading); font-weight: normal; line-height: 1; @@ -426,7 +441,12 @@ h1#volvo-trucks-magazine { font-size: 60px; } -p, dl, ol, ul, pre, blockquote { +p, +dl, +ol, +ul, +pre, +blockquote { margin-top: 1em; margin-bottom: 1.5em; font-size: var(--body-font-size-xs); @@ -454,13 +474,16 @@ a:hover { color: var(--c-cta-blue-default); } -code, pre, samp { +code, +pre, +samp { font-family: var(--font-family-fixed); font-size: var(--body-font-size-s); } -code, samp { - padding: .125em; +code, +samp { + padding: 0.125em; } pre { @@ -473,7 +496,8 @@ strong { /* buttons */ -a.button:any-link, button { +a.button:any-link, +button { font-family: var(--ff-volvo-novum-medium); box-sizing: border-box; text-decoration: none; @@ -513,18 +537,23 @@ a.button.primary.dark:hover { color: var(--c-white); } -button, input, select, textarea { +button, +input, +select, +textarea { color: inherit; font: inherit; margin: 0; } -button:disabled, button:disabled:hover { +button:disabled, +button:disabled:hover { background-color: var(--overlay-background-color); cursor: unset; } -a.button.primary:hover, button.primary:hover { +a.button.primary:hover, +button.primary:hover { background-color: var(--btn-background-hover); border-color: var(--btn-border-hover); color: var(--text-color); @@ -564,7 +593,7 @@ div:where([role="dialog"]) pre, main pre { background-color: var(--overlay-background-color); padding: 1em; - border-radius: .25em; + border-radius: 0.25em; overflow-x: auto; white-space: pre; } @@ -579,13 +608,13 @@ main blockquote { div:where([role="dialog"]) blockquote p::before, main blockquote p::before { - content: "“"; + content: '“'; line-height: 0; } div:where([role="dialog"]) blockquote p::after, main blockquote p::after { - content: "”"; + content: '”'; line-height: 0; } @@ -600,7 +629,7 @@ div:where([role="dialog"]) img, main img { max-width: 100%; height: auto; - vertical-align: middle + vertical-align: middle; } div:where([role="dialog"]) p img, @@ -622,7 +651,7 @@ main p img { .redesign-v2 a.button:focus-visible, .redesign-v2 button:focus-visible { - outline: 2px solid var(--light-border-focus); + outline: 2px solid var(--border-focus); outline-offset: 2px; } @@ -694,7 +723,8 @@ main .section.highlight { background-color: var(--highlight-background-color); } -body.disable-scroll, body.disable-scroll main { +body.disable-scroll, +body.disable-scroll main { overflow: hidden; } @@ -843,28 +873,28 @@ a.button.text-link-with-video::after { /* pagination */ .pager .pagination ol li .last::before { font-family: var(--ff-fontawesome); - content: "\f051"; + content: '\f051'; font-size: 12px; text-decoration: none; } .pager .pagination ol li .first::before { font-family: var(--ff-fontawesome); - content: "\f048"; + content: '\f048'; font-size: 12px; text-decoration: none; } .pager .pagination ol li .next::before { font-family: var(--ff-fontawesome); - content: "\f054"; + content: '\f054'; font-size: 12px; text-decoration: none; } .pager .pagination ol li .prev::before { font-family: var(--ff-fontawesome); - content: "\f053"; + content: '\f053'; font-size: 12px; text-decoration: none; } @@ -1044,7 +1074,12 @@ main.blue-contract .section.section-with-title p { padding: 0; } -.redesign-v2 body, .redesign-v2 dl, .redesign-v2 ol, .redesign-v2 ul, .redesign-v2 pre, .redesign-v2 blockquote { +.redesign-v2 body, +.redesign-v2 dl, +.redesign-v2 ol, +.redesign-v2 ul, +.redesign-v2 pre, +.redesign-v2 blockquote { font-size: var(--f-body-font-size); line-height: var(--f-body-line-height); letter-spacing: var(--f-body-letter-spacing); @@ -1066,7 +1101,8 @@ main.blue-contract .section.section-with-title p { } .redesign-v2 .section:first-child, -.section:not(.section--black-background, .section--gray-background) + .section:not(.section--black-background, .section--gray-background) { +.section:not(.section--black-background, .section--gray-background) + + .section:not(.section--black-background, .section--gray-background) { padding-top: 0; } @@ -1198,6 +1234,7 @@ main.blue-contract .section.section-with-title p { .redesign-v2 button.secondary { border-color: var(--c-main-black); color: var(--c-main-black); + background-color: transparent; } .redesign-v2 a.button.secondary:hover, @@ -1301,7 +1338,7 @@ main.blue-contract .section.section-with-title p { .redesign-v2 .section--black-background button.marketing-cta, .redesign-v2 a.button.marketing-cta.dark, .redesign-v2 button.marketing-cta.dark { - background-color: #44A1FF; + background-color: #44a1ff; color: var(--c-main-black); } @@ -1309,14 +1346,14 @@ main.blue-contract .section.section-with-title p { .redesign-v2 .section--black-background button.marketing-cta:hover, .redesign-v2 a.button.marketing-cta.dark:hover, .redesign-v2 button.marketing-cta.dark:hover { - background-color: #76BAFF; + background-color: #76baff; } .redesign-v2 .section--black-background a.button.marketing-cta:active, .redesign-v2 .section--black-background button.marketing-cta:active, .redesign-v2 a.button.marketing-cta.dark:active, .redesign-v2 button.marketing-cta.dark:active { - background-color: #1F78D1; + background-color: #1f78d1; } .redesign-v2 :is(h1, h2, h3, h4, h5, h6) a:any-link::after, @@ -1420,6 +1457,65 @@ main.blue-contract .section.section-with-title p { border-color: var(--c-grey-2); } +.redesign-v2 .checkbox-with-label { + position: relative; +} + +.redesign-v2 .checkbox-with-label input[type='checkbox'] { + left: 3px; + top: 3px; + width: 18px; + height: 18px; + position: absolute; + opacity: 1; + margin: 0; + z-index: -1; +} + +.redesign-v2 .checkbox-with-label input[type='checkbox'] + label { + display: flex; + gap: 10px; + user-select: none; + font-size: var(--f-button-font-size); + letter-spacing: var(--f-button-letter-spacing); + color: var(--text-color); + font-family: var(--font-family-body); + line-height: 100%; + margin: 0; + align-items: center; + + --checkbox-margin: 3px; + --checkbox-size: 18px; +} + +.redesign-v2 .checkbox-with-label input[type='checkbox'] + label::before { + content: ''; + display: inline-block; + width: var(--checkbox-size); + min-width: var(--checkbox-size); + height: var(--checkbox-size); + min-height: var(--checkbox-size); + border: 2px solid var(--border-strong); + border-radius: 2px; + margin: var(--checkbox-margin); + align-self: flex-start; +} + +.redesign-v2 .checkbox-with-label input[type='checkbox']:checked + label::before { + border-color: transparent; +} + +.redesign-v2 .checkbox-with-label input[type='checkbox']:checked + label { + background: url('/icons/checkbox.svg') no-repeat; + background-position-x: var(--checkbox-margin); + background-position-y: var(--checkbox-margin); +} + +.redesign-v2 .checkbox-with-label input[type='checkbox']:focus-visible + label::before { + outline: 2px solid var(--border-focus); + outline-offset: 1px; +} + .redesign-v2 .pretitle { color: var(--text-color); font-feature-settings: 'clig' off, 'liga' off; From 48c4984e2f77e3708c2b17551a4303a202f1fa85 Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Fri, 1 Dec 2023 13:21:27 +0100 Subject: [PATCH 11/41] rename block --- .../v2-resource-gallery.css} | 70 +++++++++---------- .../v2-resource-gallery.js} | 4 +- 2 files changed, 37 insertions(+), 37 deletions(-) rename blocks/{v2-resources-gallery/v2-resources-gallery.css => v2-resource-gallery/v2-resource-gallery.css} (59%) rename blocks/{v2-resources-gallery/v2-resources-gallery.js => v2-resource-gallery/v2-resource-gallery.js} (97%) diff --git a/blocks/v2-resources-gallery/v2-resources-gallery.css b/blocks/v2-resource-gallery/v2-resource-gallery.css similarity index 59% rename from blocks/v2-resources-gallery/v2-resources-gallery.css rename to blocks/v2-resource-gallery/v2-resource-gallery.css index 910d39b0..49b80ef6 100644 --- a/blocks/v2-resources-gallery/v2-resources-gallery.css +++ b/blocks/v2-resource-gallery/v2-resource-gallery.css @@ -1,10 +1,10 @@ -.v2-resources-gallery { +.v2-resource-gallery { display: flex; gap: 40px; flex-flow: column; } -.v2-resources-gallery__heading-wrapper { +.v2-resource-gallery__heading-wrapper { margin-bottom: 0; display: flex; flex-flow: column; @@ -12,7 +12,7 @@ gap: 8px; } -.v2-resources-gallery__button { +.v2-resource-gallery__button { display: flex; font-size: var(--f-button-font-size); background: none; @@ -20,29 +20,29 @@ align-self: flex-start; } -.v2-resources-gallery__button .icon-minus { +.v2-resource-gallery__button .icon-minus { display: none; } -.v2-resources-gallery__button[aria-expanded="true"] .icon-minus { +.v2-resource-gallery__button[aria-expanded="true"] .icon-minus { display: block; } -.v2-resources-gallery__button[aria-expanded="true"] .icon-plus { +.v2-resource-gallery__button[aria-expanded="true"] .icon-plus { display: none; } -.v2-resources-gallery__heading { +.v2-resource-gallery__heading { font: var(--f-heading-5-font-size) / var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); letter-spacing: 0.25px; margin-bottom: 0; } -.v2-resources-gallery__heading-wrapper .v2-resources-gallery__heading { +.v2-resource-gallery__heading-wrapper .v2-resource-gallery__heading { text-transform: capitalize; } -.v2-resources-gallery__video-list { +.v2-resource-gallery__video-list { display: grid; list-style-type: none; gap: 8px; @@ -55,17 +55,17 @@ } /* Hide scrollbar for Chrome, Safari and Opera */ -.v2-resources-gallery__video-list::-webkit-scrollbar { +.v2-resource-gallery__video-list::-webkit-scrollbar { display: none; } -.v2-resources-gallery__video-list-item { +.v2-resource-gallery__video-list-item { scroll-snap-align: start; display: flex; flex-direction: column; } -.v2-resources-gallery__video-title { +.v2-resource-gallery__video-title { font: var(--f-body-2-font-size) / var(--f-body-2-line-height) var(--ff-volvo-novum); letter-spacing: var(--f-body-2-letter-spacing); white-space: nowrap; @@ -75,18 +75,18 @@ margin-bottom: 8px; } -.v2-resources-gallery__video-list p { +.v2-resource-gallery__video-list p { font: var(--f-body-font-size) / var(--f-body-line-height) var(--ff-volvo-novum); letter-spacing: var(--f-body-letter-spacing); color: var(--text-subtle); margin: 0; } -.v2-resources-gallery__video-list .v2-resources-gallery__video-image { +.v2-resource-gallery__video-list .v2-resource-gallery__video-image { margin: 0 0 16px; } -.v2-resources-gallery__video-image img { +.v2-resource-gallery__video-image img { border-radius: 8px; aspect-ratio: 255/143; object-fit: cover; @@ -94,21 +94,21 @@ height: 100%; } -.v2-resources-gallery__document-list { +.v2-resource-gallery__document-list { list-style-type: none; display: grid; grid-template-columns: repeat(2, 1fr); gap: 40px 32px; } -.v2-resources-gallery__document-link-wrapper { +.v2-resource-gallery__document-link-wrapper { display: flex; flex-flow: column; gap: 8px; margin: 0; } -.v2-resources-gallery__document-list a.button.standalone-link { +.v2-resource-gallery__document-list a.button.standalone-link { text-decoration: underline; text-underline-offset: 4px; text-transform: capitalize; @@ -116,77 +116,77 @@ gap: 8px; } -.v2-resources-gallery__heading-wrapper button.v2-resources-gallery__button.tertiary:hover { +.v2-resource-gallery__heading-wrapper button.v2-resource-gallery__button.tertiary:hover { background-color: transparent; outline: none; text-decoration: none; } -.v2-resources-gallery__document-link-wrapper p { +.v2-resource-gallery__document-link-wrapper p { margin: 0; color: var(--text-subtle); padding-left: 32px; } -.v2-resources-gallery__document-list-item--hide, -.v2-resources-gallery__video-list-item--hide { +.v2-resource-gallery__document-list-item--hide, +.v2-resource-gallery__video-list-item--hide { display: none; } -.v2-resources-gallery__list--expand .v2-resources-gallery__video-list { +.v2-resource-gallery__list--expand .v2-resource-gallery__video-list { grid-template-columns: minmax(0, 1fr); grid-auto-flow: row; grid-row-gap: 40px; } -.v2-resources-gallery__list--expand .v2-resources-gallery__document-list-item--hide, -.v2-resources-gallery__list--expand .v2-resources-gallery__video-list-item--hide { +.v2-resource-gallery__list--expand .v2-resource-gallery__document-list-item--hide, +.v2-resource-gallery__list--expand .v2-resource-gallery__video-list-item--hide { display: flex; } @media (min-width: 744px) { - .v2-resources-gallery__heading-wrapper { + .v2-resource-gallery__heading-wrapper { flex-flow: row; gap: 40px; align-items: center; } - .v2-resources-gallery__heading { + .v2-resource-gallery__heading { flex: 1; } - .v2-resources-gallery__video-list { + .v2-resource-gallery__video-list { grid-template-columns: repeat(6, calc(100% / 2.80)); gap:16px; } - .v2-resources-gallery__document-list { + .v2-resource-gallery__document-list { grid-template-columns: repeat(3, 1fr); grid-column-gap: inherit; } - .v2-resources-gallery__list--expand .v2-resources-gallery__video-list { + .v2-resource-gallery__list--expand .v2-resource-gallery__video-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (min-width: 1200px) { - .v2-resources-gallery__video-list { + .v2-resource-gallery__video-list { gap: 40px; overflow-x: unset; } - .v2-resources-gallery__video-list, - .v2-resources-gallery__list--expand .v2-resources-gallery__video-list { + .v2-resource-gallery__video-list, + .v2-resource-gallery__list--expand .v2-resource-gallery__video-list { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .v2-resources-gallery__document-link-wrapper { + .v2-resource-gallery__document-link-wrapper { flex-flow: row; align-items: center; } - .v2-resources-gallery__document-link-wrapper p { + .v2-resource-gallery__document-link-wrapper p { padding-left: 0; } } diff --git a/blocks/v2-resources-gallery/v2-resources-gallery.js b/blocks/v2-resource-gallery/v2-resource-gallery.js similarity index 97% rename from blocks/v2-resources-gallery/v2-resources-gallery.js rename to blocks/v2-resource-gallery/v2-resource-gallery.js index d6634273..65474bb0 100644 --- a/blocks/v2-resources-gallery/v2-resources-gallery.js +++ b/blocks/v2-resource-gallery/v2-resource-gallery.js @@ -6,12 +6,12 @@ import { } from '../../scripts/video-helper.js'; import { createElement, getTextLabel, unwrapDivs } from '../../scripts/common.js'; -const blockName = 'v2-resources-gallery'; +const blockName = 'v2-resource-gallery'; export default function decorate(block) { const blockHeading = block.querySelector('div:first-child'); blockHeading.classList.add(`${blockName}__heading-wrapper`); - const title = blockHeading.querySelector('h4'); + const title = blockHeading.querySelector('h1, h2, h3, h4, h5, h6'); title?.classList.add(`${blockName}__heading`); unwrapDivs(blockHeading); From b60c9c578ff950f6422f5651960469fed6d362ed Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:14:25 +0100 Subject: [PATCH 12/41] Inpage navigation refactoring #262 (#273) * refactor alignment and typography * refactor icon usage * fix overlay, color --- .../v2-inpage-navigation.css | 41 ++++++++++--------- .../v2-inpage-navigation.js | 18 ++++---- styles/styles.css | 1 + 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/blocks/v2-inpage-navigation/v2-inpage-navigation.css b/blocks/v2-inpage-navigation/v2-inpage-navigation.css index df929f04..f0677923 100644 --- a/blocks/v2-inpage-navigation/v2-inpage-navigation.css +++ b/blocks/v2-inpage-navigation/v2-inpage-navigation.css @@ -1,5 +1,5 @@ :root { - --inpage-navigation-height: 72px; + --inpage-navigation-height: 58px; } .v2-inpage-navigation-wrapper { @@ -11,7 +11,7 @@ top: var(--nav-height); width: 100%; z-index: var(--z-index-inpage-nav); - transition: transform var(--duration-medium) ease-in-out; + transition: transform var(--duration-medium) ease-in-out, z-index var(--duration-medium) ease-in-out; } /* As the first section in main doesn't need to have padding-top @@ -21,6 +21,10 @@ then we change it for the next section item in main */ padding-top: 0; } +.v2-inpage-navigation--open { + z-index: var(--z-index-inpage-nav-open); +} + .v2-inpage-navigation--open .v2-inpage-navigation__items-container { transform: translateY(-100%); transition: transform var(--duration-large) var(--easing-entrance); @@ -33,12 +37,13 @@ then we change it for the next section item in main */ .v2-inpage-navigation { border-width: 1px 0; border-style: solid; - border-color: #EFEFEF; + border-color: var(--c-grey-50); + height: var(--inpage-navigation-height); } .v2-inpage-navigation__wrapper { display: flex; - padding: 16px; + padding: 8px 16px; } .v2-inpage-navigation__dropdown { @@ -100,9 +105,11 @@ then we change it for the next section item in main */ height: 44px; } -.v2-inpage-navigation__items-close svg { +.v2-inpage-navigation__items-close .icon svg { display: block; margin: auto; + width: 24px; + height: 24px; } .v2-inpage-navigation__items-close:focus-visible { @@ -127,9 +134,10 @@ then we change it for the next section item in main */ .v2-inpage-navigation__item button, .v2-inpage-navigation__selected-item-wrapper { + padding: 0; background: none; border: 0; - color: var(--c-main-black); + color: var(--c-grey-4); cursor: pointer; display: block; font-family: var(--ff-volvo-novum); @@ -139,23 +147,19 @@ then we change it for the next section item in main */ margin: 0; width: 100%; border-radius: 1px; - } +} .v2-inpage-navigation__item button:focus-visible { outline-offset: 5px; } /* stylelint-disable-next-line no-descending-specificity */ -.v2-inpage-navigation__selected-item-wrapper, .v2-inpage-navigation__item--active button, .v2-inpage-navigation__item button:hover, .v2-inpage-navigation__item button:active, -.v2-inpage-navigation__item button:focus, -.v2-inpage-navigation__selected-item-wrapper:hover, -.v2-inpage-navigation__selected-item-wrapper:active -.v2-inpage-navigation__selected-item-wrapper:focus, -.v2-inpage-navigation__dropdown--open .v2-inpage-navigation__selected-item-wrapper { +.v2-inpage-navigation__item button:focus { font-family: var(--ff-volvo-novum-medium); + color: var(--c-main-black); } /* stylelint-disable-next-line no-descending-specificity */ @@ -170,10 +174,10 @@ then we change it for the next section item in main */ gap: 8px; } -.v2-inpage-navigation__selected-item-wrapper svg { - height: 16px; +.v2-inpage-navigation__selected-item-wrapper .icon svg { + width: 24px; + height: 24px; transition: transform var(--duration-small) var(--easing-standard); - width: 16px; } .v2-inpage-navigation__dropdown--open .v2-inpage-navigation__selected-item-wrapper svg { @@ -184,7 +188,7 @@ then we change it for the next section item in main */ .redesign-v2 a.button.v2-inpage-navigation__cta:any-link { height: auto; - padding: 8px 20px; + padding: 10px 20px; line-height: var(--f-button-line-height); } @@ -207,7 +211,7 @@ then we change it for the next section item in main */ .v2-inpage-navigation__wrapper { align-items: center; max-width: var(--wrapper-width); - padding: 11px 0; + padding: 10px 0; margin: 0 auto; } @@ -260,7 +264,6 @@ then we change it for the next section item in main */ .v2-inpage-navigation__item button { position: relative; - color: var(--c-grey-4); padding: 12px 0; } diff --git a/blocks/v2-inpage-navigation/v2-inpage-navigation.js b/blocks/v2-inpage-navigation/v2-inpage-navigation.js index 40869152..062a7b7d 100644 --- a/blocks/v2-inpage-navigation/v2-inpage-navigation.js +++ b/blocks/v2-inpage-navigation/v2-inpage-navigation.js @@ -1,4 +1,4 @@ -import { getMetadata } from '../../scripts/lib-franklin.js'; +import { decorateIcons, getMetadata } from '../../scripts/lib-franklin.js'; import { createElement, getTextLabel, debounce } from '../../scripts/common.js'; const blockName = 'v2-inpage-navigation'; @@ -138,12 +138,9 @@ export default async function decorate(block) { sectionTitle.innerText = inpageTitle; const listCloseButton = createElement('button', { classes: `${blockName}__items-close` }); - const closeIcon = document.createRange().createContextualFragment(` - - - `); + const closeIcon = createElement('span', { classes: ['icon', 'icon-close'] }); - listCloseButton.appendChild(...closeIcon.children); + listCloseButton.appendChild(closeIcon); listContainer.appendChild(listCloseButton); const submenuTitle = getTextLabel('Section'); @@ -165,13 +162,10 @@ export default async function decorate(block) { list.appendChild(listItem); }); - const dropdownArrowIcon = document.createRange().createContextualFragment(` - - - `); + const dropdownArrowIcon = createElement('span', { classes: ['icon', 'icon-chevron-down'] }); selectedItemWrapper.append(selectedItem); - selectedItemWrapper.appendChild(...dropdownArrowIcon.children); + selectedItemWrapper.appendChild(dropdownArrowIcon); dropdownWrapper.append(selectedItemWrapper); listContainer.append(list); @@ -182,6 +176,8 @@ export default async function decorate(block) { itemsWrapper.remove(); + decorateIcons(wrapper); + if (ctaButton) { wrapper.appendChild(ctaButton); } diff --git a/styles/styles.css b/styles/styles.css index bd1498b4..84676132 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -295,6 +295,7 @@ /* Zindex inpage navigation */ --z-index-inpage-nav: 1049; + --z-index-inpage-nav-open: 1051; } *, From c14139c100ed9a3de6b4d23c37b96d902c6b32bf Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Mon, 4 Dec 2023 17:14:45 +0100 Subject: [PATCH 13/41] change url and sharepoint to upstream urls --- .github/pull_request_template.md | 4 ++-- README.md | 4 ++-- fstab.yaml | 2 +- tools/importer/import.js | 2 +- tools/importer/sub-nav.js | 2 +- tools/sidekick/config.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e11cf1b4..12b02349 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,5 +3,5 @@ Please always provide the [GitHub issue(s)](../issues) your PR is for, as well a Fix # Test URLs: -- Before: https://main--vg-volvotrucks-us--hlxsites.hlx.page/ -- After: https://--vg-volvotrucks-us--hlxsites.hlx.page/ +- Before: https://main--vg-volvotrucks-us-rd--netcentric.hlx.page/ +- After: https://--vg-volvotrucks-us-rd--netcentric.hlx.page/ diff --git a/README.md b/README.md index f35bce0d..9a0ecfff 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ Franklin site redesign for volvotrucks.us ## Environments -- Preview: https://main--vg-volvotrucks-us--hlxsites.hlx.page/ -- Live: https://main--vg-volvotrucks-us--hlxsites.hlx.live/ +- Preview: https://main--vg-volvotrucks-us-rd--netcentric.hlx.page/ +- Live: https://main--vg-volvotrucks-us-rd--netcentric.hlx.live/ ## Installation diff --git a/fstab.yaml b/fstab.yaml index 2f99f23d..d015d040 100644 --- a/fstab.yaml +++ b/fstab.yaml @@ -1,2 +1,2 @@ mountpoints: - /: https://adobe.sharepoint.com/sites/HelixProjects/Shared%20Documents/sites/VolvoGroup/vg-volvotrucks-us + /: https://adobe.sharepoint.com/sites/HelixProjects/Shared%20Documents/sites/VolvoGroup/vg-volvotrucks-us-redesign diff --git a/tools/importer/import.js b/tools/importer/import.js index 752fd9a6..e43e9673 100644 --- a/tools/importer/import.js +++ b/tools/importer/import.js @@ -74,7 +74,7 @@ const linkToHlxPage = (main, document, url) => { if (new RegExp('^(https?:)?//').test(link.href)) { // leave links with domains as is } else if (link.href.startsWith('/')) { - const newUrl = new URL(link.href, 'https://main--vg-volvotrucks-us--hlxsites.hlx.page'); + const newUrl = new URL(link.href, 'https://main--vg-volvotrucks-us-rd--netcentric.hlx.page'); link.href = newUrl.href; } }); diff --git a/tools/importer/sub-nav.js b/tools/importer/sub-nav.js index 28dcc7ee..5a25cc71 100644 --- a/tools/importer/sub-nav.js +++ b/tools/importer/sub-nav.js @@ -40,7 +40,7 @@ const linkToHlxPage = (main, document, url) => { if (new RegExp('^(https?:)?//').test(link.href)) { // leave links with domains as is } else if (link.href.startsWith('/')) { - const newUrl = new URL(link.href, 'https://main--vg-volvotrucks-us--hlxsites.hlx.page'); + const newUrl = new URL(link.href, 'https://main--vg-volvotrucks-us-rd--netcentric.hlx.page'); link.href = newUrl.href; } }); diff --git a/tools/sidekick/config.json b/tools/sidekick/config.json index 0d193841..8515d657 100644 --- a/tools/sidekick/config.json +++ b/tools/sidekick/config.json @@ -12,7 +12,7 @@ "id": "library", "title": "Library", "environments": [ "edit" ], - "url": "https://main--vg-volvotrucks-us--hlxsites.hlx.live/tools/sidekick/library.html", + "url": "https://main--vg-volvotrucks-us-rd--netcentric.hlx.live/tools/sidekick/library.html", "includePaths": [ "**.docx**" ] }, { From 5c8a978b995801c80f5d8de126545499d7ae6f7c Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Tue, 5 Dec 2023 11:59:46 +0100 Subject: [PATCH 14/41] fix standalone button, resource gallery --- blocks/v2-resource-gallery/v2-resource-gallery.css | 9 +++++---- icons/documents.svg | 2 +- styles/styles.css | 5 +---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/blocks/v2-resource-gallery/v2-resource-gallery.css b/blocks/v2-resource-gallery/v2-resource-gallery.css index 49b80ef6..23ef3bd3 100644 --- a/blocks/v2-resource-gallery/v2-resource-gallery.css +++ b/blocks/v2-resource-gallery/v2-resource-gallery.css @@ -39,7 +39,7 @@ } .v2-resource-gallery__heading-wrapper .v2-resource-gallery__heading { - text-transform: capitalize; + text-transform: none; } .v2-resource-gallery__video-list { @@ -110,10 +110,11 @@ .v2-resource-gallery__document-list a.button.standalone-link { text-decoration: underline; - text-underline-offset: 4px; - text-transform: capitalize; - display: flex; + text-underline-offset: 3px; + align-items: flex-start; gap: 8px; + white-space: normal; + text-align: left; } .v2-resource-gallery__heading-wrapper button.v2-resource-gallery__button.tertiary:hover { diff --git a/icons/documents.svg b/icons/documents.svg index 3cd852fd..b5dbb2b8 100644 --- a/icons/documents.svg +++ b/icons/documents.svg @@ -1,3 +1,3 @@ - + diff --git a/styles/styles.css b/styles/styles.css index 84676132..24d4f244 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1151,6 +1151,7 @@ main.blue-contract .section.section-with-title p { gap: 8px; letter-spacing: var(--f-body-letter-spacing); line-height: var(--f-body-line-height); + border-radius: 1px; } .redesign-v2 a.standalone-link:hover { @@ -1161,10 +1162,6 @@ main.blue-contract .section.section-with-title p { color: var(--c-cta-blue-active); } -.redesign-v2 a.standalone-link:visited { - color: var(--c-cta-blue-visited); -} - .redesign-v2 a.button.primary, .redesign-v2 button.primary, .redesign-v2 a.button.secondary, From 080974d8cb4a7e90bc9effabdf04cf68c4f5a9d8 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:49:02 +0100 Subject: [PATCH 15/41] Resource Gallery - Video in the video pop-up is cutoff #276 (#277) * refactor icon usage * remove redundant close button * fix video size --- common/modal/modal.css | 40 +++++++++++++++++++++++++++++++++++++++- common/modal/modal.js | 15 ++++----------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/common/modal/modal.css b/common/modal/modal.css index dd3148ed..abf16302 100644 --- a/common/modal/modal.css +++ b/common/modal/modal.css @@ -48,6 +48,10 @@ position: relative; transform: translateY(-100vh); transition: transform 0.3s ease-out; +} + +:root:not(.redesign-v2) .modal-content { + margin-top: 100px; background-color: var(--c-grey-1); } @@ -105,7 +109,7 @@ outline-offset: 5px; } -.modal-top-bar svg { +.modal-top-bar .icon svg { height: 24px; width: 24px; } @@ -179,6 +183,7 @@ gap: 2%; } +/* stylelint-disable-next-line no-descending-specificity */ .modal-content:has(.modal-soundcloud) { aspect-ratio: unset; } @@ -199,3 +204,36 @@ width: 100%; height: unset; } + +.redesign-v2 .modal-content:has(.modal-video) { + padding: 0 16px; + max-width: calc(var(--wrapper-width) + 32px); +} + +.redesign-v2 .modal-video { + width: calc(100% - 32px); + height: auto; + border-radius: 8px 8px 0 0; +} + +@media (min-width: 744px) { + .redesign-v2 .modal-content:has(.modal-video) { + padding: 0 32px; + } + + .redesign-v2 .modal-video { + width: calc(100% - 64px); + } +} + +@media (min-width: 1200px) { + .redesign-v2 .modal-content:has(.modal-video) { + padding: 0; + margin: 0 auto; + max-width: var(--wrapper-width); + } + + .redesign-v2 .modal-video { + width: 100%; + } +} diff --git a/common/modal/modal.js b/common/modal/modal.js index 853f3957..cb4e8efc 100644 --- a/common/modal/modal.js +++ b/common/modal/modal.js @@ -1,5 +1,5 @@ import { createElement, getTextLabel } from '../../scripts/common.js'; -import { loadCSS } from '../../scripts/lib-franklin.js'; +import { decorateIcons, loadCSS } from '../../scripts/lib-franklin.js'; // eslint-disable-next-line import/no-cycle import { createIframe, isLowResolutionVideoUrl } from '../../scripts/video-helper.js'; @@ -16,15 +16,14 @@ const createModalTopBar = (parentEl) => { `); + decorateIcons(topBar); parentEl.prepend(...topBar.children); // eslint-disable-next-line no-use-before-define parentEl.querySelector('.modal-close-button').addEventListener('click', () => hideModal()); @@ -126,12 +125,6 @@ const createModal = () => { bannerWrapper.classList.add('modal-before-banner'); bannerWrapper.addEventListener('click', (event) => event.stopPropagation()); bannerWrapper.appendChild(beforeBanner); - const closeButton = document.createElement('button'); - closeButton.classList.add('modal-close-button'); - closeButton.innerHTML = ''; - bannerWrapper.appendChild(closeButton); - // eslint-disable-next-line no-use-before-define - closeButton.addEventListener('click', () => hideModal()); videoOrIframe.before(bannerWrapper); } From 2cbaaee3d009601c37a083a8368cf83f6262bab1 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:47:09 +0100 Subject: [PATCH 16/41] Checkbox button is incorrectly displayed and slightly overlaps the text #278 (#280) * fix checkbox on iOS * add focus style --- styles/styles.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/styles/styles.css b/styles/styles.css index 24d4f244..6dd38609 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1455,6 +1455,14 @@ main.blue-contract .section.section-with-title p { border-color: var(--c-grey-2); } +.redesign-v2 div:where([role="dialog"]) input:focus, +.redesign-v2 main input:focus, +.redesign-v2 div:where([role="dialog"]) input:focus-visible, +.redesign-v2 main input:focus-visible { + outline: 2px solid var(--border-focus); + outline-offset: 2px; +} + .redesign-v2 .checkbox-with-label { position: relative; } @@ -1468,6 +1476,13 @@ main.blue-contract .section.section-with-title p { opacity: 1; margin: 0; z-index: -1; + appearance: none; + border: 0; +} + +.redesign-v2 .checkbox-with-label input[type='checkbox']:focus, +.redesign-v2 .checkbox-with-label input[type='checkbox']:focus-visible { + outline: none; } .redesign-v2 .checkbox-with-label input[type='checkbox'] + label { @@ -1509,6 +1524,7 @@ main.blue-contract .section.section-with-title p { background-position-y: var(--checkbox-margin); } +.redesign-v2 .checkbox-with-label input[type='checkbox']:focus + label::before, .redesign-v2 .checkbox-with-label input[type='checkbox']:focus-visible + label::before { outline: 2px solid var(--border-focus); outline-offset: 1px; From 58fc439b8a4c47ccfecd7b3617ac96452aa6b377 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:15:03 +0100 Subject: [PATCH 17/41] Error prompts on empty / invalid fields are not correctly displayed on Chrome with version below 119 #279 (#281) * #279 - Add :invalid when :user-invalid is not supported --- blocks/v2-event-notify/v2-event-notify.css | 8 ++++++++ blocks/v2-event-notify/v2-event-notify.js | 11 ++++++++--- blocks/v2-forms/forms/event-notify.js | 19 ++++++++++++++----- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/blocks/v2-event-notify/v2-event-notify.css b/blocks/v2-event-notify/v2-event-notify.css index 1d3cbf3f..94f13b8c 100644 --- a/blocks/v2-event-notify/v2-event-notify.css +++ b/blocks/v2-event-notify/v2-event-notify.css @@ -94,6 +94,14 @@ border-color: var(--c-error-red); } +@supports not selector(:user-invalid) { + .v2-event-notify__container input:invalid, + .v2-event-notify__container input:invalid:hover, + .v2-event-notify__container input:invalid:focus { + border-color: var(--c-error-red); + } +} + .v2-event-notify__container .event-notify__add-event-button { cursor: pointer; } diff --git a/blocks/v2-event-notify/v2-event-notify.js b/blocks/v2-event-notify/v2-event-notify.js index baeea051..549a91ec 100644 --- a/blocks/v2-event-notify/v2-event-notify.js +++ b/blocks/v2-event-notify/v2-event-notify.js @@ -162,9 +162,14 @@ export default async function decorate(block) { // we can inject the policy content when form content loaded const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { - const formRef = [...mutation.addedNodes]; - const policyEl = formRef.find((el) => el.querySelector('.event-notify__policy'))?.querySelector('.event-notify__policy'); - const calendarButtonEl = formRef.find((el) => el.querySelector('.event-notify__add-event-button'))?.querySelector('.event-notify__add-event-button'); + const formRef = [...mutation.addedNodes].find((el) => el instanceof Element && el.classList.contains('v2-forms__container')); + + if (!formRef) { + return; + } + + const policyEl = formRef.querySelector('.event-notify__policy'); + const calendarButtonEl = formRef.querySelector('.event-notify__add-event-button'); if (formRef) { policyEl.append(policyText); diff --git a/blocks/v2-forms/forms/event-notify.js b/blocks/v2-forms/forms/event-notify.js index 32447b30..45efcc4a 100644 --- a/blocks/v2-forms/forms/event-notify.js +++ b/blocks/v2-forms/forms/event-notify.js @@ -42,13 +42,16 @@ const formContent = ` `; -const checkFieldValidity = (field) => { +const checkFieldValidity = (field, useUserInvalid = true) => { const errorMessageEl = field.parentElement.querySelector(`:scope > .${formName}__error-message`); if (errorMessageEl) { - const isUserInvalid = field.parentElement.querySelector(':scope:user-invalid') === field; - errorMessageEl.innerText = isUserInvalid ? '' : field.validationMessage; - errorMessageEl.classList[isUserInvalid ? 'add' : 'remove'](`${formName}__error-message--hidden`); + const isSupportingUserInvalid = CSS.supports('selector(:user-invalid)'); + const invalidSelector = isSupportingUserInvalid && useUserInvalid ? ':user-invalid' : ':invalid'; + const isInvalid = field.parentElement.querySelector(`:scope ${invalidSelector}`) === field; + + errorMessageEl.innerText = isInvalid ? field.validationMessage : ''; + errorMessageEl.classList[isInvalid ? 'remove' : 'add'](`${formName}__error-message--hidden`); } }; @@ -61,13 +64,19 @@ export const postLoad = (form) => { field.addEventListener('input', () => { checkFieldValidity(field); }); + + field.addEventListener('focusout', () => { + checkFieldValidity(field); + }); + + checkFieldValidity(field); }); }; export const onSubmit = async (form, handleSubmit) => { const fields = [...form.querySelectorAll('input')]; - fields.forEach(checkFieldValidity); + fields.forEach((el) => checkFieldValidity(el, false)); if (form.checkValidity()) { await handleSubmit(form); From 305124cf4fbae2b825cce69fd9999f6f804c672e Mon Sep 17 00:00:00 2001 From: manva <116451851+manuel-vara@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:54:31 +0100 Subject: [PATCH 18/41] Add "My Assets" functionality #285 (#286) --- tools/sidekick/config.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/sidekick/config.json b/tools/sidekick/config.json index 8515d657..baf75b9d 100644 --- a/tools/sidekick/config.json +++ b/tools/sidekick/config.json @@ -20,6 +20,19 @@ "title": "Search", "environments": [ "dev", "preview", "live", "prod" ], "url": "/tools/fgrep-attr/fgrep.html" + }, + { + "id": "asset-library", + "title": "My Assets", + "environments": [ + "edit" + ], + "url": "https://experience.adobe.com/solutions/CQ-assets-selectors/static-assets/resources/franklin/asset-selector.html", + "isPalette": true, + "includePaths": [ + "**.docx**" + ], + "paletteRect": "top: 50px; bottom: 10px; right: 10px; left: auto; width:400px; height: calc(100vh - 60px)" } ] } From 0eb9fa224e087585ae709918651f9ff331346cc9 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:35:16 +0100 Subject: [PATCH 19/41] Fix multiple event calendar file downloading #296 (#302) * Fix multiple event calendar file downloading * Fix displaying 'add to event button' button after submitting the form --- blocks/v2-event-notify/v2-event-notify.js | 119 ++++++++++++---------- 1 file changed, 65 insertions(+), 54 deletions(-) diff --git a/blocks/v2-event-notify/v2-event-notify.js b/blocks/v2-event-notify/v2-event-notify.js index 549a91ec..32d6731a 100644 --- a/blocks/v2-event-notify/v2-event-notify.js +++ b/blocks/v2-event-notify/v2-event-notify.js @@ -11,42 +11,6 @@ let successText; let errorText; let socialsLinks; -const onSuccess = async () => { - sampleRUM('form:submit'); - const block = document.querySelector(`.${blockName}__container`); - const addToEventButton = block.querySelector('.event-notify__add-event-button'); - - block.innerHTML = ''; - const buttonWrapper = createElement('div', { classes: `${blockName}__button-wrapper` }); - addToEventButton.classList.remove('secondary'); - addToEventButton.classList.add('primary'); - - const socialsLinksBlock = document.createRange().createContextualFragment(` -
-
-
-
-
`); - - const socialLinkBlockEl = socialsLinksBlock.children[0]; - socialLinkBlockEl.querySelector(':scope > div > div').innerHTML = socialsLinks.innerHTML; - - await loadBlock(socialLinkBlockEl); - - buttonWrapper.append(addToEventButton); - block.append(successText, buttonWrapper, socialLinkBlockEl); -}; - -const onError = (error) => { - // eslint-disable-next-line no-console - console.error(error); - - const block = document.querySelector(`.${blockName}__container`); - - block.innerHTML = ''; - block.append(errorText); -}; - // Convert date to ICS format (e.g., 20231210T120000Z) function formatDateToICS(date) { return date.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, ''); @@ -93,12 +57,44 @@ function downloadICSFile(icsData, filename) { URL.revokeObjectURL(url); } -window.logResult = function logResult(json) { - if (json.result === 'success') { - onSuccess(); - } else if (json.result === 'error') { - onError(json.log); - } +const onSuccess = async (calendarEventData) => { + sampleRUM('form:submit'); + const block = document.querySelector(`.${blockName}__container`); + const addToEventButton = block.querySelector('.event-notify__add-event-button').cloneNode(true); + + block.innerHTML = ''; + const buttonWrapper = createElement('div', { classes: `${blockName}__button-wrapper` }); + addToEventButton.classList.remove('secondary'); + addToEventButton.classList.add('primary'); + addToEventButton.addEventListener('click', () => { + const icsFileContent = generateICS(calendarEventData); + downloadICSFile(icsFileContent, 'event.ics'); + }); + + const socialsLinksBlock = document.createRange().createContextualFragment(` +
+
+
+
+
`); + + const socialLinkBlockEl = socialsLinksBlock.children[0]; + socialLinkBlockEl.querySelector(':scope > div > div').innerHTML = socialsLinks.innerHTML; + + await loadBlock(socialLinkBlockEl); + + buttonWrapper.append(addToEventButton); + block.append(successText, buttonWrapper, socialLinkBlockEl); +}; + +const onError = (error) => { + // eslint-disable-next-line no-console + console.error(error); + + const block = document.querySelector(`.${blockName}__container`); + + block.innerHTML = ''; + block.append(errorText); }; export default async function decorate(block) { @@ -130,6 +126,14 @@ export default async function decorate(block) { description: blockSection?.dataset.eventDescription, }; + window.logResult = function logResult(json) { + if (json.result === 'success') { + onSuccess(calendarEventData); + } else if (json.result === 'error') { + onError(json.log); + } + }; + const container = createElement('div', { classes: `${blockName}__container` }); const formContainer = createElement('div', { classes: `${blockName}__form-container` }); const form = document.createRange().createContextualFragment(` @@ -162,24 +166,31 @@ export default async function decorate(block) { // we can inject the policy content when form content loaded const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { - const formRef = [...mutation.addedNodes].find((el) => el instanceof Element && el.classList.contains('v2-forms__container')); + const formRef = [...mutation.addedNodes]; + const formContainerEl = formRef.find((el) => el instanceof Element && el.classList.contains('v2-forms__container')); - if (!formRef) { + if (!formContainerEl) { return; } - const policyEl = formRef.querySelector('.event-notify__policy'); - const calendarButtonEl = formRef.querySelector('.event-notify__add-event-button'); - - if (formRef) { - policyEl.append(policyText); - calendarButtonEl.addEventListener('click', () => { - const icsFileContent = generateICS(calendarEventData); - downloadICSFile(icsFileContent, 'event.ics'); - }); - + if (formContainerEl.getAttribute('data-initialized') === 'true') { observer.disconnect(); + formContainerEl.querySelector('form')?.reset(); + + return; } + + const policyEl = formContainerEl.querySelector('.event-notify__policy'); + const calendarButtonEl = formContainerEl.querySelector('.event-notify__add-event-button'); + + policyEl.append(policyText); + calendarButtonEl.addEventListener('click', () => { + const icsFileContent = generateICS(calendarEventData); + downloadICSFile(icsFileContent, 'event.ics'); + }); + + observer.disconnect(); + formContainerEl.setAttribute('data-initialized', 'true'); }); }); From a18a5a86a4b98b3dad26c719b28fd11044cad0b3 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:37:48 +0100 Subject: [PATCH 20/41] Modal Visual QA issues #290 (#303) * #296 Fix multiple event calendar file downloading * #290 Fix event notify padding --- blocks/v2-event-notify/v2-event-notify.css | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/blocks/v2-event-notify/v2-event-notify.css b/blocks/v2-event-notify/v2-event-notify.css index 94f13b8c..b5931484 100644 --- a/blocks/v2-event-notify/v2-event-notify.css +++ b/blocks/v2-event-notify/v2-event-notify.css @@ -1,3 +1,11 @@ +.redesign-v2 .section .v2-event-notify-wrapper { + padding-top: 0; +} + +.v2-event-notify__container { + margin-bottom: 32px; +} + .v2-event-notify__container > * { max-width: 694px; margin: auto; @@ -9,7 +17,7 @@ gap: 8px; text-align: center; max-width: var(--text-block-max-width); - margin: 40px auto; + margin: 0 auto 40px; } .v2-event-notify__container .v2-event-notify__title { From 0614158d0099040cf660f3205fd098181b2c2692 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Fri, 15 Dec 2023 13:58:29 +0100 Subject: [PATCH 21/41] Media Gallery missing rounded corners #272 (#284) * #272 Refactor v2-text-with-images -> v2-media-gallery * #272 Update css * #272 Refactor border radius * #272 Udpate inpage nav height var unit --- .../v2-media-gallery.css} | 65 +++++++++---------- .../v2-media-gallery.js} | 2 +- styles/styles.css | 3 +- 3 files changed, 33 insertions(+), 37 deletions(-) rename blocks/{v2-text-with-images/v2-text-with-images.css => v2-media-gallery/v2-media-gallery.css} (69%) rename blocks/{v2-text-with-images/v2-text-with-images.js => v2-media-gallery/v2-media-gallery.js} (97%) diff --git a/blocks/v2-text-with-images/v2-text-with-images.css b/blocks/v2-media-gallery/v2-media-gallery.css similarity index 69% rename from blocks/v2-text-with-images/v2-text-with-images.css rename to blocks/v2-media-gallery/v2-media-gallery.css index fb41de3f..e88f41fd 100644 --- a/blocks/v2-text-with-images/v2-text-with-images.css +++ b/blocks/v2-media-gallery/v2-media-gallery.css @@ -1,25 +1,25 @@ -.v2-text-with-images { +.v2-media-gallery { --text-with-images-image-gap: 12px; --text-with-images-next-slide-preview: 22px; } -.v2-text-with-images__row { +.v2-media-gallery__row { display: flex; flex-direction: column; } -.v2-text-with-images__col { +.v2-media-gallery__col { display: flex; flex-direction: column; align-items: center; text-align: center; color: var(--text-color); padding-bottom: 40px; - max-width: 500px; + max-width: var(--text-block-max-width); align-self: center; } -.v2-text-with-images__heading { +.v2-media-gallery__heading { font-family: var(--ff-volvo-novum-medium); font-size: var(--f-heading-4-font-size); letter-spacing: var(--f-heading-4-letter-spacing); @@ -27,17 +27,17 @@ margin: 16px 0 8px; } -.v2-text-with-images__text { +.v2-media-gallery__text { margin: 0 0 16px; } -.v2-text-with-images__images-list-col { +.v2-media-gallery__images-list-col { max-width: unset; padding: 0; margin: 0 -16px; } -.v2-text-with-images__images-list { +.v2-media-gallery__images-list { display: flex; flex-flow: row nowrap; list-style: none; @@ -45,14 +45,14 @@ padding-left: 0; scroll-snap-type: x mandatory; margin: 0; - gap: 12px; + gap: 16px; } -.v2-text-with-images__images-list::-webkit-scrollbar { +.v2-media-gallery__images-list::-webkit-scrollbar { display: none; } -.v2-text-with-images__images-list-item { +.v2-media-gallery__images-list-item { text-align: left; width: calc( 100% - @@ -69,101 +69,98 @@ max-width: 500px; } -.v2-text-with-images__images-list-item:first-child { +.v2-media-gallery__images-list-item:first-child { margin-left: calc( 100% - var(--text-with-images-next-slide-preview) + var(--text-with-images-image-gap) ); } -.v2-text-with-images__images-list-item:last-child { +.v2-media-gallery__images-list-item:last-child { margin-right: calc( 100% - var(--text-with-images-next-slide-preview) + var(--text-with-images-image-gap) ); } -.v2-text-with-images__figure { +.v2-media-gallery__figure { margin: 0; display: flex; flex-direction: column; gap: 8px; } -.v2-text-with-images__image { +.v2-media-gallery__image { aspect-ratio: 4/3; width: 100%; + border-radius: var(--border-radius); } @media screen and (min-width: 744px) { - .v2-text-with-images { + .v2-media-gallery { --text-with-images-image-gap: 24px; --text-with-images-next-slide-preview: 90px; } - .v2-text-with-images__images-list { - gap: 24px; - } - - .v2-text-with-images__heading { + .v2-media-gallery__heading { margin: 16px 0; } - .v2-text-with-images__text { + .v2-media-gallery__text { margin: 0 0 24px; } } @media screen and (min-width: 1200px) { - .v2-text-with-images__row { + .v2-media-gallery__row { flex-direction: row; justify-content: space-between; } - .v2-text-with-images__col { + .v2-media-gallery__col { align-self: flex-start; position: sticky; top: calc(var(--nav-height) + var(--inpage-navigation-height) + 8px); text-align: left; align-items: flex-start; - width: 360px; + width: 21rem; margin: 0; } - .v2-text-with-images__images-list-col { + .v2-media-gallery__images-list-col { width: auto; } - .v2-text-with-images__images-list { + .v2-media-gallery__images-list { flex-direction: column; gap: 32px; - max-width: 700px; + max-width: var(--text-block-max-width); } - .v2-text-with-images__heading { + .v2-media-gallery__heading { font-size: var(--f-heading-3-font-size); letter-spacing: var(--f-heading-3-letter-spacing); line-height: var(--f-heading-3-line-height); margin: 16px 0 24px; } - p.v2-text-with-images__text { + p.v2-media-gallery__text { margin: 0 0 32px; font-size: var(--f-body-2-font-size); letter-spacing: var(--f-body-2-letter-spacing); line-height: var(--f-body-2-line-height); } - .v2-text-with-images__images-list-item { + .v2-media-gallery__images-list-item { max-width: unset; - width: 700px; + width: var(--text-block-max-width); } - .v2-text-with-images__images-list-item:first-child { + .v2-media-gallery__images-list-item:first-child { margin-left: 0; } - .v2-text-with-images__images-list-item:last-child { + .v2-media-gallery__images-list-item:last-child { margin-right: 0; } } diff --git a/blocks/v2-text-with-images/v2-text-with-images.js b/blocks/v2-media-gallery/v2-media-gallery.js similarity index 97% rename from blocks/v2-text-with-images/v2-text-with-images.js rename to blocks/v2-media-gallery/v2-media-gallery.js index c299d07a..bb38a6a3 100644 --- a/blocks/v2-text-with-images/v2-text-with-images.js +++ b/blocks/v2-media-gallery/v2-media-gallery.js @@ -1,7 +1,7 @@ import { createElement, adjustPretitle } from '../../scripts/common.js'; export default function decorate(block) { - const blockName = 'v2-text-with-images'; + const blockName = 'v2-media-gallery'; const rows = [...block.querySelectorAll(':scope > div')]; rows.forEach((row) => { row.classList.add(`${blockName}__row`); }); diff --git a/styles/styles.css b/styles/styles.css index 6dd38609..03ebf00d 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -291,7 +291,7 @@ --z-index-header: 1050; /* In page navigation */ - --inpage-navigation-height: 0; + --inpage-navigation-height: 0px; /* Zindex inpage navigation */ --z-index-inpage-nav: 1049; @@ -1597,7 +1597,6 @@ main.blue-contract .section.section-with-title p { :root.redesign-v2 { --wrapper-width: 1170px; - --border-radius: 12px; --section-padding: 56px 0 136px; --section-div-padding: 24px 0; --text-block-max-width: 694px; From 7a1001684f89f7918f52c51be754459d62ec2c0e Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:01:33 +0100 Subject: [PATCH 22/41] Volvo Trucks Configurator Embed to website #199 (#283) --- blocks/v2-iframe/v2-iframe.css | 14 ++++++++++++++ blocks/v2-iframe/v2-iframe.js | 16 ++++++++++++++++ scripts/scripts.js | 17 ++++++++++++----- styles/styles.css | 18 ++++++++++++++++++ 4 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 blocks/v2-iframe/v2-iframe.css create mode 100644 blocks/v2-iframe/v2-iframe.js diff --git a/blocks/v2-iframe/v2-iframe.css b/blocks/v2-iframe/v2-iframe.css new file mode 100644 index 00000000..c697211d --- /dev/null +++ b/blocks/v2-iframe/v2-iframe.css @@ -0,0 +1,14 @@ +.v2-iframe iframe { + width: 100%; +} + +.v2-iframe.v2-iframe--full-viewport { + width: 100vw; + height: 100vh; + display: flex; +} + +.v2-iframe--full-viewport iframe { + width: 100%; + height: 100%; +} diff --git a/blocks/v2-iframe/v2-iframe.js b/blocks/v2-iframe/v2-iframe.js new file mode 100644 index 00000000..186a7c10 --- /dev/null +++ b/blocks/v2-iframe/v2-iframe.js @@ -0,0 +1,16 @@ +import { createElement, variantsClassesToBEM } from '../../scripts/common.js'; + +export default async function decorate(block) { + const link = block.querySelector('a')?.getAttribute('href') || block.textContent.trim(); + const title = block.querySelector('h1, h2, h3, h4, h5, h6').textContent; + const iframe = createElement('iframe', { props: { src: link, frameborder: 0, title } }); + const fixedHeightClass = [...block.classList].find((el) => /[0-9]+px/.test(el)); + + variantsClassesToBEM(block.classList, ['full-viewport'], 'v2-iframe'); + + if (fixedHeightClass) { + iframe.height = fixedHeightClass; + } + + block.replaceChildren(iframe); +} diff --git a/scripts/scripts.js b/scripts/scripts.js index 2383881c..4ab9b817 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -665,8 +665,15 @@ export function getImageForBreakpoint(imagesList, onChange = () => { }) { onDesktopChange(desktopMQ); } -/* REDESING CLASS CHECK */ -if (document.querySelector('main').classList.contains('redesign-v2')) { - document.querySelector('html').classList.add('redesign-v2'); - document.querySelector('main').classList.remove('redesign-v2'); -} +const moveClassToHtmlEl = (className, elementSelector = 'main') => { + if (document.querySelector(elementSelector).classList.contains(className)) { + document.querySelector('html').classList.add(className); + document.querySelector(elementSelector).classList.remove(className); + } +}; + +/* REDESIGN CLASS CHECK */ +moveClassToHtmlEl('redesign-v2'); + +/* EXTERNAL APP CLASS CHECK */ +moveClassToHtmlEl('external-app'); diff --git a/styles/styles.css b/styles/styles.css index 03ebf00d..b8633b64 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1729,3 +1729,21 @@ main.blue-contract .section.section-with-title p { } /* generic tooltip styles ends here */ + +/* external app styles start */ + +.external-app header, +.external-app footer { + display: none; +} + +.external-app .v2-iframe-wrapper { + padding: 0 !important; + margin: 0 !important; +} + +.external-app .section.v2-iframe-container { + padding: 0; +} + +/* external app styles end */ From ae82273c3c6ca0be16564a18648722396641c8b2 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:03:30 +0100 Subject: [PATCH 23/41] Social block Visual QA issues #289 (#306) * #289 Update heading and icons styles * #289 Update social text color --- blocks/v2-social-block/v2-social-block.css | 26 +++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/blocks/v2-social-block/v2-social-block.css b/blocks/v2-social-block/v2-social-block.css index 46773046..df27c478 100644 --- a/blocks/v2-social-block/v2-social-block.css +++ b/blocks/v2-social-block/v2-social-block.css @@ -9,9 +9,9 @@ .v2-social-block { --social-block-padding: 24px 16px; --social-block-gap: 16px; - --social-block-list-gap: 12px; + --social-block-list-gap: 24px; --social-link-color: var(--c-main-black); - --social-text-color: var(--c-main-black); + --social-text-color: var(--c-grey-4); padding: var(--social-block-padding); border-radius: 8px; @@ -37,9 +37,9 @@ .v2-social-block__title { font-family: var(--font-family-body); - font-size: var(--f-heading-5-font-size); - letter-spacing: var(--f-heading-5-letter-spacing); - line-height: var(--f-heading-5-line-height); + font-size: var(--f-body-2-font-size); + letter-spacing: var(--f-body-2-letter-spacing); + line-height: var(--f-body-2-line-height); margin-bottom: 0; color: var(--social-text-color); } @@ -47,6 +47,7 @@ .v2-social-block__list { list-style-type: none; display: flex; + gap: var(--social-block-list-gap); } .v2-social-block__button:any-link { @@ -58,8 +59,8 @@ letter-spacing: 0.4px; justify-content: center; max-width: 21.5em; - height: 48px; - padding: 0 var(--social-block-gap); + height: auto; + padding: 0; border-radius: 4px; } @@ -80,16 +81,13 @@ .v2-social-block { --social-block-padding: 32px; + --social-block-list-gap: 40px; } .v2-social-block__list-wrapper { max-width: 680px; margin: auto; } - - .v2-social-block__list { - gap: 16px; - } } @media screen and (min-width: 1200px) { @@ -101,4 +99,10 @@ --social-block-padding: 48px 40px; --social-block-gap: 32px; } + + .v2-social-block__title { + font-size: var(--f-heading-5-font-size); + letter-spacing: var(--f-heading-5-letter-spacing); + line-height: var(--f-heading-5-line-height); + } } From bece20815474b0fc2a1ccbeecacfbb6562439218 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:03:49 +0100 Subject: [PATCH 24/41] Post reveal page quote spacing #301 (#309) --- blocks/v2-quote/v2-quote.js | 2 +- scripts/common.js | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/blocks/v2-quote/v2-quote.js b/blocks/v2-quote/v2-quote.js index 20e1ec7f..f9f24539 100644 --- a/blocks/v2-quote/v2-quote.js +++ b/blocks/v2-quote/v2-quote.js @@ -5,7 +5,7 @@ export default function decorate(block) { const blockquotes = [...block.querySelectorAll('blockquote')]; const pSiblingsOfBlockquote = block.querySelectorAll('blockquote + p'); - unwrapDivs(block); + unwrapDivs(block, { ignoreDataAlign: true }); blockquotes.forEach((bq) => { const em = bq.querySelector('em'); diff --git a/scripts/common.js b/scripts/common.js index 93b0013d..ce69f5e0 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -156,8 +156,9 @@ export const removeEmptyTags = (block) => { }); }; -export const unwrapDivs = (element) => { +export const unwrapDivs = (element, options = {}) => { const stack = [element]; + const { ignoreDataAlign = false } = options; while (stack.length > 0) { const currentElement = stack.pop(); @@ -165,8 +166,15 @@ export const unwrapDivs = (element) => { let i = 0; while (i < currentElement.children.length) { const node = currentElement.children[i]; + const attributesLength = [...node.attributes].filter((el) => { + if (ignoreDataAlign) { + return !(el.name.startsWith('data-align') || el.name.startsWith('data-valign')); + } + + return el; + }).length; - if (node.tagName === 'DIV' && node.attributes.length === 0) { + if (node.tagName === 'DIV' && attributesLength === 0) { while (node.firstChild) { currentElement.insertBefore(node.firstChild, node); } From 029bb7eeac923f74599edae6fee2ebcbd7b6c270 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:04:26 +0100 Subject: [PATCH 25/41] Content Carousel typography adjustments #297 Content Carousel fading effect #305 (#307) --- .../v2-static-content-carousel.css | 66 +++++++++++++------ .../v2-static-content-carousel.js | 6 -- news-and-stories/press-releases/feed.xml | 10 ++- styles/styles.css | 8 ++- 4 files changed, 60 insertions(+), 30 deletions(-) diff --git a/blocks/v2-static-content-carousel/v2-static-content-carousel.css b/blocks/v2-static-content-carousel/v2-static-content-carousel.css index f71795c3..3f2cfa05 100644 --- a/blocks/v2-static-content-carousel/v2-static-content-carousel.css +++ b/blocks/v2-static-content-carousel/v2-static-content-carousel.css @@ -1,9 +1,9 @@ .v2-static-content-carousel { - --static-content-carousel-row-gap: 32px; + --static-content-carousel-row-gap: 24px; --static-content-carousel-list-gap: 8px; --static-content-carousel-col-gap: 16px; + --static-content-carousel-container-margin: 16px; --static-content-card-width: 85%; - --static-content-card-height: 250px; } .v2-static-content-carousel__row { @@ -22,6 +22,11 @@ padding-right: 32px; } +.v2-static-content-carousel__images-list-col { + margin-left: calc(var(--static-content-carousel-container-margin) * -1); + margin-right: calc(var(--static-content-carousel-container-margin) * -1); +} + .v2-static-content-carousel__images-list { list-style-type: none; display: flex; @@ -29,23 +34,35 @@ scroll-behavior: smooth; scroll-snap-align: start; scroll-snap-type: x mandatory; + scrollbar-width: none; gap: var(--static-content-carousel-list-gap); } +.v2-static-content-carousel__images-list::before, +.v2-static-content-carousel__images-list::after { + content: ''; + display: block; + flex: 0 0 var(--static-content-carousel-container-margin); + min-height: 1px; +} + +.v2-static-content-carousel__images-list::after { + flex: 0 0 calc(var(--static-content-carousel-container-margin) / 2); +} + .v2-static-content-carousel__images-list::-webkit-scrollbar { display: none; } .v2-static-content-carousel__images-list-item { + scroll-margin: 0 var(--static-content-carousel-container-margin); scroll-snap-align: start; flex: 0 0 var(--static-content-card-width); } .v2-static-content-carousel__heading { - font-family: var(--ff-volvo-novum-medium); - font-size: var(--f-heading-4-font-size); - letter-spacing: var(--f-heading-4-letter-spacing); - line-height: var(--f-heading-4-line-height); + font: var(--f-heading-5-font-size)/var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: 0.25px; } .v2-static-content-carousel__text { @@ -64,12 +81,12 @@ .v2-static-content-carousel__figure-text { padding-top: 16px; - font-size: var(--f-body-font-size); - line-height: var(--f-body-line-heigh); - letter-spacing: var(--f-body-letter-spacing); + font: var(--f-overline-font-size)/var(--f-overline-line-height) var(--font-family-body); + letter-spacing: var(--f-overline-letter-spacing); } -.v2-static-content-carousel__arrowcontrols { +ul.v2-static-content-carousel__arrowcontrols { + margin-left: var(--static-content-carousel-container-margin); list-style-type: none; display: flex; gap: 24px; @@ -112,11 +129,11 @@ @media screen and (min-width: 744px) { .v2-static-content-carousel { - --static-content-carousel-row-gap: 48px; + --static-content-carousel-row-gap: 32px; --static-content-carousel-list-gap: 20px; --static-content-carousel-col-gap: 24px; + --static-content-carousel-container-margin: 32px; --static-content-card-width: clamp(506px, 68%, calc((100% - var(--static-content-carousel-list-gap))/1.25)); - --static-content-card-height: 432px; } .v2-static-content-carousel__images-list-col { @@ -140,12 +157,13 @@ } .v2-static-content-carousel { - --static-content-carousel-row-gap: 91px; + --static-content-carousel-row-gap: 72px; + --static-content-carousel-container-margin: 20px; --static-content-card-width: calc((100% - var(--static-content-carousel-list-gap))/1.80); } .v2-static-content-carousel__col { - min-width: 337px; + min-width: 21rem; } .v2-static-content-carousel__row { @@ -157,19 +175,27 @@ padding-right: 0; } + .v2-static-content-carousel__heading { + font-size: var(--f-heading-4-font-size); + } + + .v2-static-content-carousel__figure-text { + font: var(--f-body-font-size)/var(--f-body-line-height) var(--font-family-body); + letter-spacing: var(--f-body-letter-spacing); + } + .v2-static-content-carousel__images-list-col { position: relative; } - .v2-static-content-carousel__images-list-col--gradient::after { + .v2-static-content-carousel__images-list-col::before { content: ''; display: block; - height: 100%; - width: 30px; position: absolute; top: 0; - pointer-events: none; - background: linear-gradient(90deg, rgb(255 255 255 / 0%) 0%, var(--background-gradient) 100%); - right: 0; + left: 0; + height: 100%; + width: var(--static-content-carousel-container-margin); + background: var(--background-color); } } diff --git a/blocks/v2-static-content-carousel/v2-static-content-carousel.js b/blocks/v2-static-content-carousel/v2-static-content-carousel.js index 4b1e307d..ed607da1 100644 --- a/blocks/v2-static-content-carousel/v2-static-content-carousel.js +++ b/blocks/v2-static-content-carousel/v2-static-content-carousel.js @@ -23,12 +23,6 @@ const updateActiveClass = (elements, targetElement, carousel) => { if (arrowControl) { arrowControl.disabled = true; } - - if (index !== el.parentNode.children.length - 1) { - carousel.parentElement.classList.add(`${blockName}__images-list-col--gradient`); - } else { - carousel.parentElement.classList.remove(`${blockName}__images-list-col--gradient`); - } } else { el.classList.remove('active'); } diff --git a/news-and-stories/press-releases/feed.xml b/news-and-stories/press-releases/feed.xml index c82540e7..41981f17 100644 --- a/news-and-stories/press-releases/feed.xml +++ b/news-and-stories/press-releases/feed.xml @@ -2,10 +2,18 @@ https://www.volvotrucks.us/news-and-stories/press-releases/ Volvo Trucks USA Press Releases - 2023-11-30T00:00:00.000Z + 2023-12-04T00:00:00.000Z AEM News feed generator (GitHub action) Volvo Trucks USA Press Releases + + <![CDATA[Volvo Trucks Expands Certified EV Dealer Network into Arizona, Georgia and North Carolina as Vanguard Truck Centers Adds Five Locations]]> + https://www.volvotrucks.us/news-and-stories/press-releases/2023/december/volvo-trucks-expands-certified-ev-dealer-network-into-arizona-georgia-and-north-carolina-as-vanguard-truck-centers-adds-five-locations/ + + 2023-12-04T00:00:00.000Z + + 2023-12-04T00:00:00.000Z + <![CDATA[Volvo VNR Electric Trucks Deployed by Tim Hortons as Company Begins First Zero-Tailpipe Emission Deliveries in Ontario and British Columbia, Canada]]> https://www.volvotrucks.us/news-and-stories/press-releases/2023/november/volvo-vnr-electric-trucks-deployed-by-tim-hortons-as-company-begins-first-zero-tailpipe-emission-deliveries-in-ontario-and-british-columbia/ diff --git a/styles/styles.css b/styles/styles.css index b8633b64..d559d229 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1093,7 +1093,10 @@ main.blue-contract .section.section-with-title p { /* stylelint-disable-next-line no-descending-specificity */ .redesign-v2 .section { + --background-color: var(--c-white); + padding: var(--section-padding); + background-color: var(--background-color); scroll-margin-top: calc(var(--nav-height) + var(--inpage-navigation-height)); } @@ -1125,19 +1128,18 @@ main.blue-contract .section.section-with-title p { --card-text: var(--c-white); --c-cta-blue-default: #44A1FF; --text-subtle: var(--c-grey-2); + --background-color: var(--c-main-black); --background-gradient: rgb(20 20 20 / 100%); color: var(--text-color); - background-color: var(--c-main-black); padding-top: 80px; } .section--gray-background { --card-background: var(--c-white); --card-text: var(--c-main-black); + --background-color: var(--c-grey-50); --background-gradient: rgb(247 247 247 / 100%); - - background-color: var(--c-grey-50); } /* End section Background colors */ From 0c64019b0d702606786f7326835a3a0ba4d57218 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:04:43 +0100 Subject: [PATCH 26/41] Embed Youtube video player not loading as expected after accepting cookies #207 (#308) --- scripts/delayed.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/delayed.js b/scripts/delayed.js index 6ab19099..ad4168a3 100644 --- a/scripts/delayed.js +++ b/scripts/delayed.js @@ -51,8 +51,8 @@ if (!window.location.pathname.includes('srcdoc') const currentOnetrustActiveGroups = window.OnetrustActiveGroups; function isSameGroups(groups1, groups2) { - const s1 = JSON.stringify(groups1.split(',').sort()); - const s2 = JSON.stringify(groups2.split(',').sort()); + const s1 = JSON.stringify(groups1.split(',')); + const s2 = JSON.stringify(groups2.split(',')); return s1 === s2; } From 176bbcb797903618b97810603948cdc4134b5e57 Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Mon, 18 Dec 2023 13:06:40 +0100 Subject: [PATCH 27/41] rename block --- .../v2-content-carousel.css | 201 ++++++++++++++++++ .../v2-content-carousel.js} | 2 +- .../v2-static-content-carousel.css | 201 ------------------ 3 files changed, 202 insertions(+), 202 deletions(-) create mode 100644 blocks/v2-content-carousel/v2-content-carousel.css rename blocks/{v2-static-content-carousel/v2-static-content-carousel.js => v2-content-carousel/v2-content-carousel.js} (99%) delete mode 100644 blocks/v2-static-content-carousel/v2-static-content-carousel.css diff --git a/blocks/v2-content-carousel/v2-content-carousel.css b/blocks/v2-content-carousel/v2-content-carousel.css new file mode 100644 index 00000000..cfa98aa3 --- /dev/null +++ b/blocks/v2-content-carousel/v2-content-carousel.css @@ -0,0 +1,201 @@ +.v2-content-carousel { + --content-carousel-row-gap: 24px; + --content-carousel-list-gap: 8px; + --content-carousel-col-gap: 16px; + --content-carousel-container-margin: 16px; + --static-content-card-width: 85%; +} + +.v2-content-carousel__row { + display: flex; + flex-flow: column; + gap: var(--content-carousel-row-gap); +} + +.v2-content-carousel__col { + display: flex; + flex-direction: column; + gap: var(--content-carousel-col-gap); +} + +.v2-content-carousel__text-col { + padding-right: 32px; +} + +.v2-content-carousel__images-list-col { + margin-left: calc(var(--content-carousel-container-margin) * -1); + margin-right: calc(var(--content-carousel-container-margin) * -1); +} + +.v2-content-carousel__images-list { + list-style-type: none; + display: flex; + overflow: scroll; + scroll-behavior: smooth; + scroll-snap-align: start; + scroll-snap-type: x mandatory; + scrollbar-width: none; + gap: var(--content-carousel-list-gap); +} + +.v2-content-carousel__images-list::before, +.v2-content-carousel__images-list::after { + content: ''; + display: block; + flex: 0 0 var(--content-carousel-container-margin); + min-height: 1px; +} + +.v2-content-carousel__images-list::after { + flex: 0 0 calc(var(--content-carousel-container-margin) / 2); +} + +.v2-content-carousel__images-list::-webkit-scrollbar { + display: none; +} + +.v2-content-carousel__images-list-item { + scroll-margin: 0 var(--content-carousel-container-margin); + scroll-snap-align: start; + flex: 0 0 var(--static-content-card-width); +} + +.v2-content-carousel__heading { + font: var(--f-heading-5-font-size)/var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: 0.25px; +} + +.v2-content-carousel__text { + margin-top: 0; + margin-bottom: 0; +} + +.v2-content-carousel__figure { + margin: 0; +} + +.v2-content-carousel__figure img { + border-radius: var(--border-radius); + aspect-ratio: 574/474; +} + +.v2-content-carousel__figure-text { + padding-top: 16px; + font: var(--f-overline-font-size)/var(--f-overline-line-height) var(--font-family-body); + letter-spacing: var(--f-overline-letter-spacing); +} + +ul.v2-content-carousel__arrowcontrols { + margin-left: var(--content-carousel-container-margin); + list-style-type: none; + display: flex; + gap: 24px; + align-items: center; +} + +.v2-content-carousel__button { + padding: 0; +} + +.v2-content-carousel__button:focus { + outline: 0; +} + +/* stylelint-disable-next-line no-descending-specificity */ +.v2-content-carousel__button:focus-visible { + outline: 2px solid var(--border-focus); + outline-offset: 1px; +} + +/* stylelint-disable-next-line no-descending-specificity */ +.v2-content-carousel__button, +.v2-content-carousel__button:disabled, +.v2-content-carousel__button:disabled:hover { + background: transparent; + border: none; + border-radius: 4px; +} + +.v2-content-carousel__button .icon-arrow-left svg, +.v2-content-carousel__button .icon-arrow-right svg { + width: 48px; + height: 48px; +} + +.v2-content-carousel__button:disabled .icon-arrow-right svg path, +.v2-content-carousel__button:disabled .icon-arrow-left svg path { + fill: var(--c-grey-2) +} + +@media screen and (min-width: 744px) { + .v2-content-carousel { + --content-carousel-row-gap: 32px; + --content-carousel-list-gap: 20px; + --content-carousel-col-gap: 24px; + --content-carousel-container-margin: 32px; + --static-content-card-width: clamp(506px, 68%, calc((100% - var(--content-carousel-list-gap))/1.25)); + } + + .v2-content-carousel__images-list-col { + gap: 32px; + } + + p.v2-content-carousel__text { + font-size: var(--f-body-2-font-size); + letter-spacing: var(--f-subtitle-2-letter-spacing); + line-height: var(--f-caption-line-height); + max-width: var(--text-block-max-width); + } +} + +@media screen and (min-width: 1200px) { + .section.v2-content-carousel-container .v2-content-carousel-wrapper { + max-width: 1440px; + padding-left: 64px; + width: 100%; + padding-right: 20px; + } + + .v2-content-carousel { + --content-carousel-row-gap: 72px; + --content-carousel-container-margin: 20px; + --static-content-card-width: calc((100% - var(--content-carousel-list-gap))/1.80); + } + + .v2-content-carousel__col { + min-width: 21rem; + } + + .v2-content-carousel__row { + flex-direction: row; + } + + .v2-content-carousel__text-col { + align-self: center; + padding-right: 0; + } + + .v2-content-carousel__heading { + font-size: var(--f-heading-4-font-size); + } + + .v2-content-carousel__figure-text { + font: var(--f-body-font-size)/var(--f-body-line-height) var(--font-family-body); + letter-spacing: var(--f-body-letter-spacing); + } + + .v2-content-carousel__images-list-col { + position: relative; + } + + .v2-content-carousel__images-list-col::before { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: var(--content-carousel-container-margin); + background: var(--background-color); + } +} diff --git a/blocks/v2-static-content-carousel/v2-static-content-carousel.js b/blocks/v2-content-carousel/v2-content-carousel.js similarity index 99% rename from blocks/v2-static-content-carousel/v2-static-content-carousel.js rename to blocks/v2-content-carousel/v2-content-carousel.js index ed607da1..177b4eef 100644 --- a/blocks/v2-static-content-carousel/v2-static-content-carousel.js +++ b/blocks/v2-content-carousel/v2-content-carousel.js @@ -2,7 +2,7 @@ import { createElement, adjustPretitle } from '../../scripts/common.js'; import { decorateIcons } from '../../scripts/lib-franklin.js'; import { smoothScrollHorizontal } from '../../scripts/motion-helper.js'; -const blockName = 'v2-static-content-carousel'; +const blockName = 'v2-content-carousel'; const updateActiveClass = (elements, targetElement, carousel) => { elements.forEach((el, index) => { diff --git a/blocks/v2-static-content-carousel/v2-static-content-carousel.css b/blocks/v2-static-content-carousel/v2-static-content-carousel.css deleted file mode 100644 index 3f2cfa05..00000000 --- a/blocks/v2-static-content-carousel/v2-static-content-carousel.css +++ /dev/null @@ -1,201 +0,0 @@ -.v2-static-content-carousel { - --static-content-carousel-row-gap: 24px; - --static-content-carousel-list-gap: 8px; - --static-content-carousel-col-gap: 16px; - --static-content-carousel-container-margin: 16px; - --static-content-card-width: 85%; -} - -.v2-static-content-carousel__row { - display: flex; - flex-flow: column; - gap: var(--static-content-carousel-row-gap); -} - -.v2-static-content-carousel__col { - display: flex; - flex-direction: column; - gap: var(--static-content-carousel-col-gap); -} - -.v2-static-content-carousel__text-col { - padding-right: 32px; -} - -.v2-static-content-carousel__images-list-col { - margin-left: calc(var(--static-content-carousel-container-margin) * -1); - margin-right: calc(var(--static-content-carousel-container-margin) * -1); -} - -.v2-static-content-carousel__images-list { - list-style-type: none; - display: flex; - overflow: scroll; - scroll-behavior: smooth; - scroll-snap-align: start; - scroll-snap-type: x mandatory; - scrollbar-width: none; - gap: var(--static-content-carousel-list-gap); -} - -.v2-static-content-carousel__images-list::before, -.v2-static-content-carousel__images-list::after { - content: ''; - display: block; - flex: 0 0 var(--static-content-carousel-container-margin); - min-height: 1px; -} - -.v2-static-content-carousel__images-list::after { - flex: 0 0 calc(var(--static-content-carousel-container-margin) / 2); -} - -.v2-static-content-carousel__images-list::-webkit-scrollbar { - display: none; -} - -.v2-static-content-carousel__images-list-item { - scroll-margin: 0 var(--static-content-carousel-container-margin); - scroll-snap-align: start; - flex: 0 0 var(--static-content-card-width); -} - -.v2-static-content-carousel__heading { - font: var(--f-heading-5-font-size)/var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); - letter-spacing: 0.25px; -} - -.v2-static-content-carousel__text { - margin-top: 0; - margin-bottom: 0; -} - -.v2-static-content-carousel__figure { - margin: 0; -} - -.v2-static-content-carousel__figure img { - border-radius: var(--border-radius); - aspect-ratio: 574/474; -} - -.v2-static-content-carousel__figure-text { - padding-top: 16px; - font: var(--f-overline-font-size)/var(--f-overline-line-height) var(--font-family-body); - letter-spacing: var(--f-overline-letter-spacing); -} - -ul.v2-static-content-carousel__arrowcontrols { - margin-left: var(--static-content-carousel-container-margin); - list-style-type: none; - display: flex; - gap: 24px; - align-items: center; -} - -.v2-static-content-carousel__button { - padding: 0; -} - -.v2-static-content-carousel__button:focus { - outline: 0; -} - -/* stylelint-disable-next-line no-descending-specificity */ -.v2-static-content-carousel__button:focus-visible { - outline: 2px solid var(--border-focus); - outline-offset: 1px; -} - -/* stylelint-disable-next-line no-descending-specificity */ -.v2-static-content-carousel__button, -.v2-static-content-carousel__button:disabled, -.v2-static-content-carousel__button:disabled:hover { - background: transparent; - border: none; - border-radius: 4px; -} - -.v2-static-content-carousel__button .icon-arrow-left svg, -.v2-static-content-carousel__button .icon-arrow-right svg { - width: 48px; - height: 48px; -} - -.v2-static-content-carousel__button:disabled .icon-arrow-right svg path, -.v2-static-content-carousel__button:disabled .icon-arrow-left svg path { - fill: var(--c-grey-2) -} - -@media screen and (min-width: 744px) { - .v2-static-content-carousel { - --static-content-carousel-row-gap: 32px; - --static-content-carousel-list-gap: 20px; - --static-content-carousel-col-gap: 24px; - --static-content-carousel-container-margin: 32px; - --static-content-card-width: clamp(506px, 68%, calc((100% - var(--static-content-carousel-list-gap))/1.25)); - } - - .v2-static-content-carousel__images-list-col { - gap: 32px; - } - - p.v2-static-content-carousel__text { - font-size: var(--f-body-2-font-size); - letter-spacing: var(--f-subtitle-2-letter-spacing); - line-height: var(--f-caption-line-height); - max-width: var(--text-block-max-width); - } -} - -@media screen and (min-width: 1200px) { - .section.v2-static-content-carousel-container .v2-static-content-carousel-wrapper { - max-width: 1440px; - padding-left: 64px; - width: 100%; - padding-right: 20px; - } - - .v2-static-content-carousel { - --static-content-carousel-row-gap: 72px; - --static-content-carousel-container-margin: 20px; - --static-content-card-width: calc((100% - var(--static-content-carousel-list-gap))/1.80); - } - - .v2-static-content-carousel__col { - min-width: 21rem; - } - - .v2-static-content-carousel__row { - flex-direction: row; - } - - .v2-static-content-carousel__text-col { - align-self: center; - padding-right: 0; - } - - .v2-static-content-carousel__heading { - font-size: var(--f-heading-4-font-size); - } - - .v2-static-content-carousel__figure-text { - font: var(--f-body-font-size)/var(--f-body-line-height) var(--font-family-body); - letter-spacing: var(--f-body-letter-spacing); - } - - .v2-static-content-carousel__images-list-col { - position: relative; - } - - .v2-static-content-carousel__images-list-col::before { - content: ''; - display: block; - position: absolute; - top: 0; - left: 0; - height: 100%; - width: var(--static-content-carousel-container-margin); - background: var(--background-color); - } -} From 977f06cba3e43d94dd4e05de16395981af2db252 Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Mon, 18 Dec 2023 13:11:19 +0100 Subject: [PATCH 28/41] fix background color for sections --- styles/styles.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/styles/styles.css b/styles/styles.css index d559d229..4b9f0cb0 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1065,6 +1065,7 @@ main.blue-contract .section.section-with-title p { --card-text: var(--c-main-black); --text-color: var(--c-main-black); --text-subtle: var(--c-grey-4); + --background-color: var(--c-white); --background-gradient: rgb(255 255 255 / 100%); font-size: 16px; @@ -1093,8 +1094,6 @@ main.blue-contract .section.section-with-title p { /* stylelint-disable-next-line no-descending-specificity */ .redesign-v2 .section { - --background-color: var(--c-white); - padding: var(--section-padding); background-color: var(--background-color); scroll-margin-top: calc(var(--nav-height) + var(--inpage-navigation-height)); From 2f0d8df2d9e1a74f7865fcbcba13b5989936e5d2 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Tue, 19 Dec 2023 10:43:00 +0100 Subject: [PATCH 29/41] Add custom viewport configurations for Sidekick Library #314 (#315) --- tools/sidekick/library.html | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tools/sidekick/library.html b/tools/sidekick/library.html index 5ba2b470..5c8bcead 100644 --- a/tools/sidekick/library.html +++ b/tools/sidekick/library.html @@ -34,6 +34,33 @@ const library = document.createElement('sidekick-library') library.config = { base: '/block-library/library.json', + plugins: { + blocks: { + viewPorts: [ + { + width: '375px', + label: 'Phone', + icon: 'device-phone', + }, + { + width: '744px', + label: 'Tablet', + icon: 'device-tablet', + }, + { + width: '1200px', + label: 'Desktop 1200', + icon: 'device-desktop', + }, + { + width: '1440px', + label: 'Desktop 1440', + icon: 'device-desktop', + default: true, + }, + ], + } + } } document.body.prepend(library) From a85b2cf72f839c0a777d89e796384417f9ae1b2c Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:45:27 +0100 Subject: [PATCH 30/41] Add Location field in the ICS file #319 (#320) * make more content configurable * remove social block from form * fix custom property usage * update content locally --- blocks/v2-event-notify/v2-event-notify.css | 6 +---- blocks/v2-event-notify/v2-event-notify.js | 25 +++++-------------- .../v2-inpage-navigation.css | 1 - placeholder.json | 2 +- 4 files changed, 8 insertions(+), 26 deletions(-) diff --git a/blocks/v2-event-notify/v2-event-notify.css b/blocks/v2-event-notify/v2-event-notify.css index b5931484..440df256 100644 --- a/blocks/v2-event-notify/v2-event-notify.css +++ b/blocks/v2-event-notify/v2-event-notify.css @@ -44,7 +44,7 @@ } .v2-event-notify__container .v2-forms__container input { - background: var(--c-primary-white); + background: var(--c-white); font-size: var(--f-body-font-size); letter-spacing: var(--f-body-letter-spacing); line-height: var(--f-body-line-height); @@ -92,10 +92,6 @@ margin-bottom: 40px; } -.v2-event-notify__container .v2-social-block { - max-width: unset; -} - .v2-event-notify__container input:user-invalid, .v2-event-notify__container input:user-invalid:hover, .v2-event-notify__container input:user-invalid:focus { diff --git a/blocks/v2-event-notify/v2-event-notify.js b/blocks/v2-event-notify/v2-event-notify.js index 32d6731a..826ae656 100644 --- a/blocks/v2-event-notify/v2-event-notify.js +++ b/blocks/v2-event-notify/v2-event-notify.js @@ -9,7 +9,6 @@ const blockName = 'v2-event-notify'; let successText; let errorText; -let socialsLinks; // Convert date to ICS format (e.g., 20231210T120000Z) function formatDateToICS(date) { @@ -38,7 +37,7 @@ function generateICS(event) { `DTSTART:${formatDateToICS(event.startDate)}`, `DTEND:${formatDateToICS(event.endDate)}`, `DESCRIPTION:${event.description}`, - 'LOCATION:online', + `LOCATION:${event.location}`, 'END:VEVENT', 'END:VCALENDAR', ].join('\r\n'); @@ -68,23 +67,11 @@ const onSuccess = async (calendarEventData) => { addToEventButton.classList.add('primary'); addToEventButton.addEventListener('click', () => { const icsFileContent = generateICS(calendarEventData); - downloadICSFile(icsFileContent, 'event.ics'); + downloadICSFile(icsFileContent, `${calendarEventData.fileName}.ics`); }); - const socialsLinksBlock = document.createRange().createContextualFragment(` -
-
-
-
-
`); - - const socialLinkBlockEl = socialsLinksBlock.children[0]; - socialLinkBlockEl.querySelector(':scope > div > div').innerHTML = socialsLinks.innerHTML; - - await loadBlock(socialLinkBlockEl); - buttonWrapper.append(addToEventButton); - block.append(successText, buttonWrapper, socialLinkBlockEl); + block.append(successText, buttonWrapper); }; const onError = (error) => { @@ -110,8 +97,6 @@ export default async function decorate(block) { const formLink = contentData.link.innerText.trim(); const beforeFormText = contentData['before form']; const policyText = contentData.policy; - socialsLinks = contentData.socials; - socialsLinks.classList.add(`${blockName}__socials`); errorText = contentData['error message']; errorText.classList.add(`${blockName}__message-text`); successText = contentData['success message']; @@ -120,10 +105,12 @@ export default async function decorate(block) { // Calendar event meta data const blockSection = block.parentElement?.parentElement; const calendarEventData = { + fileName: blockSection?.dataset.eventFileName, summary: blockSection?.dataset.eventSummary, startDate: new Date(blockSection?.dataset.eventStartDate), endDate: new Date(blockSection?.dataset.eventEndDate), description: blockSection?.dataset.eventDescription, + location: blockSection?.dataset.eventLocation, }; window.logResult = function logResult(json) { @@ -186,7 +173,7 @@ export default async function decorate(block) { policyEl.append(policyText); calendarButtonEl.addEventListener('click', () => { const icsFileContent = generateICS(calendarEventData); - downloadICSFile(icsFileContent, 'event.ics'); + downloadICSFile(icsFileContent, `${calendarEventData.fileName}.ics`); }); observer.disconnect(); diff --git a/blocks/v2-inpage-navigation/v2-inpage-navigation.css b/blocks/v2-inpage-navigation/v2-inpage-navigation.css index f0677923..376efb56 100644 --- a/blocks/v2-inpage-navigation/v2-inpage-navigation.css +++ b/blocks/v2-inpage-navigation/v2-inpage-navigation.css @@ -125,7 +125,6 @@ then we change it for the next section item in main */ } .v2-inpage-navigation__items { - background-color: var(--c-primary-white); list-style: none; display: flex; gap: 24px; diff --git a/placeholder.json b/placeholder.json index 8ea82b1d..9c71ee1a 100644 --- a/placeholder.json +++ b/placeholder.json @@ -177,7 +177,7 @@ }, { "Key": "event-notify:zip", - "Text": "ZIP" + "Text": "Zip or Postal Code" }, { "Key": "event-notify:email", From afe278bfb8ed2709ca3137f400a0139a047b4e84 Mon Sep 17 00:00:00 2001 From: TomaszDziezykNetcentric <125962117+TomaszDziezykNetcentric@users.noreply.github.com> Date: Tue, 2 Jan 2024 14:36:11 +0100 Subject: [PATCH 31/41] Volvo Truck Configurator - X close window button is out of view in the Compare models pop-up #311 (#316) --- blocks/v2-iframe/v2-iframe.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blocks/v2-iframe/v2-iframe.css b/blocks/v2-iframe/v2-iframe.css index c697211d..255a865c 100644 --- a/blocks/v2-iframe/v2-iframe.css +++ b/blocks/v2-iframe/v2-iframe.css @@ -8,6 +8,12 @@ display: flex; } +@supports (height: 1svh) { + .v2-iframe.v2-iframe--full-viewport { + height: 100svh; + } +} + .v2-iframe--full-viewport iframe { width: 100%; height: 100%; From 1d0a59612242ab8c26b53ea6f833611b5ebdec43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Vuki=C4=87evi=C4=87?= <139957716+markovukiceviccn@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:32:34 +0100 Subject: [PATCH 32/41] Media with Text Block #180 (#282) * #180 - Add Media with text styles and bahaviour, fix video playback icon in resources gallery * #180 - Add block selectors, unwrap specific divs * #180 - Add Media with text block styles * Refactor: new classes names for consistent content and developer experience, new reusable JS functions and code cleanup * fix variant gallery gap * fix aspect ratio for video * fix subtext color with black bg * refactor subtext color * fix replace gap property * refactor scroll behavior * remove redundant code and update styles for better readibility * refactor use variable for width * fix center content for media-gallery variant * revert icon css * feat expanded wrapper for media-left and media-right variants * fix content vertical position * feat function to add class to child parent * fix the container width of media-tabs to default width * feat expanded container for media-tabs * remove redundant icon css * update media section with autoplay and video poster option * fix exclude IFRAME from empty tags list * update add iframe for youtube video * fix play icon size * fix pixel issue * fix media-gallery cut-off * fix global play icon jump issue * Revert "fix global play icon jump issue" This reverts commit b60fba0ee8569ba2cdf1c1678eccad1ab8497373. * fix video play icon jump issue * fix heading spacing * fix center the content for full-width variant * refactor css cleanup * refactor css * refactor remove unnecessary css * fix media-with-tabs spacing and width * fix media-with-tabs sub-title font * fix next image peek for media-gallery variant * feature mute/unmute controls for video * fix position of play and mute button for media-with-text * refactor media-gallery variant * fix video size and button alignment * fix image aspect ratio * refactor alignment * fix gallery image size on mobile * Mute and pause btn moved to top on tablet * fix alignment of text * make changes only on media with text component --------- Co-authored-by: Taimur Co-authored-by: Syb Wartna Co-authored-by: SantiagoHomps-NC --- blocks/v2-content-card/v2-content-card.js | 20 +- .../v2-media-with-tabs/v2-media-with-tabs.css | 21 +- .../v2-media-with-tabs/v2-media-with-tabs.js | 12 +- .../v2-media-with-text/v2-media-with-text.css | 330 ++++++++++++++++++ .../v2-media-with-text/v2-media-with-text.js | 83 +++++ .../v2-resource-gallery.css | 1 + common/modal/modal.js | 2 +- scripts/common.js | 56 +++ scripts/video-helper.js | 149 +++++++- styles/styles.css | 47 ++- 10 files changed, 668 insertions(+), 53 deletions(-) create mode 100644 blocks/v2-media-with-text/v2-media-with-text.css create mode 100644 blocks/v2-media-with-text/v2-media-with-text.js diff --git a/blocks/v2-content-card/v2-content-card.js b/blocks/v2-content-card/v2-content-card.js index 6c95badf..d4e9d9b0 100644 --- a/blocks/v2-content-card/v2-content-card.js +++ b/blocks/v2-content-card/v2-content-card.js @@ -2,7 +2,7 @@ import { createOptimizedPicture } from '../../scripts/lib-franklin.js'; import { adjustPretitle, createElement, removeEmptyTags, variantsClassesToBEM, } from '../../scripts/common.js'; -import { createVideo, isVideoLink } from '../../scripts/video-helper.js'; +import { createVideo, getDynamicVideoHeight, isVideoLink } from '../../scripts/video-helper.js'; const variantClasses = ['images-grid', 'images-grid-masonry', 'editorial']; @@ -63,23 +63,7 @@ export default async function decorate(block) { parentElement.style.position = 'relative'; parentElement.append(playbackControls); - // Get the element's height(use requestAnimationFrame to get actual height instead of 0) - requestAnimationFrame(() => { - const height = newVideo.offsetHeight - 60; - playbackControls.style.top = `${height.toString()}px`; - }); - - // Get the element's height on resize - const getVideoHeight = (entries) => { - // eslint-disable-next-line no-restricted-syntax - for (const entry of entries) { - const height = entry.target.offsetHeight - 60; - playbackControls.style.top = `${height.toString()}px`; - } - }; - - const resizeObserver = new ResizeObserver(getVideoHeight); - resizeObserver.observe(newVideo); + getDynamicVideoHeight(newVideo, playbackControls); } // Add wrapper around the text content diff --git a/blocks/v2-media-with-tabs/v2-media-with-tabs.css b/blocks/v2-media-with-tabs/v2-media-with-tabs.css index 6b37f917..410c677f 100644 --- a/blocks/v2-media-with-tabs/v2-media-with-tabs.css +++ b/blocks/v2-media-with-tabs/v2-media-with-tabs.css @@ -112,38 +112,28 @@ button.v2-media-with-tabs__tab:focus-visible { } .v2-media-with-tabs__header-section { - width: 50%; + max-width: 332px; } } @media screen and (min-width: 1200px) { - .section.v2-media-with-tabs-container .v2-media-with-tabs-wrapper { - width: 100%; - margin: 0; - } - .v2-media-with-tabs { - padding: 0 64px 0 32px; flex-direction: row; gap: 48px; } .v2-media-with-tabs__images-section { order: 1; - width: 70%; + width: 100%; } .v2-media-with-tabs__texts-section { order: 2; - width: 30%; + max-width: 309px; justify-content: center; } /* media-right variant: images displayed on the right side */ - .v2-media-with-tabs.v2-media-with-tabs--media-right { - padding: 0 32px 0 64px; - } - .v2-media-with-tabs--media-right .v2-media-with-tabs__images-section { order: 2; } @@ -158,9 +148,11 @@ button.v2-media-with-tabs__tab:focus-visible { text-wrap: balance; } - .v2-media-with-tabs__subtitle { + .v2-media-with-tabs__texts-section .v2-media-with-tabs__subtitle { margin-bottom: 48px; font-size: var(--f-body-2-font-size); + letter-spacing: var(--f-body-2-letter-spacing); + line-height: var(--f-body-2-line-height); } .v2-media-with-tabs__image img { @@ -181,6 +173,7 @@ button.v2-media-with-tabs__tab:focus-visible { .v2-media-with-tabs__header-section { order: 1; width: 100%; + max-width: unset; } } diff --git a/blocks/v2-media-with-tabs/v2-media-with-tabs.js b/blocks/v2-media-with-tabs/v2-media-with-tabs.js index ccc80d43..ffe573c3 100644 --- a/blocks/v2-media-with-tabs/v2-media-with-tabs.js +++ b/blocks/v2-media-with-tabs/v2-media-with-tabs.js @@ -1,6 +1,11 @@ -import { createElement, unwrapDivs, variantsClassesToBEM } from '../../scripts/common.js'; - -const variantClasses = ['media-right']; +import { + addClassIfChildHasClass, + createElement, + unwrapDivs, + variantsClassesToBEM, +} from '../../scripts/common.js'; + +const variantClasses = ['media-right', 'expanded-width']; const blockName = 'v2-media-with-tabs'; const handleChangeTab = (e) => { @@ -25,6 +30,7 @@ const handleChangeTab = (e) => { export default function decorate(block) { variantsClassesToBEM(block.classList, variantClasses, blockName); + addClassIfChildHasClass(block, 'expanded-width'); const content = block.querySelectorAll(':scope > div > div'); const [header, list] = content; diff --git a/blocks/v2-media-with-text/v2-media-with-text.css b/blocks/v2-media-with-text/v2-media-with-text.css new file mode 100644 index 00000000..1a146703 --- /dev/null +++ b/blocks/v2-media-with-text/v2-media-with-text.css @@ -0,0 +1,330 @@ +/* Variant: default */ + +.v2-media-with-text { + display: flex; + flex-direction: column; + gap: 40px; +} + +.v2-media-with-text__container { + display: flex; + flex-direction: column; + gap: 32px; +} + +.v2-media-with-text__media-section { + position: relative; +} + +.v2-media-with-text__media-section .v2-media-with-text__media { + border-radius: var(--border-radius); + object-fit: cover; + aspect-ratio: 7/5; + display: block; +} + + +/* Video */ +.v2-media-with-text__media-section video.v2-media-with-text__media { + width: 100%; +} + + +/* Video with poster */ +.v2-media-with-text--video-with-poster .v2-media-with-text__media { + aspect-ratio: 1; +} + +.v2-media-with-text--video-with-poster video.v2-media-with-text__media, +.v2-media-with-text--video-with-poster iframe.v2-media-with-text__media { + display: none; +} + +.v2-media-with-text--video-with-poster .icon-play-video { + display: flex; +} + +.v2-media-with-text--video-with-poster .v2-video__playback-button { + width: 100%; + height: 100%; + inset: 0; + transform: none; +} + +.v2-media-with-text--video-with-poster .v2-video__playback-button .icon, +.v2-media-with-text--video-with-poster .v2-video__playback-button .icon svg { + width: 72px; + height: 72px; +} + +.v2-media-with-text__content-section, +.v2-media-with-text__sub-text-section { + width: 100%; + max-width: var(--text-block-max-width); +} + +.v2-media-with-text__content-section p { + margin-bottom: 0; +} + +.v2-media-with-text__content-section p:first-of-type { + margin-top: 0; +} + +.v2-media-with-text__heading { + font: 400 var(--f-heading-5-font-size) / var(--f-heading-5-line-height) var(--font-family-heading); + letter-spacing: var(--f-heading-5-letter-spacing); + margin-bottom: 16px; +} + +.v2-media-with-text__sub-text-section { + color: var(--text-subtle); +} + +/* Variant: text-centered */ + +.v2-media-with-text--text-centered .v2-media-with-text__content-section { + text-align: center; + margin: auto; +} + +/* Variant: full-width */ + +.v2-media-with-text--full-width .v2-media-with-text__media-section .v2-media-with-text__media { + width: 100%; + aspect-ratio: 16/9; + border-radius: 0; +} + +.v2-media-with-text--full-width .v2-media-with-text__content-section { + padding: 0 16px; + align-self: center; + text-align: center; +} + +/* Variant: media-right, media-left */ +.v2-media-with-text--media-left, +.v2-media-with-text--media-right { + gap: 24px; +} + +.v2-media-with-text--media-left .v2-media-with-text__media-section .v2-media-with-text__media, +.v2-media-with-text--media-right .v2-media-with-text__media-section .v2-media-with-text__media { + aspect-ratio: 4/3; + height: 100%; +} + +/* Variant: media-vertical */ + +.v2-media-with-text--media-vertical { + gap: 48px; +} + +.v2-media-with-text--media-vertical .v2-media-with-text__content-section { + text-align: left; + max-width: 100%; +} + + +/* Variant: media-gallery */ + +.v2-media-with-text--media-gallery { + --media-gallery-gap: 8px; + --media-gallery-container-margin: 16px; + --media-gallery-max-image-width: 506px; + + /* margin-left: calc(var(--media-gallery-container-margin) * -1); + margin-right: calc(var(--media-gallery-container-margin) * -1); */ +} + +.v2-media-with-text--media-gallery .v2-media-with-text__media-section { + margin-left: calc(var(--media-gallery-container-margin) * -1); + margin-right: calc(var(--media-gallery-container-margin) * -1); + padding-left: 0; + display: flex; + gap: var(--media-gallery-gap); + scroll-behavior: smooth; + scroll-snap-align: start; + scroll-snap-type: x mandatory; + scrollbar-width: none; + overflow: scroll; + flex-flow: row nowrap; +} + +.v2-media-with-text--media-gallery .v2-media-with-text__media-section::before, +.v2-media-with-text--media-gallery .v2-media-with-text__media-section::after { + content: ''; + display: block; + flex: 0 0 calc(var(--media-gallery-container-margin) - var(--media-gallery-gap)); + min-height: 1px; +} + +.v2-media-with-text--media-gallery .v2-media-with-text__media-section::-webkit-scrollbar { + display: none; +} + +.v2-media-with-text--media-gallery .v2-media-with-text__media-section picture { + scroll-snap-align: center; + width: calc(100% - (var(--media-gallery-container-margin) * 2) - var(--media-gallery-gap)); + max-width: var(--media-gallery-max-image-width); + aspect-ratio: 1; + flex-shrink: 0; +} + +.v2-media-with-text--media-gallery .v2-media-with-text__media-section .v2-media-with-text__media { + width: 100%; + height: 100%; +} + +.v2-media-with-text--media-gallery .v2-media-with-text__content-section { + margin: auto; + text-align: center +} + +@media (min-width: 744px) { + /* Variant: default */ + .v2-media-with-text__media-section .v2-media-with-text__media { + aspect-ratio: 4/3; + } + + + /* Variant: video with poster */ + .v2-media-with-text--video-with-poster .v2-media-with-text__media { + aspect-ratio: 1; + } + + + /* Variant: media-left, media-right */ + .v2-media-with-text--media-left, + .v2-media-with-text--media-right { + gap: 32px; + } + + .v2-media-with-text--media-left .v2-media-with-text__content-section, + .v2-media-with-text--media-right .v2-media-with-text__content-section { + max-width: 332px; + } + + .v2-media-with-text--media-left .v2-media-with-text__media-section .v2-media-with-text__media { + aspect-ratio: 4/3; + } + + + /* Variant: media-vertical */ + .v2-media-with-text--media-vertical .v2-media-with-text__content-section { + max-width: 332px; + } + + + /* Variant: media-gallery */ + .v2-media-with-text--media-gallery { + --media-gallery-gap: 20px; + --media-gallery-container-margin: 32px; + } +} + +@media (min-width: 1024px) { + .v2-media-with-text--media-gallery { + --media-gallery-max-image-width: 575px; + } +} + +@media (min-width: 1200px) { + /* Variant: default */ + .v2-media-with-text { + gap: 48px; + } + + + .v2-media-with-text__media-section video.v2-media-with-text__media { + aspect-ratio: 16/9; + } + + .v2-media-with-text__content-section { + text-align: center; + margin-left: auto; + margin-right: auto; + } + + .v2-media-with-text__sub-text-section { + text-align: center; + } + + .v2-media-with-text__content-section p { + font-size: var(--f-body-2-font-size); + letter-spacing: var(--f-body-2-letter-spacing); + line-height: var(--f-body-2-line-height); + margin-bottom: 0; + } + + .v2-media-with-text__heading { + font: 400 var(--f-heading-4-font-size) / var(--f-heading-4-line-height) var(--font-family-heading); + letter-spacing: var(--f-heading-4-letter-spacing); + margin-bottom: 24px; + } + + /* Variant: full-width */ + .v2-media-with-text--full-width .v2-media-with-text__content-section { + padding: 0; + } + + + /* Variant: media-left, media-right */ + .v2-media-with-text--media-left, + .v2-media-with-text--media-right { + align-items: center; + } + + .v2-media-with-text--media-left { + flex-direction: row; + } + + .v2-media-with-text--media-right { + flex-direction: row-reverse + } + + .v2-media-with-text--media-left .v2-media-with-text__content-section, + .v2-media-with-text--media-right .v2-media-with-text__content-section { + text-align: left; + max-width: 309px; + } + + /* Variant: media-vertical */ + .v2-media-with-text--media-vertical { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: auto; + gap: 64px 32px; + } + + + .v2-media-with-text--media-vertical .v2-media-with-text__content-section { + max-width: 100%; + } + + .v2-media-with-text--media-vertical .v2-media-with-text__sub-text-section { + grid-column: 1/-1; + margin: auto; + } + + /* Variant: media-gallery */ + .v2-media-with-text--media-gallery { + --media-gallery-container-margin: 0px; + } + + .v2-media-with-text--media-gallery .v2-media-with-text__media-section::before, + .v2-media-with-text--media-gallery .v2-media-with-text__media-section::after { + display: none; + } +} + +@media (max-width: 1200px) { + .v2-media-with-text--mute-controls .v2-video__playback-button, + .v2-media-with-text--mute-controls .v2-video__mute-controls { + top: 18px; + } + + .v2-media-with-text--mute-controls .v2-video__mute-controls { + top: 70px ; + } +} diff --git a/blocks/v2-media-with-text/v2-media-with-text.js b/blocks/v2-media-with-text/v2-media-with-text.js new file mode 100644 index 00000000..c0f2dec7 --- /dev/null +++ b/blocks/v2-media-with-text/v2-media-with-text.js @@ -0,0 +1,83 @@ +import { + addClassIfChildHasClass, + addVideoToSection, + createElement, + createNewSection, + removeEmptyTags, + unwrapDivs, + variantsClassesToBEM, +} from '../../scripts/common.js'; +import { + addMuteControls, createVideoWithPoster, isVideoLink, selectVideoLink, +} from '../../scripts/video-helper.js'; + +const blockName = 'v2-media-with-text'; +const variantClasses = ['text-centered', 'expanded-width', 'full-width', 'media-left', 'media-left', 'media-right', 'media-vertical', 'media-gallery', 'media-autoplay', 'mute-controls']; +export default async function decorate(block) { + variantsClassesToBEM(block.classList, variantClasses, blockName); + addClassIfChildHasClass(block, 'full-width'); + addClassIfChildHasClass(block, 'expanded-width'); + + const cells = block.querySelectorAll(':scope > div > div'); + let contentSection; + let mediaSection; + let subTextSection; + let containerSection; + + cells.forEach((cell, index) => { + // First cell for content, second for media and last for the subtext + const isLastCell = index % 2 === 0 && index === cells.length - 1; + const isCellNumberEven = index % 2 === 0; + const isTotalCellsEven = cells.length % 2 === 0; + + if (isLastCell) subTextSection = createNewSection(blockName, 'sub-text', cell); else if (isCellNumberEven) { + contentSection = createNewSection(blockName, 'content', cell); + const headings = [...cell.querySelectorAll(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])]; + headings.forEach((heading) => heading.classList.add(`${blockName}__heading`)); + } else { + mediaSection = createNewSection(blockName, 'media', cell); + + const videos = [...mediaSection.querySelectorAll('a')].filter((link) => isVideoLink(link)); + const picture = mediaSection.querySelector('picture'); + const videoButtons = mediaSection.querySelectorAll('.button-container'); // need to remove it later from DOM + + if (videos.length) { + const linkEl = selectVideoLink(videos); + + if (linkEl) { + if (picture) { + const videoWithPoster = createVideoWithPoster(linkEl, picture, blockName); + videoButtons.forEach((button) => button.remove()); + mediaSection.append(videoWithPoster); + } else { + mediaSection = addVideoToSection(blockName, mediaSection, linkEl); + if (block.classList.contains(`${blockName}--mute-controls`)) addMuteControls(mediaSection); + } + } + } + } + + // If cell number is odd(i.e. a 'media' cell) and not the last cell + if (!isLastCell && !isCellNumberEven) { + // Wrap with a row if the number of cells is more than 2. + if (cells.length > 2) { + containerSection = createElement('div', { classes: `${blockName}__container` }); + containerSection.append(mediaSection, contentSection); + block.append(containerSection); + } else { + // Append the media and content sections directly to the block. + block.append(mediaSection, contentSection); + } + } + // For an odd number of cells, append the remaining subTextSection. + if (!isTotalCellsEven) { + if (subTextSection) block.append(subTextSection); + } + }); + + const medias = block.querySelectorAll(['img', 'video', 'iframe']); + medias.forEach((media) => media.classList.add(`${blockName}__media`)); + + unwrapDivs(block); + removeEmptyTags(block); +} diff --git a/blocks/v2-resource-gallery/v2-resource-gallery.css b/blocks/v2-resource-gallery/v2-resource-gallery.css index 23ef3bd3..ad301e3e 100644 --- a/blocks/v2-resource-gallery/v2-resource-gallery.css +++ b/blocks/v2-resource-gallery/v2-resource-gallery.css @@ -94,6 +94,7 @@ height: 100%; } + .v2-resource-gallery__document-list { list-style-type: none; display: grid; diff --git a/common/modal/modal.js b/common/modal/modal.js index cb4e8efc..a4d0a4cb 100644 --- a/common/modal/modal.js +++ b/common/modal/modal.js @@ -1,6 +1,6 @@ +// eslint-disable-next-line import/no-cycle import { createElement, getTextLabel } from '../../scripts/common.js'; import { decorateIcons, loadCSS } from '../../scripts/lib-franklin.js'; -// eslint-disable-next-line import/no-cycle import { createIframe, isLowResolutionVideoUrl } from '../../scripts/video-helper.js'; const styles$ = new Promise((r) => { diff --git a/scripts/common.js b/scripts/common.js index ce69f5e0..77e30b9a 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -7,6 +7,8 @@ import { buildBlock, decorateBlock, } from './lib-franklin.js'; +// eslint-disable-next-line import/no-cycle +import { createVideo, isVideoLink } from './video-helper.js'; let placeholders = null; @@ -62,6 +64,44 @@ export function createElement(tagName, options = {}) { return elem; } + +/** + * Create a new section with the specific name. + * Append the given node to this section. + * @param {string} blockName - Block name. + * @param {string} sectionName - Name of the section + * (content-section, media-section etc...). + * @param {HTMLElement} node - HTML element that represents + * the node to append to the section. + * @returns {HTMLElement} - Returns an HTML element representing + * the new section with appended cell. + */ +export function createNewSection(blockName, sectionName, node) { + const section = createElement('div', { classes: `${blockName}__${sectionName}-section` }); + section.append(node); + return section; +} + +/** + * Provides the functionality to add a video + * to the provided section if the link corresponds to a video. + * @param {string} blockName - Name of the block. + * @param {HTMLElement} section - Represents the section to which the video should be added. + * @param {HTMLAnchorElement} link - Anchor link + * @returns {HTMLElement} - Section with added video + */ +export function addVideoToSection(blockName, section, link) { + const isVideo = link ? isVideoLink(link) : false; + if (isVideo) { + const video = createVideo(link.getAttribute('href'), `${blockName}__video`, { + muted: true, autoplay: true, loop: true, playsinline: true, + }); + const playbackControls = video.querySelector('button'); + link.remove(); + section.append(video, playbackControls); + } + return section; +} /** * Adds the favicon. * @param {string} href The favicon URL @@ -145,6 +185,10 @@ export const removeEmptyTags = (block) => { block.querySelectorAll('*').forEach((x) => { const tagName = ``; + // exclude iframes + if (x.tagName.toUpperCase() === 'IFRAME') { + return; + } // checking that the tag is not autoclosed to make sure we don't remove // checking the innerHTML and trim it to make sure the content inside the tag is 0 if ( @@ -196,6 +240,18 @@ export const variantsClassesToBEM = (blockClasses, expectedVariantsNames, blockN }); }; +/** + * Adds a CSS class to the parent element of a child element + * if the child element's class list includes the specified class. + * + * @param {HTMLElement} child - The child HTML element. + * @param {string} className - The name of the class to check and add. + */ +export function addClassIfChildHasClass(child, className) { + if (child.className.includes(className)) { + child.parentElement.classList.add(className); + } +} /** * * @param {string} blockName - block name with '-' instead of spaces diff --git a/scripts/video-helper.js b/scripts/video-helper.js index 425e74aa..ec4cb763 100644 --- a/scripts/video-helper.js +++ b/scripts/video-helper.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line import/no-cycle import { createElement, getTextLabel } from './common.js'; /* video helpers */ @@ -7,9 +8,7 @@ export function isLowResolutionVideoUrl(url) { export function isVideoLink(link) { const linkString = link.getAttribute('href'); - return (linkString.includes('youtube.com/embed/') - || isLowResolutionVideoUrl(linkString)) - && link.closest('.block.embed') === null; + return (linkString.includes('youtube.com/embed/') || isLowResolutionVideoUrl(linkString)) && link.closest('.block.embed') === null; } export function selectVideoLink(links, preferredType) { @@ -64,8 +63,7 @@ export function addVideoShowHandler(link) { } export function isSoundcloudLink(link) { - return link.getAttribute('href').includes('soundcloud.com/player') - && link.closest('.block.embed') === null; + return link.getAttribute('href').includes('soundcloud.com/player') && link.closest('.block.embed') === null; } export function addSoundcloudShowHandler(link) { @@ -121,9 +119,7 @@ export function createIframe(url, { parentEl, classes = [] }) { const iframe = createElement('iframe', { classes: Array.isArray(classes) ? classes : [classes], props: { - frameborder: '0', - allowfullscreen: 'allowfullscreen', - src: url, + frameborder: '0', allowfullscreen: 'allowfullscreen', src: url, }, }); @@ -162,8 +158,7 @@ export const createVideo = (src, className = '', props = {}) => { const source = createElement('source', { props: { - src, - type: 'video/mp4', + src, type: 'video/mp4', }, }); @@ -218,3 +213,137 @@ export const createVideo = (src, className = '', props = {}) => { return video; }; + +export function getDynamicVideoHeight(video, playbackControls) { + // Get the element's height(use requestAnimationFrame to get actual height instead of 0) + requestAnimationFrame(() => { + const height = video.offsetHeight - 60; + playbackControls.style.top = `${height.toString()}px`; + }); + + // Get the element's height on resize + const getVideoHeight = (entries) => { + // eslint-disable-next-line no-restricted-syntax + for (const entry of entries) { + const height = entry.target.offsetHeight - 60; + playbackControls.style.top = `${height.toString()}px`; + } + }; + + const resizeObserver = new ResizeObserver(getVideoHeight); + resizeObserver.observe(video); +} + +/** + * Creates a video element with a poster image. + * @param {HTMLElement} linkEl - The link element that contains the video URL. + * @param {HTMLPictureElement} poster - The URL of the poster image. + * @param {string} blockName - The name of the CSS block for styling. + * @return {HTMLElement} - The container element that holds the video and poster. + */ +export function createVideoWithPoster(linkEl, poster, blockName) { + const linkUrl = linkEl.getAttribute('href'); + const videoContainer = document.createElement('div'); + videoContainer.classList.add(`${blockName}__video-container`, `${blockName}--video-with-poster`); + + let videoOrIframe; + let playButton; + + const showVideo = (e) => { + const ele = e.currentTarget; + const eleParent = ele.parentElement; + const picture = eleParent?.querySelector('picture'); + const video = eleParent?.querySelector('video'); + const iframe = eleParent?.querySelector('iframe'); + if (eleParent && picture) { + ele.remove(); + picture.remove(); + if (video) video.style.display = 'block'; + if (iframe) iframe.style.display = 'block'; + } + }; + + if (isLowResolutionVideoUrl(linkUrl)) { + videoOrIframe = createVideo(linkUrl, `${blockName}__video`, { + muted: false, autoplay: false, loop: true, playsinline: true, controls: true, + }); + } else { + videoOrIframe = createIframe(linkUrl, { classes: `${blockName}__iframe` }); + playButton = createElement('button', { + props: { type: 'button', class: 'v2-video__playback-button' }, + }); + addPlayIcon(playButton); + videoContainer.append(playButton); + } + videoContainer.append(poster, videoOrIframe); + videoContainer.querySelector('.v2-video__playback-button').addEventListener('click', showVideo); + videoContainer.querySelector('.icon-pause-video')?.remove(); + return videoContainer; +} + +const getMuteSvg = () => ` + + +`; + +const getUnmuteSvg = () => ` + + + `; + +/** + * Adds mute controls to a given section. + * @param {HTMLElement} section - The section element to add the controls to. + * @returns {void} + */ +export const addMuteControls = (section) => { + const muteSvg = getMuteSvg(); + const unmuteSvg = getUnmuteSvg(); + + const controls = createElement('button', { + props: { type: 'button', class: 'v2-video__mute-controls' }, + }); + + const iconsHTML = document.createRange().createContextualFragment(` + ${muteSvg} + ${unmuteSvg} + `); + + controls.append(...iconsHTML.children); + section.appendChild(controls); + + const video = section.querySelector('video'); + const muteIcon = section.querySelector('.icon-mute'); + const unmuteIcon = section.querySelector('.icon-unmute'); + const muteIconLabel = getTextLabel('Mute video'); + const unmuteIconLabel = getTextLabel('Unmute video'); + + controls.setAttribute('aria-label', muteIconLabel); + + if (!video) return; + + const showHideMuteIcon = (isMuted) => { + if (!isMuted) { + muteIcon.style.display = 'none'; + unmuteIcon.style.display = 'flex'; + controls.setAttribute('aria-label', unmuteIconLabel); + } else { + muteIcon.style.display = 'flex'; + unmuteIcon.style.display = 'none'; + controls.setAttribute('aria-label', muteIconLabel); + } + }; + showHideMuteIcon(video?.muted); + + const toggleMute = (vid) => { + vid.muted = !vid.muted; + const isMuted = vid.muted; + showHideMuteIcon(isMuted); + }; + + controls.addEventListener('click', () => toggleMute(video)); +}; diff --git a/styles/styles.css b/styles/styles.css index 4b9f0cb0..696dd9ae 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1120,6 +1120,11 @@ main.blue-contract .section.section-with-title p { width: 100%; } +:root.redesign-v2 .section .expanded-width { + max-width: var(--wrapper-expanded-width); + width: 100%; +} + /* Section Background colors */ .section--black-background { --text-color: var(--c-white); @@ -1540,7 +1545,8 @@ main.blue-contract .section.section-with-title p { } /* Global video block styles */ -.v2-video__playback-button { +.v2-video__playback-button, +.v2-video__mute-controls{ position: absolute; top: 18px; right: 18px; @@ -1552,28 +1558,43 @@ main.blue-contract .section.section-with-title p { padding: 0; } -.v2-video__playback-button .icon-play-video { +.v2-video__mute-controls{ + top: 70px; +} + +.v2-video__playback-button .icon-play-video, +.v2-video__mute-controls .icon-mute{ display: none; justify-content: center; } -.v2-video__playback-button .icon-pause-video { +.v2-video__playback-button .icon-pause-video, +.v2-video__mute-controls .icon-unmute{ display: flex; justify-content: center; } -.v2-video__playback-button:hover svg circle { +.v2-video__playback-button:hover svg circle, +.v2-video__mute-controls:hover svg circle{ fill: var(--c-grey-50); } +.v2-video__playback-button .icon, +.v2-video__mute-controls .icon{ + height: 100%; + align-items: center; +} + /* stylelint-disable-next-line no-descending-specificity */ -.v2-video__playback-button .icon svg { +.v2-video__playback-button .icon svg, +.v2-video__mute-controls .icon svg{ width: 32px; height: 32px; } @media (max-width: 744px) { - .v2-content-card__item .v2-video__playback-button { + .v2-content-card__item .v2-video__playback-button, + .v2-content-card__item .v2-video__mute-controls{ top: 18px !important; } } @@ -1585,12 +1606,18 @@ main.blue-contract .section.section-with-title p { --text-block-max-width: 506px; } - .v2-video__playback-button { + .v2-video__playback-button, + .v2-video__mute-controls{ bottom: 18px; top: initial; + } + + .v2-video__mute-controls{ + bottom: 70px; } } + @media (min-width: 1200px) { :root { --nav-height: 64px; @@ -1598,11 +1625,17 @@ main.blue-contract .section.section-with-title p { :root.redesign-v2 { --wrapper-width: 1170px; + --wrapper-expanded-width: 1440px; --section-padding: 56px 0 136px; --section-div-padding: 24px 0; --text-block-max-width: 694px; } + :root.redesign-v2 .section .expanded-width { + padding-left: 32px; + padding-right: 32px; + } + .redesign-v2 .section div.full-width { margin: 0; } From 5ac6f9b18669b0de4c612f085da6f7d802a51918 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:59:22 +0100 Subject: [PATCH 33/41] Revisit scroll behaviour #321 (#322) Refactor scroll to top --- blocks/footer/footer.css | 31 +++++++++++++++++--------- blocks/footer/footer.js | 48 +++++++++++----------------------------- placeholder.json | 4 ++++ styles/styles.css | 3 +++ 4 files changed, 41 insertions(+), 45 deletions(-) diff --git a/blocks/footer/footer.css b/blocks/footer/footer.css index 1e7e9a4d..344b4cf2 100644 --- a/blocks/footer/footer.css +++ b/blocks/footer/footer.css @@ -158,27 +158,38 @@ display: inline-block; } -.v2-scroll-to-top { +.scroll-to-top-container { + padding: 24px; + display: block; + position: absolute; + top: calc(100vh - 1px); + right: 0; + bottom: 0; + z-index: 1; +} + +a.scroll-to-top { background-color: var(--c-white); border-radius: 50%; border: 1px solid var(--c-grey-400); - bottom: 20px; + display: block; box-shadow: 0 4px 70px 0 rgb(0 0 0 / 20%); color: var(--c-main-black); cursor: pointer; - display: none; padding: 12px; - position: fixed; - right: 30px; + position: sticky; + top: calc(100vh - 24px - 50px); z-index: 99; } -.v2-scroll-to-top:hover { +a.scroll-to-top:hover { background-color: var(--c-grey-50); + color: var(--c-main-black); } -.v2-scroll-to-top:active { +a.scroll-to-top:active { background-color: var(--c-grey-100); + color: var(--c-main-black); } .footer-list .footer__title:not(.expand):focus, @@ -187,17 +198,17 @@ outline: 2px solid var(--border-focus); } -button.v2-scroll-to-top:focus { +a.scroll-to-top:focus { outline: 0; } -.v2-scroll-to-top:focus-visible { +.scroll-to-top:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 1px; } /* stylelint-disable-next-line no-descending-specificity */ -.v2-scroll-to-top svg { +.scroll-to-top svg { display: block; } diff --git a/blocks/footer/footer.js b/blocks/footer/footer.js index 6530969d..e34c0015 100644 --- a/blocks/footer/footer.js +++ b/blocks/footer/footer.js @@ -1,39 +1,17 @@ import { readBlockConfig, decorateIcons, getMetadata } from '../../scripts/lib-franklin.js'; -import { createElement, getLanguagePath } from '../../scripts/common.js'; +import { createElement, getLanguagePath, getTextLabel } from '../../scripts/common.js'; /* eslint-disable no-use-before-define */ -function displayScrollToTop(buttonEl) { - if (document.body.scrollTop > 160 || document.documentElement.scrollTop > 160) { - buttonEl.style.display = 'block'; - } else { - buttonEl.style.display = 'none'; - } -} - -function goToTopFunction() { - let timeOut; - if (document.body.scrollTop !== 0 || document.documentElement.scrollTop !== 0) { - window.scrollBy(0, -50); - timeOut = setTimeout(goToTopFunction, 10); - } else { - clearTimeout(timeOut); - } -} - function addScrollToTopButton(mainEl) { - const scrollToTopButton = createElement('button', { - classes: 'v2-scroll-to-top', - props: { - title: 'Go to the top of the page', - }, - }); - scrollToTopButton.addEventListener('click', goToTopFunction); - window.addEventListener('scroll', () => displayScrollToTop(scrollToTopButton)); - const svgIcon = document.createRange().createContextualFragment(` - - - `); - scrollToTopButton.append(...svgIcon.children); + const scrollToTopButton = document.createRange().createContextualFragment(` + + `); mainEl.append(scrollToTopButton); } @@ -57,7 +35,7 @@ export default async function decorate(block) { // for custom footer we don't need the external link section, // so check if columns block exist - const coulmnsWrapper = footer.querySelector('.columns'); + const columnsWrapper = footer.querySelector('.columns'); let footerBar = footer.children[1]; let footerCopyright = footer.children[2]; let mainLinkWrapper; @@ -67,8 +45,8 @@ export default async function decorate(block) { openExternalLinksInNewTab(footer); - if (coulmnsWrapper) { - mainLinkWrapper = coulmnsWrapper.parentElement; + if (columnsWrapper) { + mainLinkWrapper = columnsWrapper.parentElement; wrapSocialMediaLinks(mainLinkWrapper); mainLinkWrapper.classList.add('footer-links-wrapper'); // in Word, it is edited like a column block, but we style it differently diff --git a/placeholder.json b/placeholder.json index 9c71ee1a..e4df0440 100644 --- a/placeholder.json +++ b/placeholder.json @@ -206,6 +206,10 @@ { "Key": "view less", "Text": "View less" + }, + { + "Key": "go to top", + "Text": "Go to the top of the page" } ], ":type": "sheet" diff --git a/styles/styles.css b/styles/styles.css index 696dd9ae..2e4f6ccc 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -305,6 +305,8 @@ } html { + min-height: 100%; + position: relative; font-size: 10px; } @@ -1069,6 +1071,7 @@ main.blue-contract .section.section-with-title p { --background-gradient: rgb(255 255 255 / 100%); font-size: 16px; + scroll-behavior: smooth; } .redesign-v2 ul { From 8e006707588fba8e83f68ac284c3865f627fc243 Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Mon, 18 Dec 2023 20:08:11 +0100 Subject: [PATCH 34/41] add recalls date since when 0 recalls are present --- blocks/vin-number/vin-number.js | 10 +++++++--- placeholder.json | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/blocks/vin-number/vin-number.js b/blocks/vin-number/vin-number.js index 6b64bc12..fd2aeaa4 100644 --- a/blocks/vin-number/vin-number.js +++ b/blocks/vin-number/vin-number.js @@ -66,13 +66,13 @@ function capitalize(text) { } function renderRecalls(recallsData) { - const resultText = document.querySelector(`.${blockName}__results-text`); - resultText.innerText = getTextLabel('result text').replace(/\${count}/, recallsData.number_of_recalls).replace(/\${vin}/, recallsData.vin); + const resultTextEle = document.querySelector(`.${blockName}__results-text`); + let resultContent = getTextLabel('result text').replace(/\${count}/, recallsData.number_of_recalls).replace(/\${vin}/, recallsData.vin); let noFrenchInfo = false; + const recallsOldestDate = isFrench ? new Date(recallsData.recalls_since).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' }) : recallsData.recalls_since; if (recallsData.recalls_available) { const blockEl = document.querySelector(`.${blockName}__recalls-wrapper`); - const recallsOldestDate = isFrench ? new Date(recallsData.recalls_since).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' }) : recallsData.recalls_since; const listWrapperFragment = docRange.createContextualFragment(`
@@ -126,7 +126,11 @@ function renderRecalls(recallsData) { blockEl.append(listWrapperFragment); blockEl.appendChild(list); + } else { + resultContent = `${resultContent} [${getTextLabel('recall_available_info')} ${recallsOldestDate}]`; } + + resultTextEle.innerText = resultContent; } function getAPIConfig() { diff --git a/placeholder.json b/placeholder.json index e4df0440..a98c892b 100644 --- a/placeholder.json +++ b/placeholder.json @@ -210,6 +210,10 @@ { "Key": "go to top", "Text": "Go to the top of the page" + }, + { + "Key": "recall_available_info", + "Text": "since:" } ], ":type": "sheet" From 10d74bcfc68551ab6ecf0484a88c1eeb03492d5c Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Tue, 2 Jan 2024 19:16:02 +0100 Subject: [PATCH 35/41] merge and resolve conflicts add model and make 545 --- blocks/vin-number/vin-number.css | 10 ++++++---- blocks/vin-number/vin-number.js | 18 +++++++++++++++++- placeholder.json | 8 ++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/blocks/vin-number/vin-number.css b/blocks/vin-number/vin-number.css index 785ebbe1..30c18f42 100644 --- a/blocks/vin-number/vin-number.css +++ b/blocks/vin-number/vin-number.css @@ -61,11 +61,8 @@ font-family: var(--ff-volvo-novum-medium); } -.vin-number__recalls-wrapper { - margin-top: 25px; -} - .vin-number__recalls-heading-wrapper { + margin-top: 25px; display: flex; align-items: center; gap: 16px; @@ -110,6 +107,11 @@ color: var(--c-leaf-4); } +.vin-number__recalls-make-wrapper { + display: flex; + gap: 32px; +} + @media (min-width: 744px) { :root { --label-top: 3px; diff --git a/blocks/vin-number/vin-number.js b/blocks/vin-number/vin-number.js index fd2aeaa4..a69159b9 100644 --- a/blocks/vin-number/vin-number.js +++ b/blocks/vin-number/vin-number.js @@ -71,8 +71,24 @@ function renderRecalls(recallsData) { let noFrenchInfo = false; const recallsOldestDate = isFrench ? new Date(recallsData.recalls_since).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' }) : recallsData.recalls_since; + const blockEl = document.querySelector(`.${blockName}__recalls-wrapper`); + + const recallsMake = createElement('div', { classes: `${blockName}__recalls-make-wrapper` }); + const makeFragment = docRange.createContextualFragment(` +
+ ${getTextLabel('make')} + ${recallsData.make} +
+
+ ${getTextLabel('model')} + ${recallsData.model} +
+ `); + + recallsMake.append(...makeFragment.children); + blockEl.append(recallsMake); + if (recallsData.recalls_available) { - const blockEl = document.querySelector(`.${blockName}__recalls-wrapper`); const listWrapperFragment = docRange.createContextualFragment(`
diff --git a/placeholder.json b/placeholder.json index a98c892b..0f07d84e 100644 --- a/placeholder.json +++ b/placeholder.json @@ -214,6 +214,14 @@ { "Key": "recall_available_info", "Text": "since:" + }, + { + "Key": "make", + "Text": "Make" + }, + { + "Key": "model", + "Text": "Model" } ], ":type": "sheet" From edefa1b3defb31e54ea7b34c28712bc322e342a1 Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Tue, 2 Jan 2024 19:24:38 +0100 Subject: [PATCH 36/41] add padding bottom for french page --- blocks/vin-number/vin-number.css | 1 + 1 file changed, 1 insertion(+) diff --git a/blocks/vin-number/vin-number.css b/blocks/vin-number/vin-number.css index 30c18f42..bc03d57b 100644 --- a/blocks/vin-number/vin-number.css +++ b/blocks/vin-number/vin-number.css @@ -57,6 +57,7 @@ .vin-number__no-french-info { padding-top: 20px; + padding-bottom: 20px; color: var(--c-grey-800); font-family: var(--ff-volvo-novum-medium); } From ff944c4d3e52bc696a6161bab26d3ccab569a80f Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Tue, 2 Jan 2024 19:55:18 +0100 Subject: [PATCH 37/41] add model year --- blocks/vin-number/vin-number.js | 4 ++++ placeholder.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/blocks/vin-number/vin-number.js b/blocks/vin-number/vin-number.js index a69159b9..4075d659 100644 --- a/blocks/vin-number/vin-number.js +++ b/blocks/vin-number/vin-number.js @@ -75,6 +75,10 @@ function renderRecalls(recallsData) { const recallsMake = createElement('div', { classes: `${blockName}__recalls-make-wrapper` }); const makeFragment = docRange.createContextualFragment(` +
+ ${getTextLabel('model year')} + ${recallsData.year} +
${getTextLabel('make')} ${recallsData.make} diff --git a/placeholder.json b/placeholder.json index 0f07d84e..e0376b31 100644 --- a/placeholder.json +++ b/placeholder.json @@ -222,6 +222,10 @@ { "Key": "model", "Text": "Model" + }, + { + "Key": "model year", + "Text": "Model year" } ], ":type": "sheet" From bf1b27d8a2e924866f3acef33248c2ada1b875ec Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Wed, 3 Jan 2024 11:06:42 +0100 Subject: [PATCH 38/41] change css --- blocks/vin-number/vin-number.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blocks/vin-number/vin-number.css b/blocks/vin-number/vin-number.css index bc03d57b..fb93eab9 100644 --- a/blocks/vin-number/vin-number.css +++ b/blocks/vin-number/vin-number.css @@ -110,7 +110,8 @@ .vin-number__recalls-make-wrapper { display: flex; - gap: 32px; + flex-direction: row; + gap: 16px; } @media (min-width: 744px) { From ff4f91fa2cc294463818f161afc2586085393a6b Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Wed, 3 Jan 2024 11:08:40 +0100 Subject: [PATCH 39/41] change flex direction --- blocks/vin-number/vin-number.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/vin-number/vin-number.css b/blocks/vin-number/vin-number.css index fb93eab9..d6659441 100644 --- a/blocks/vin-number/vin-number.css +++ b/blocks/vin-number/vin-number.css @@ -110,7 +110,7 @@ .vin-number__recalls-make-wrapper { display: flex; - flex-direction: row; + flex-direction: column; gap: 16px; } From 686f2db40dc4a40de7543c15dd26f625f138218b Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:04:26 +0100 Subject: [PATCH 40/41] Content Carousel typography adjustments #297 Content Carousel fading effect #305 (#307) merge conflicts --- styles/styles.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/styles/styles.css b/styles/styles.css index 2e4f6ccc..9d3e0209 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -1097,6 +1097,8 @@ main.blue-contract .section.section-with-title p { /* stylelint-disable-next-line no-descending-specificity */ .redesign-v2 .section { + --background-color: var(--c-white); + padding: var(--section-padding); background-color: var(--background-color); scroll-margin-top: calc(var(--nav-height) + var(--inpage-navigation-height)); From e8cc072cc79fbb1f3cf4bb1fff4a7cf9e7194618 Mon Sep 17 00:00:00 2001 From: Syb <133873665+cogniSyb@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:59:22 +0100 Subject: [PATCH 41/41] Revisit scroll behaviour #321 (#322) Refactor scroll to top --- placeholder.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/placeholder.json b/placeholder.json index e0376b31..180252bb 100644 --- a/placeholder.json +++ b/placeholder.json @@ -226,6 +226,10 @@ { "Key": "model year", "Text": "Model year" + }, + { + "Key": "go to top", + "Text": "Go to the top of the page" } ], ":type": "sheet"