diff --git a/blocks/accordion/accordion.js b/blocks/accordion/accordion.js index 9d43a7e5e..abb10c96a 100644 --- a/blocks/accordion/accordion.js +++ b/blocks/accordion/accordion.js @@ -1,4 +1,4 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; import fragmentBlock from '../fragment/fragment.js'; /* Function checks if the content of the provied element is just a link to other doc */ @@ -19,13 +19,13 @@ export default async function decorate(block) { ':scope > div:nth-child(2)', ); - const headerButton = createElement('button', 'accordion-header-button'); - const headerEl = createElement('h2', 'accordion-header'); - const plusEl = createElement('div', 'accordion-close'); + const headerButton = createElement('button', { classes: 'accordion-header-button' }); + const headerEl = createElement('h2', { classes: 'accordion-header' }); + const plusEl = createElement('div', { classes: 'accordion-close' }); headerEl.innerHTML = accordionHeader?.innerHTML; headerButton.append(headerEl, plusEl); - const contentEl = createElement('div', ['accordion-content', 'accordion-content-close']); + const contentEl = createElement('div', { classes: ['accordion-content', 'accordion-content-close'] }); if (isContentLink(accordionContent)) { await fragmentBlock(accordionContent); @@ -51,7 +51,7 @@ export default async function decorate(block) { contentEl.innerHTML = accordionContent.innerHTML; - const accordionEl = createElement('div', ['accordion-item', 'accordion-item-close']); + const accordionEl = createElement('div', { classes: ['accordion-item', 'accordion-item-close'] }); accordionEl.append(headerButton); accordionEl.append(contentEl); diff --git a/blocks/breadcrumb/breadcrumb.js b/blocks/breadcrumb/breadcrumb.js index b4f4ecc3a..0da147bcc 100644 --- a/blocks/breadcrumb/breadcrumb.js +++ b/blocks/breadcrumb/breadcrumb.js @@ -1,7 +1,7 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; export default function decorate(block) { - const breadcrumbItems = createElement('ul', 'breadcrumb-list'); + const breadcrumbItems = createElement('ul', { classes: 'breadcrumb-list' }); const articleUrl = (window.location.href).split('/').pop(); const articleName = articleUrl.replaceAll('-', ' ').toLowerCase(); diff --git a/blocks/cards/cards.js b/blocks/cards/cards.js index 7f0e7dd2f..d8df8fbb8 100644 --- a/blocks/cards/cards.js +++ b/blocks/cards/cards.js @@ -1,5 +1,5 @@ import { createOptimizedPicture } from '../../scripts/lib-franklin.js'; -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; const updateListElements = (ul, isDarkVar = false, isCTABlock = false) => { const lis = [...ul.children]; @@ -17,7 +17,9 @@ const updateListElements = (ul, isDarkVar = false, isCTABlock = false) => { const { length } = buttons; if (length === 0) return; const tempLink = [...buttons].at(-1).firstChild; - const newLink = createElement('a', '', { href: tempLink.href, title: tempLink.title }); + const newLink = createElement('a', '', { + props: { href: tempLink.href, title: tempLink.title }, + }); buttons[length - 1].remove(); newLink.innerHTML = li.innerHTML; diff --git a/blocks/carousel/carousel.js b/blocks/carousel/carousel.js index 92d87b95f..9119d7a81 100644 --- a/blocks/carousel/carousel.js +++ b/blocks/carousel/carousel.js @@ -1,6 +1,7 @@ import { - createElement, isVideoLink, selectVideoLink, wrapImageWithVideoLink, addVideoShowHandler, -} from '../../scripts/scripts.js'; + isVideoLink, selectVideoLink, wrapImageWithVideoLink, addVideoShowHandler, +} from '../../scripts/video-helper.js'; +import { createElement } from '../../scripts/common.js'; const debounceDelay = 30; @@ -86,12 +87,12 @@ function createDesktopControls(ul) { ul.scrollBy({ top: 0, left, behavior: 'smooth' }); } - const desktopControls = createElement('ul', 'desktop-controls'); + const desktopControls = createElement('ul', { classes: 'desktop-controls' }); const leftButtonContainer = createElement('li'); - const leftButton = createElement('button', [], { type: 'button' }); + const leftButton = createElement('button', { props: { type: 'button' } }); leftButton.textContent = 'Left'; const rightButtonContainer = createElement('li'); - const rightButton = createElement('button', [], { type: 'button' }); + const rightButton = createElement('button', { props: { type: 'button' } }); rightButton.textContent = 'Right'; leftButtonContainer.append(leftButton); rightButtonContainer.append(rightButton); @@ -110,11 +111,11 @@ function createDesktopControls(ul) { function createDotControls(ul) { // create carousel controls for mobile - const dotControls = createElement('ul', ['mobile-controls']); + const dotControls = createElement('ul', { classes: 'mobile-controls' }); [...ul.children].forEach((item, j) => { const control = createElement('li'); if (!j) control.className = 'active'; - const button = createElement('button', '', { type: 'button' }); + const button = createElement('button', { props: { type: 'button' } }); button.textContent = j + 1; control.append(button); control.firstElementChild.addEventListener('click', () => { @@ -156,7 +157,7 @@ function replaceBlockClasses(block) { export default function decorate(block) { replaceBlockClasses(block); - const ul = createElement('ul', ['items']); + const ul = createElement('ul', { classes: ['items'] }); // We support two formats: // 1. Each slide in a row (can be splitted into columns) @@ -170,14 +171,14 @@ export default function decorate(block) { column.classList.add('carousel-item-column'); }); - const listItem = createElement('li', ['carousel-item']); + const listItem = createElement('li', { classes: ['carousel-item'] }); listItem.append(...row.children); ul.append(listItem); }); [...ul.children].forEach((li) => { // Add wrapper around the content - const container = createElement('div', ['carousel-content-wrapper']); + const container = createElement('div', { classes: ['carousel-content-wrapper'] }); container.innerHTML = li.innerHTML; li.innerHTML = ''; @@ -215,12 +216,12 @@ export default function decorate(block) { const [header, subheader] = [...column.querySelectorAll('h1, h2, h3, h4, h5, h6')].sort((h1, h2) => h1.tagName[1] - h2.tagName[1]); if (header) { - const newHeader = createElement('h2', 'carousel-item-header'); + const newHeader = createElement('h2', { classes: 'carousel-item-header' }); newHeader.innerHTML = header.innerHTML; header.replaceWith(newHeader); } if (subheader) { - const newSubheader = createElement('h3', 'carousel-item-subheader'); + const newSubheader = createElement('h3', { classes: 'carousel-item-subheader' }); newSubheader.innerHTML = subheader.innerHTML; subheader.replaceWith(newSubheader); } @@ -240,7 +241,7 @@ export default function decorate(block) { // warpping all paragraphs and headers const textElements = column.querySelectorAll('p, h2, h3'); if (textElements.length) { - const textWrapper = createElement('div', 'carousel-item-text'); + const textWrapper = createElement('div', { classes: 'carousel-item-text' }); textWrapper.append(...textElements); column.append(textWrapper); } diff --git a/blocks/columns/columns.js b/blocks/columns/columns.js index cc17e61df..b1ccac47b 100644 --- a/blocks/columns/columns.js +++ b/blocks/columns/columns.js @@ -1,14 +1,14 @@ import { addVideoShowHandler, - createElement, - getAllElWithChildren, isVideoLink, selectVideoLink, wrapImageWithVideoLink, -} from '../../scripts/scripts.js'; +} from '../../scripts/video-helper.js'; +import { createElement } from '../../scripts/common.js'; +import { getAllElWithChildren } from '../../scripts/scripts.js'; const decorateUnderline = (col) => { - const hr = createElement('hr', 'column-underline'); + const hr = createElement('hr', { classes: 'column-underline' }); const u = col.querySelector('u'); if (!u) { const strong = col.firstElementChild.querySelector('strong'); diff --git a/blocks/cookie-preference-center/cookie-preference-center.js b/blocks/cookie-preference-center/cookie-preference-center.js index e94d5d155..5775c3756 100644 --- a/blocks/cookie-preference-center/cookie-preference-center.js +++ b/blocks/cookie-preference-center/cookie-preference-center.js @@ -1,7 +1,7 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; export default function decorate(block) { - const button = createElement('button', 'cta', { onclick: 'OneTrust.ToggleInfoDisplay();' }); + const button = createElement('button', { classes: 'cta', props: { onclick: 'OneTrust.ToggleInfoDisplay();' } }); button.textContent = block.textContent; block.textContent = ''; diff --git a/blocks/download-specs/download-specs.js b/blocks/download-specs/download-specs.js index 125b59a39..065cc2b00 100644 --- a/blocks/download-specs/download-specs.js +++ b/blocks/download-specs/download-specs.js @@ -1,24 +1,24 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; export default function decorate(block) { const children = [...block.children]; - const downloadSpecsSection = createElement('div', 'download-specs-section'); + const downloadSpecsSection = createElement('div', { classes: 'download-specs-section' }); const specsTitle = children.shift().querySelector('h3'); - const downloadSpecsList = createElement('ul', 'download-specs-list'); + const downloadSpecsList = createElement('ul', { classes: 'download-specs-list' }); children.forEach((e, idx) => { - const downloadItem = createElement('li', ['download-specs-item', `download-specs-item-${idx + 1}`]); + const downloadItem = createElement('li', { classes: ['download-specs-item', `download-specs-item-${idx + 1}`] }); - const anchorElement = createElement('a', 'anchor-element'); + const anchorElement = createElement('a', { classes: 'anchor-element' }); const linkUrl = e.querySelector('a').getAttribute('href'); anchorElement.setAttribute('href', linkUrl); anchorElement.setAttribute('target', '_blank'); - const img = createElement('img', 'download-specs-icon'); + const img = createElement('img', { classes: 'download-specs-icon' }); img.src = '/icons/pdficon.png'; - const pElement = createElement('p', 'p-element'); + const pElement = createElement('p', { classes: 'p-element' }); const text = e.querySelector('div').innerText; pElement.textContent = text; diff --git a/blocks/download/download.js b/blocks/download/download.js index ced3d5880..a3d0034a5 100644 --- a/blocks/download/download.js +++ b/blocks/download/download.js @@ -1,12 +1,12 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; const buildRegularDownloadBlock = (block) => { const children = [...block.children]; - const downloadList = createElement('ul', 'download-list'); + const downloadList = createElement('ul', { classes: 'download-list' }); children.forEach((e, idx) => { - const downloadItem = createElement('li', ['download-item', `download-item-${idx + 1}`]); + const downloadItem = createElement('li', { classes: ['download-item', `download-item-${idx + 1}`] }); const image = e.querySelector('picture').innerHTML; @@ -52,10 +52,10 @@ const buildRegularDownloadBlock = (block) => { const buildNewDownloadBlock = (block) => { const children = [...block.children]; - const downloadNewList = createElement('ul', 'download-new-list'); + const downloadNewList = createElement('ul', { classes: 'download-new-list' }); children.forEach((e, idx) => { - const downloadItem = createElement('li', ['download-new-item', `download-new-item-${idx + 1}`]); + const downloadItem = createElement('li', { classes: ['download-new-item', `download-new-item-${idx + 1}`] }); const allTexts = e.querySelectorAll('li'); const linkUrl = e.querySelector('a').getAttribute('href'); diff --git a/blocks/eloqua-form/eloqua-form.js b/blocks/eloqua-form/eloqua-form.js index a59a7db0b..26c9fca79 100644 --- a/blocks/eloqua-form/eloqua-form.js +++ b/blocks/eloqua-form/eloqua-form.js @@ -1,4 +1,5 @@ -import { createElement, loadScriptIfNotLoadedYet } from '../../scripts/scripts.js'; +import { loadScriptIfNotLoadedYet } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; // Every eloqua form has it's own JS, CSS and HTML. // Once the form is loaded all the JS and CSS are added to the body. @@ -40,7 +41,7 @@ const loadFormScripts = async (elqForm) => { // the script element added by innerHTML is NOT executed // the workaround is to create the new script tag, copy attibutes and content - const newScript = createElement('script', '', { type: 'text/javascript' }); + const newScript = createElement('script', { props: { type: 'text/javascript' } }); newScript.innerHTML = script.innerHTML; document.body.append(newScript); @@ -131,7 +132,7 @@ const addForm = async (block) => { } const text = await data.text(); - const formWrapper = createElement('div', 'eloqua-form-container'); + const formWrapper = createElement('div', { classes: 'eloqua-form-container' }); formWrapper.innerHTML = text; block.innerHTML = ''; block.append(formWrapper); diff --git a/blocks/embed/embed.js b/blocks/embed/embed.js index 78c6d71d1..58d3bd3ae 100644 --- a/blocks/embed/embed.js +++ b/blocks/embed/embed.js @@ -1,7 +1,7 @@ import { selectVideoLink, addPlayIcon, showVideoModal, isLowResolutionVideoUrl, createIframe, createLowResolutionBanner, -} from '../../scripts/scripts.js'; +} from '../../scripts/video-helper.js'; export default function decorate(block) { const isAutoplay = block.classList.contains('autoplay'); diff --git a/blocks/explore-articles/explore-articles.js b/blocks/explore-articles/explore-articles.js index e553abd04..962002374 100644 --- a/blocks/explore-articles/explore-articles.js +++ b/blocks/explore-articles/explore-articles.js @@ -1,4 +1,4 @@ -import { createElement, getTextLabel } from '../../scripts/scripts.js'; +import { createElement, getTextLabel } from '../../scripts/common.js'; import { getAllArticles } from '../recent-articles/recent-articles.js'; const allArticles = await getAllArticles(); @@ -26,7 +26,7 @@ const getOptions = (list, placeholder) => { const options = []; list.unshift(placeholder); list.forEach((el) => { - const option = createElement('option', '', { value: el }); + const option = createElement('option', { props: { value: el } }); option.innerText = el; if (el.length !== 0) options.push(option); }); @@ -43,34 +43,46 @@ const buildSelect = (type, array, text) => { }; const buildArticle = (e) => { - const article = createElement('div', ['article']); - const articleImage = createElement('div', 'article-image'); - const articleContent = createElement('div', 'article-content'); + const article = createElement('div', { classes: ['article'] }); + const articleImage = createElement('div', { classes: 'article-image' }); + const articleContent = createElement('div', { classes: 'article-content' }); const linkUrl = new URL(e.path, window.location.origin); const categoriesWithDash = e.category.replaceAll(' ', '-').toLowerCase(); const categoryUrl = new URL(`magazine/categories/${categoriesWithDash}`, window.location.origin); - const image = createElement('img', 'image', { src: e.image }); + const image = createElement('img', { + classes: 'image', + props: { src: e.image }, + }); articleImage.append(image); - const category = createElement('a', 'article-category', { href: categoryUrl }); + const category = createElement('a', { + classes: 'article-category', + props: { href: categoryUrl }, + }); category.innerText = e.category; - const link = createElement('a', 'article-link', { href: linkUrl }); - const title = createElement('h3', 'article-title'); + const link = createElement('a', { + classes: 'article-link', + props: { href: linkUrl }, + }); + const title = createElement('h3', { classes: 'article-title' }); title.innerText = e.title; - const subtitle = createElement('p', 'article-subtitle'); + const subtitle = createElement('p', { classes: 'article-subtitle' }); subtitle.innerText = e.subtitle; link.append(title); if (e.subtitle.length !== 0) link.append(subtitle); - const truck = createElement('div', 'article-truck'); - const truckText = createElement('p', 'article-truck-text'); + const truck = createElement('div', { classes: 'article-truck' }); + const truckText = createElement('p', { classes: 'article-truck-text' }); truckText.innerText = e.truck; - const truckIcon = createElement('img', 'article-truck-icon', { src: '/icons/Truck_Key_icon.svg', alt: 'truck icon' }); + const truckIcon = createElement('img', { + classes: 'article-truck-icon', + props: { src: '/icons/Truck_Key_icon.svg', alt: 'truck icon' }, + }); truck.append(truckIcon, truckText); articleContent.append(category, link); @@ -133,23 +145,23 @@ const buildArticleList = (articles) => { const totalArticlesNumber = addAllArrays(groupedArticles); const amountOfGroups = articleGroups.length; - const paginationSection = createElement('div', 'pagination-section'); - const articlesSection = createElement('div', 'explore-articles-articles'); + const paginationSection = createElement('div', { classes: 'pagination-section' }); + const articlesSection = createElement('div', { classes: 'explore-articles-articles' }); - const amountOfArticles = createElement('p', 'article-amount'); + const amountOfArticles = createElement('p', { classes: 'article-amount' }); amountOfArticles.textContent = (totalArticlesNumber !== 0) ? `${totalArticlesNumber} articles` : getTextLabel('No article Message'); paginationSection.append(amountOfArticles); articlesSection.append(paginationSection); - const moreSection = createElement('div', 'explore-articles-more'); - const moreButton = createElement('button', 'more-btn'); + const moreSection = createElement('div', { classes: 'explore-articles-more' }); + const moreButton = createElement('button', { classes: 'more-btn' }); moreButton.textContent = getTextLabel('Load more articles button'); moreButton.addEventListener('click', (evt) => loadMoreArticles(evt, articleGroups, amountOfGroups)); if (totalArticlesNumber > articlesPerChunk) moreSection.append(moreButton); if (articleGroups.length !== 0) { - const articleListSection = createElement('div', 'article-list'); + const articleListSection = createElement('div', { classes: 'article-list' }); buildFirstArticles(articleGroups, articleListSection); articlesSection.append(articleListSection, moreSection); } @@ -185,14 +197,14 @@ const handleForm = () => { }; const buildFieldset = () => { - const formSection = createElement('div', 'explore-articles-fieldset'); + const formSection = createElement('div', { classes: 'explore-articles-fieldset' }); const form = createElement('form', ['form', 'filter-list'], { method: 'get', name: 'article-fieldset' }); form.addEventListener('change', handleForm); const fieldset = createElement('fieldset', ['fieldset', 'filter-list'], { method: 'get', name: 'article-fieldset', id: 'explore-magazine-fieldset' }); - const categoryField = createElement('div', 'category-field'); - const trucksField = createElement('div', 'trucks-field'); + const categoryField = createElement('div', { classes: 'category-field' }); + const trucksField = createElement('div', { classes: 'trucks-field' }); categoryField.append(buildSelect('categories', allCategories, categoryPlaceholder)); trucksField.append(buildSelect('trucks', allTrucks, truckPlaceholder)); @@ -209,12 +221,12 @@ export default async function decorate(block) { const children = block.querySelectorAll('p'); const [title, text] = children; - const generalSection = createElement('div', 'explore-articles-section'); + const generalSection = createElement('div', { classes: 'explore-articles-section' }); - const headingSection = createElement('div', 'explore-articles-heading'); - const contentSection = createElement('div', 'explore-articles-content'); + const headingSection = createElement('div', { classes: 'explore-articles-heading' }); + const contentSection = createElement('div', { classes: 'explore-articles-content' }); - const h4Element = createElement('h4', 'explore-articles-title'); + const h4Element = createElement('h4', { classes: 'explore-articles-title' }); h4Element.innerText = title.innerText; text.classList.add('explore-articles-text'); diff --git a/blocks/features/features.js b/blocks/features/features.js index a6d8362b3..7fe324fa2 100644 --- a/blocks/features/features.js +++ b/blocks/features/features.js @@ -1,9 +1,9 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; export default function decorate(block) { block.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((title) => { // convert to h4 because it might be any header level - const h4 = createElement('h4', 'feature-title'); + const h4 = createElement('h4', { classes: 'feature-title' }); h4.innerHTML = title.innerHTML; title.replaceWith(h4); }); diff --git a/blocks/find-dealer/find-dealer.js b/blocks/find-dealer/find-dealer.js index a9061a8b6..d3d75ad16 100644 --- a/blocks/find-dealer/find-dealer.js +++ b/blocks/find-dealer/find-dealer.js @@ -1,12 +1,15 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; export default function decorate(block) { const container = block.querySelector(':scope > div > div'); - const inputContainer = createElement('div', 'find-dealer-input-wrapper'); - const input = createElement('input', 'find-dealer-input', { - title: 'code', - type: 'text', - placeholder: 'Enter Zip', + const inputContainer = createElement('div', { classes: 'find-dealer-input-wrapper' }); + const input = createElement('input', { + classes: 'find-dealer-input', + props: { + title: 'code', + type: 'text', + placeholder: 'Enter Zip', + }, }); container.className = 'find-dealer-form-container'; input.onkeydown = (e) => { diff --git a/blocks/footer/footer.css b/blocks/footer/footer.css index d9ef259b1..cccfcbc0f 100644 --- a/blocks/footer/footer.css +++ b/blocks/footer/footer.css @@ -1,250 +1,247 @@ -.footer-wrapper { +.footer { background: var(--c-primary-black); + color: var(--c-secondary-silver); } -.footer { - color: var(--c-primary-white); - padding: 60px 32px 10px; - font-size: 14px; - margin: auto; - max-width: 1376px; +.footer svg { + display: block; + height: 1em; + width: 1em; } -.footer ul { - list-style: none; +/* General ul styles */ +.prefooter ul, +.footer-menu__column ul, +.footer-legal ul, +.footer-menu__socialmedia, +.footer-truck-list__items { + margin: 0; padding: 0; + list-style: none; } -.footer .footer-social-media-section { - margin-top: 32px; - display: flex; - gap: 20px; - flex-wrap: wrap; +/* Apply max width to all the necessary footer items */ +.prefooter ul, +.footer-truck-list__wrapper, +.footer-legal ul, +.footer-menu { + max-width: var(--wrapper-width); + margin: 0 auto; } -.footer .footer-social-media { - border-radius: 17.5px; - border: 2px solid var(--c-secondary-dark-gold); - color: var(--c-secondary-dark-gold); - text-decoration: none; - display: flex; - width: 35px; - height: 35px; - align-items: center; - justify-content: center; - font-size: 20px; +/* Prefooter */ +.prefooter { + background-color: var(--c-primary-gold); } -.footer .footer-social-media:hover { - color: #af9866; - border-color: #af9866; +.prefooter ul { + padding: 8px 16px; } -.footer .footer-social-media::before { - width: 17px; +.prefooter li:not(:last-child) { + border-bottom: 1px solid var(--c-primary-black); } -.footer h1, -.footer h2, -.footer h3 { - font-size: 14px; - font-weight: 400; - margin-bottom: 5px; - border-bottom: 2px solid; - line-height: 22px; - margin-top: 0; -} - -.footer ul li a { - color: var(--c-primary-white); +.prefooter a { + font-size: 18px; + font-family: var(--ff-subheadings-medium); + line-height: 140%; + letter-spacing: 0.36px; + color: var(--c-primary-black); + padding: 24px 0; + display: flex; + justify-content: space-between; + align-items: center; } -.footer .footer-bottom-section li a { - color: #999; +.prefooter .icon-arrow-right { + width: 28px; + height: 28px; + display: block; + padding-top: 2px; } -.footer ul li a:hover { - color: #999; - text-decoration: none; +/* Truck list menu */ +.footer-truck-list { + background-color: var(--c-secondary-graphite); } -.footer h1 ~ ul, -.footer h2 ~ ul, -.footer h3 ~ ul { +.footer-truck-list__title { + color: var(--c-primary-white); + margin: 0 auto; + padding: 24px 32px; + max-width: var(--wrapper-width); + text-transform: uppercase; display: flex; - flex-flow: row wrap; + align-items: center; + justify-content: space-between; + cursor: pointer; } -.footer h1 ~ ul li, -.footer h2 ~ ul li, -.footer h3 ~ ul li { - width: 50%; +.footer-truck-list__title, .footer-newsletter__title { + font-family: var(--ff-body); + font-size: 12px; + line-height: 16px; + letter-spacing: 1.92px; } -.footer hr { - border-bottom: 1px solid #111; - width: 100%; -} +.footer-truck-list__title .icon-dropdown-caret { + --color-icon: var(--c-primary-white); -.footer .footer-bottom-section { - display: flex; - align-items: end; - font-size: 12px; - justify-content: space-between; + transition: transform var(--duration-medium) cubic-bezier(0.45, 0, 0.125, 1); + pointer-events: none; } -.footer .footer-bottom-section ul { - max-width: 50%; +.footer-truck-list.expand .icon-dropdown-caret { + transform: rotate(180deg); } -.footer .footer-bottom-section li { - padding: 10px 0; +.footer-truck-list__title .icon-dropdown-caret svg { + width: 16px; + height: 16px; } -.footer .footer-bottom-section li a:hover { - color: var(--c-primary-white); +.footer-truck-list__items { + transition: 300ms max-height ease; + will-change: max-height; + overflow: hidden; + max-height: 0; } -.footer .footer-form-and-social-section { +.footer-truck-list__items a { display: flex; - flex-direction: column; + padding: 0 32px 16px; + color: var(--c-primary-white); + font-family: var(--ff-body); + font-size: var(--body-1-font-size); + line-height: var(--body-1-line-height); + letter-spacing: 0.16px; + justify-content: flex-start; } -.footer .footer-form-section p { - margin-top: 0; +/* Menu wrapper */ +.footer-menu { + display: grid; + gap: 40px 0; + grid-template-columns: 1fr; + grid-template-rows: auto; + padding: 40px 16px; + /* stylelint-disable-next-line declaration-block-no-redundant-longhand-properties */ + grid-template-areas: + 'logo' + 'menu' + 'newsletter' + 'social'; } -@media (min-width: 495px) { - .footer .footer-form-and-social-section { - width: 255px; - } +/* Logo */ +.footer-menu__logo { + grid-area: logo; } -@media (min-width: 1024px) { - .footer { - padding: 60px 48px 10px; - } - - .footer .footer-content { - display: flex; - flex-flow: row wrap; - } - - .footer .footer-main-content { - display: flex; - justify-content: space-between; - flex-direction: row; - align-items: flex-start; - width: 100%; - } - - .footer .footer-links-section { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 20px; - } +/* stylelint-disable-next-line no-descending-specificity */ +.footer-menu__logo svg { + width: 193px; + height: 19px; +} - .footer .footer-links-col-1 { - width: 185px; - } +.footer-menu__logo svg path[fill] { + fill: var(--c-primary-white); +} - .footer .footer-bottom-section li { - padding: 10px; - width: auto; - } +/* Menu */ +.footer-menu__columns { + grid-area: menu; + display: flex; + flex-wrap: wrap; + gap: 40px 16px; +} - .footer .footer-links-col-1 ul li { - width: 100%; - } +.footer-menu__column { + width: calc(50% - 20px); +} - .footer .footer-links-col-2 { - width: 370px; - margin-left: 20px; - } +/* stylelint-disable-next-line no-descending-specificity */ +.footer-menu__column a { + color: var(--c-secondary-silver); + font-family: var(--ff-body); + font-size: var(--body-2-font-size); + line-height: var(--body-2-line-height); +} - .footer .footer-bottom-section { - width: 100%; - } +/* stylelint-disable-next-line no-descending-specificity */ +.footer-menu__column a:hover { + text-decoration: underline; +} - .footer .footer-bottom-section ul { - width: 100%; - display: flex; - flex-wrap: nowrap; - max-width: unset; - align-items: flex-start; - justify-content: left; - } +/* Newsletter */ +.footer-newsletter { + grid-area: newsletter; +} - hr { - margin-top: 65px; - } +.footer-newsletter__title { + color: var(--c-primary-white); + margin-bottom: 12px; } /* eloqua footer form styles - START */ -.footer .elq-form .field-style { +.footer-newsletter .eloqua-form { margin: 0; } -.footer .elq-form .item-padding { - padding: 0; +.footer-newsletter .eloqua-form p { + font-family: var(--ff-body); } -.footer .eloqua-form .elq-form input[type="text"] { - background-color: #212121; - padding: 15px 10px; - border: none; - width: 100%; - font-family: var(--ff-body); - font-size: 16px; - line-height: 16px; - color: var(--c-primary-white); +.footer-newsletter .elq-form .field-style { + margin: 0; } -.footer .eloqua-form .elq-form input[type="text"]::placeholder { - color: var(--c-primary-white); +.footer-newsletter .elq-form .item-padding { + padding: 0; } -.footer .elq-form .checkbox-label { - font-size: 13px; - font-family: var(--ff-body); - bottom: 0; - padding-left: 0; +.footer-newsletter .eloqua-form .elq-form .email-and-submit-container { + display: block; + position: relative; + max-width: 320px; + margin-bottom: 5px; } -.footer .elq-form input.submit-button { - color: var(--c-secondary-dark-gold); - background: transparent; - border: solid 2px var(--c-secondary-dark-gold); - font-size: 20px !important; - line-height: 20px; - height: auto !important; - cursor: pointer; - margin: 0; - padding: 5px 10px; +.footer-newsletter .eloqua-form .elq-form .email-and-submit-container input[type='text'] { + background-color: var(--c-primary-white); + border: none; + color: var(--c-secondary-graphite); + font-family: var(--ff-body); + font-size: var(--body-2-font-size); + height: 48px; + line-height: var(--body-2-font-size); + padding: 14px 48px 14px 14px; } -.footer .elq-form input.submit-button:hover { - color: #af9866; - border-color: #af9866; +.footer-newsletter .elq-form input[type='text']::placeholder { + color: var(--c-secondary-graphite); } -.footer .eloqua-form p { - font-size: 11px; - font-family: var(--ff-body); +.footer-newsletter .elq-form .submit-button { + padding: 0; + position: absolute; + right: 0; + top: 0; + width: 48px !important; + height: 48px !important; + background: var(--button-secondary-gold-enabled) url('../../icons/arrow-right.svg') no-repeat center; } -.footer .eloqua-form .elq-form .email-and-submit-container { - display: flex; - align-items: stretch; - position: relative; +.footer-newsletter .elq-form .submit-button:hover { + background-color: var(--button-secondary-gold-hover); } -.footer .eloqua-form .elq-form .email-and-submit-container input[type="text"] { - padding-right: 40px; +.footer-newsletter .elq-form .submit-button:active { + background-color: var(--button-secondary-gold-pressed); } -.footer .elq-form .loader { +.footer-newsletter .elq-form .loader { position: absolute; right: 10px; bottom: 50%; @@ -254,37 +251,26 @@ background: #212121; } -.footer .eloqua-form .elq-form .email-and-submit-container input[type="submit"] { - position: absolute; - right: 0; - border: none; - background: transparent; - top: 50%; - transform: translateY(-50%); - padding: 5px 10px 5px 5px; +.footer-newsletter .elq-form .checkbox-label { + bottom: 0; + padding-left: 0; + font-family: var(--ff-body); + font-size: var(--body-2-font-size); + line-height: var(--body-2-font-size); } -.footer .checkbox-span { +.footer-newsletter .checkbox-span { position: relative; } -.footer .checkbox-span input[type="checkbox"][id] { - width: 20px; - height: 20px; - position: absolute; - top: 4px; - left: 0; - padding: 0; -} - -.footer .checkbox-span label[for] { +.footer-newsletter .checkbox-span label[for] { position: relative; margin-left: 0; white-space: break-spaces; cursor: pointer; } -.footer .checkbox-span label[for]::before { +.footer-newsletter .checkbox-span label[for]::before { content: ""; width: 20px; height: 20px; @@ -296,7 +282,16 @@ background: var(--c-primary-black); } -.footer .checkbox-span input:checked ~ label[for]::after { +.footer-newsletter .checkbox-span input[type='checkbox'][id] { + width: 20px; + height: 20px; + position: absolute; + top: 4px; + left: 0; + padding: 0; +} + +.footer-newsletter .checkbox-span input:checked~label[for]::after { content: "\f00c"; font-family: var(--ff-fontawesome); color: var(--c-secondary-dark-gold); @@ -307,34 +302,206 @@ line-height: 18px; } -.confirm-checkbox { +.footer-newsletter .confirm-checkbox { + display: none !important; user-select: none; cursor: pointer; - opacity: 0; - transition: opacity 1s; - visibility: hidden; } -.confirm-checkbox.show { - opacity: 1; - visibility: visible; +.footer-newsletter .confirm-checkbox.show { + display: block !important; } /* eloqua footer form styles - END */ +/* Social media */ +.footer-menu__socialmedia { + display: flex; + flex-wrap: wrap; + gap: 20px; + grid-area: social; +} + +/* stylelint-disable-next-line no-descending-specificity */ +.footer-menu__socialmedia a { + --color-icon: var(--c-tertiary-cool-gray); + + display: block; +} + +.footer-menu__socialmedia a:hover { + --color-icon: var(--c-primary-white); +} + +/* stylelint-disable-next-line no-descending-specificity */ +.footer-menu__socialmedia svg { + width: 24px; + height: 24px; +} + +/* Legal */ +.footer-legal ul { + border-top: 1px solid var(--c-tertiary-cool-gray); + margin: 0 16px; + padding: 40px 16px 40px 0; + display: flex; + justify-content: flex-start; + gap: 8px 24px; + flex-wrap: wrap; +} + +/* stylelint-disable-next-line no-descending-specificity */ +.footer-legal li { + font-size: 13px; +} + +/* stylelint-disable-next-line no-descending-specificity */ +.footer-legal a { + font-family: var(--ff-body); + font-style: normal; + font-weight: 400; + line-height: 160%; + color: var(--c-secondary-silver); +} + +/* Scroll to top */ -.block.footer .scroll-to-top { +.scroll-to-top { display: none; position: fixed; - bottom: 20px; + margin: 0; + bottom: 30px; right: 30px; z-index: 99; border: none; cursor: pointer; - border-radius: 10px; - padding: 12px 24px; + padding: 12px; + background-color: var(--c-primary-gold); + box-shadow: 0 8px 16px rgb(0 0 0 / 30%); +} + +.scroll-to-top:hover { + background-color: var(--button-secondary-gold-hover); } -.block.footer .scroll-to-top:hover { - background-color: #53565A; +.scroll-to-top:active { + background-color: var(--button-secondary-gold-pressed); +} + +/* stylelint-disable-next-line no-descending-specificity */ +.scroll-to-top svg { + transform: rotate(-90deg); + height: 24px; + width: 24px; +} + +@media (min-width: 1200px) { + /* Prefooter */ + .prefooter ul { + display: flex; + padding: 28px 0 24px; + } + + .prefooter li { + align-items: center; + border-right: 1px solid var(--c-primary-black); + display: flex; + flex: 1; + justify-content: center; + } + + .prefooter li:last-child { + border-right: 0; + } + + .prefooter li:not(:last-child) { + border-bottom: 0; + } + + .prefooter a { + font-size: 24px; + gap: 12px; + border-bottom: none; + justify-content: center; + letter-spacing: 0.48px; + padding: 15px 0; + } + + .prefooter li:first-child a { + margin-right: auto; + } + + .prefooter li:last-child a { + margin-left: auto; + } + + .prefooter .icon-arrow-right { + padding-top: 0; + } + + /* Truck list menu */ + .footer-truck-list { + padding: 0; + } + + .footer-truck-list__wrapper { + margin: auto; + display: flex; + align-items: baseline; + } + + .footer-truck-list__title { + font-family: var(--ff-subheadings-medium); + margin: 0 auto 0 0; + padding: 24px 32px 24px 0; + cursor: default; + } + + .footer-truck-list__title .icon-dropdown-caret { + display: none; + } + + .footer-truck-list__items { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 44px; + max-height: 100%; + overflow: auto; + } + + /* stylelint-disable-next-line no-descending-specificity */ + .footer-truck-list__items a { + padding: 0; + } + + .footer-menu { + padding: 96px 0 60px; + grid-template-columns: repeat(3, 1fr); + grid-template-rows: repeat(2, 1fr); + gap: 16px; + /* stylelint-disable-next-line declaration-block-no-redundant-longhand-properties */ + grid-template-areas: + 'logo menu newsletter' + 'social menu newsletter'; + } + + /* Logo */ + .footer-menu__logo { + margin-top: 10px; + } + + /* Menu */ + .footer-menu__columns { + flex-wrap: nowrap; + } + + .footer-menu__column { + width: 160px; + } + + /* Legal */ + .footer-legal ul { + margin: 0 auto; + } } \ No newline at end of file diff --git a/blocks/footer/footer.js b/blocks/footer/footer.js index dc7d2d6e7..1781eef95 100644 --- a/blocks/footer/footer.js +++ b/blocks/footer/footer.js @@ -1,16 +1,76 @@ import { readBlockConfig, decorateIcons, loadBlocks } from '../../scripts/lib-franklin.js'; -import { createElement, getTextLabel } from '../../scripts/scripts.js'; +import { createElement, getTextLabel } from '../../scripts/common.js'; const PLACEHOLDERS = { - visit: getTextLabel('visit aria label'), - social: getTextLabel('social aria label'), - channel: getTextLabel('channel aria label'), + subscribe: getTextLabel('SUBSCRIBE TO BULLDOG'), }; -/** - * loads and decorates the footer - * @param {Element} block The header block element - */ +const addClassToTitle = (block, className) => { + const headings = block.querySelectorAll('h1, h2, h3, h4, h5, h6'); + [...headings].forEach((h) => h.classList.add(className)); +}; + +const blockName = 'footer'; +const blockNamePrefooter = 'prefooter'; +const blockNameTruckList = 'footer-truck-list'; +const blockNameMenu = 'footer-menu'; +const blockNameNewsletter = 'footer-newsletter'; +const blockNameLegal = 'footer-legal'; + +function displayScrollToTop(buttonEl) { + const scrollTop = document.body.scrollTop || document.documentElement.scrollTop; + buttonEl.style.display = scrollTop > 160 ? 'block' : 'none'; +} + +function goToTopFunction() { + const scrollTop = document.body.scrollTop || document.documentElement.scrollTop; + let timeOut; + + if (scrollTop !== 0) { + window.scrollBy(0, -50); + timeOut = setTimeout(goToTopFunction, 10); + return; + } + + clearTimeout(timeOut); +} + +function addScrollToTopButton(mainEl) { + const scrollToTopButton = createElement('button', { + classes: 'scroll-to-top', + props: { + title: 'Go to the top of the page', + }, + }); + const svgIcon = document.createRange().createContextualFragment(` + `); + scrollToTopButton.append(...svgIcon.children); + + scrollToTopButton.addEventListener('click', goToTopFunction); + window.addEventListener('scroll', () => displayScrollToTop(scrollToTopButton)); + mainEl.append(scrollToTopButton); +} + +function findList(ele) { + if (ele.classList.contains(blockNameTruckList)) { + return ele; + } + return findList(ele.parentElement); +} + +function toggleExpand(targetH3) { + const clickedColumn = findList(targetH3); + const isExpanded = clickedColumn.classList.contains('expand'); + const wrapper = targetH3.closest(`.${blockNameTruckList}`); + const content = wrapper.querySelector(`.${blockNameTruckList}__items`); + if (wrapper === clickedColumn && !isExpanded) { + wrapper.classList.add('expand'); + content.style.maxHeight = `${content.scrollHeight}px`; + } else { + wrapper.classList.remove('expand'); + content.style.maxHeight = null; + } +} export default async function decorate(block) { const cfg = readBlockConfig(block); @@ -19,106 +79,111 @@ export default async function decorate(block) { const footerPath = cfg.footer || '/footer'; const resp = await fetch(`${footerPath}.plain.html`); const html = await resp.text(); - const footer = createElement('footer', 'footer-content'); - footer.innerHTML = html; + block.innerHTML = html; - // adding the 'block' class and 'data-block-name' - // needed to load blocks - const eloquaFormEl = footer.querySelector('.eloqua-form'); - eloquaFormEl?.classList.add('block'); - eloquaFormEl?.setAttribute('data-block-name', 'eloqua-form'); + // Eloqua form necessary variables + let observer = null; + let submitButtonFixed = false; + let checkboxFixed = false; - // transforming icons into font-awesome - footer.querySelectorAll('span.icon').forEach((icon) => { - const iconClass = icon.getAttribute('class').split(' ').find((el) => el.startsWith('icon-')); + const footerItems = block.querySelectorAll(`:scope > div .${blockName} > div`); - if (iconClass) { - const iconName = iconClass.split('icon-')[1]; - const link = icon.closest('a'); - const social = iconName.replace('fa-', ''); + const prefooter = footerItems?.[0].querySelector(':scope > div'); + const truckList = footerItems?.[1].querySelector(':scope > div'); + const footerMenu = footerItems?.[2]; + const footerLegal = footerItems?.[3].querySelector(':scope > div'); - link.ariaLabel = `${PLACEHOLDERS.visit.replace('$0', social)} ${ - social !== 'youtube' ? PLACEHOLDERS.social : PLACEHOLDERS.channel}`; + const newFooter = createElement('div'); - icon.classList.remove('icon', iconClass); - icon.classList.add('fa', iconName, 'footer-social-media'); - } - }); + // Prefooter + if (prefooter) { + prefooter.classList.add(blockNamePrefooter); + newFooter.append(prefooter); + } - const socialMediaSection = footer.querySelector('.fa-twitter, .fa-facebook, .fa-twitter, .fa-linkedin, .fa-instagram, .fa-youtube')?.closest('ul'); - socialMediaSection?.classList.add('footer-social-media-section'); + // Truck list + if (truckList) { + truckList.classList.add(`${blockNameTruckList}__wrapper`); + truckList.querySelector('ul')?.classList.add(`${blockNameTruckList}__items`); + addClassToTitle(truckList, `${blockNameTruckList}__title`); + const truckListContent = createElement('div', { classes: blockNameTruckList }); + truckListContent.appendChild(truckList); - const [firstHeader, secondHeader] = [...footer.querySelectorAll('h3')]; - const [firstLinks, secondLinks] = [...footer.querySelectorAll('h3 ~ ul')]; + newFooter.append(truckListContent); + } - // creating the logo link - const picture = footer.querySelector('picture'); - let logoLink = null; + // Menu: social media + logo + menu list + newsletter form + if (footerMenu) { + const newMenu = createElement('div', { classes: blockNameMenu }); + + // Logo + const logo = createElement('div'); + const logoLink = createElement('a', { props: { href: 'https://www.macktrucks.com/' } }); + const svgLogo = document.createRange().createContextualFragment(` + + + + + + + + + + + + + + + + `); + logo.appendChild(logoLink); + logoLink.appendChild(svgLogo); + logo.classList.add(`${blockNameMenu}__logo`); + newMenu.appendChild(logo); + + // Social media + const socialMedia = footerMenu.querySelector(':scope > div ul'); + socialMedia.classList.add(`${blockNameMenu}__socialmedia`); + const socialLinks = socialMedia.querySelectorAll('a'); + [...socialLinks].forEach((a) => { a.target = '_blank'; }); + newMenu.appendChild(socialMedia); + + // remove div which contained logo and social media + footerMenu.firstElementChild.remove(); + + // Menu Columns: Newsletter form + const newsletter = createElement('div', { classes: blockNameNewsletter }); + const oldNews = footerMenu.querySelector(':scope > div:last-child'); + newsletter.appendChild(oldNews); + + const eloquaForm = block.querySelector('.eloqua-form'); + eloquaForm?.setAttribute('data-block-name', 'eloqua-form'); + newsletter.append(eloquaForm); + addClassToTitle(newsletter, `${blockNameNewsletter}__title`); + + // Menu Columns: menu + const menu = createElement('div', { classes: `${blockNameMenu}__columns` }); + menu.innerHTML = footerMenu.innerHTML; + const menuList = menu.querySelectorAll(':scope > div'); + menuList.forEach((item) => item.classList.add(`${blockNameMenu}__column`)); + + newMenu.appendChild(menu); + newMenu.appendChild(newsletter); + newFooter.append(newMenu); + } - if (picture) { - logoLink = picture.closest('div').querySelector('a'); - logoLink?.classList.add('footer-logo-link'); - picture.parentElement.remove(); + if (footerLegal) { + footerLegal.classList.add(blockNameLegal); - logoLink.innerHTML = ''; - logoLink.append(picture); + newFooter.append(footerLegal); } - const bottomLinksList = [...footer.querySelectorAll('ul')].at(-1); - - const formSection = footer.querySelector('.eloqua-form')?.parentElement; - formSection?.classList.add('footer-form-section'); - - const footerTemplate = ` - - `; - - const fragment = document.createRange().createContextualFragment(footerTemplate); - block.appendChild(fragment); - - // make links to open in another browser tab/window - const socialLinks = block.querySelectorAll('.footer-social-media-section a'); - [...socialLinks].forEach((a) => { a.target = '_blank'; }); + block.innerHTML = newFooter.innerHTML; await decorateIcons(block); await loadBlocks(block); - const targetNode = block.querySelector('.eloqua-form.block'); - const observerOptions = { - childList: true, - attributes: false, - subtree: true, - }; - let observer = null; - let submitButtonFixed = false; - let checkboxFixed = false; const onFormLoaded = (mutationList) => { // eslint-disable-next-line no-restricted-syntax for (const mutation of mutationList) { @@ -131,13 +196,14 @@ export default async function decorate(block) { const submitButton = block.querySelector('input[type="submit"]'); const emailInput = block.querySelector('input[name="emailAddress"]'); const label = emailInput.parentElement.querySelector('label'); - const emailAndSubmitContainer = createElement('span', ['email-and-submit-container']); + const emailAndSubmitContainer = createElement('span', { classes: ['email-and-submit-container'] }); // change the submit button to arrow button // and display it sticked to the right side of email input if (submitButton && emailInput) { const parent = emailInput.parentElement; - submitButton.value = '→'; + submitButton.value = ''; + submitButton.ariaLabel = `${PLACEHOLDERS.subscribe}`; emailAndSubmitContainer.append(emailInput, submitButton); parent.append(emailAndSubmitContainer); @@ -170,36 +236,19 @@ export default async function decorate(block) { } }; - function displayScrollToTop(buttonEl) { - const scrollTop = document.body.scrollTop || document.documentElement.scrollTop; - buttonEl.style.display = scrollTop > 160 ? 'block' : 'none'; - } - - function goToTopFunction() { - const scrollTop = document.body.scrollTop || document.documentElement.scrollTop; - let timeOut; + const eloquaForm = block.querySelector('.eloqua-form'); + observer = new MutationObserver(onFormLoaded); + observer.observe(eloquaForm, { + childList: true, + attributes: false, + subtree: true, + }); - if (scrollTop !== 0) { - window.scrollBy(0, -50); - timeOut = setTimeout(goToTopFunction, 10); - return; + block.addEventListener('click', (e) => { + if (e.target.classList.contains(`${blockNameTruckList}__title`)) { + toggleExpand(e.target); } - - clearTimeout(timeOut); - } - - function addScrollToTopButton(mainEl) { - const scrollToTopButton = createElement('button', ['scroll-to-top', 'button'], { title: 'Go to the top of the page' }); - const icon = createElement('i', ['fa', 'fa-angle-up']); - scrollToTopButton.appendChild(icon); - - scrollToTopButton.addEventListener('click', goToTopFunction); - window.addEventListener('scroll', () => displayScrollToTop(scrollToTopButton)); - mainEl.append(scrollToTopButton); - } - - observer = new MutationObserver(onFormLoaded); - observer.observe(targetNode, observerOptions); + }); addScrollToTopButton(block); } diff --git a/blocks/header/header.js b/blocks/header/header.js index a33fe4cdb..73e53e0b9 100644 --- a/blocks/header/header.js +++ b/blocks/header/header.js @@ -1,6 +1,7 @@ import { readBlockConfig, decorateIcons } from '../../scripts/lib-franklin.js'; -import { createElement, debounce } from '../../scripts/scripts.js'; +import { debounce } from '../../scripts/scripts.js'; import { fetchAutosuggest, handleArrowDown, handleArrowUp } from '../search/autosuggest.js'; +import { createElement } from '../../scripts/common.js'; // media query match that indicates mobile/tablet width const MQ = window.matchMedia('(min-width: 1140px)'); @@ -64,7 +65,7 @@ function addLabelsToIcons(navTools) { if (!pictures) return; const picturesWrappers = [...pictures].map((pic) => pic.parentElement); picturesWrappers.forEach((p, i) => { - const span = createElement('span', 'nav-label'); + const span = createElement('span', { classes: 'nav-label' }); const br = createElement('br'); span.textContent = links[i].textContent; links[i].textContent = ''; @@ -148,7 +149,7 @@ export default async function decorate(block) { const html = await resp.text(); // decorate nav DOM - const nav = createElement('nav', '', { id: 'nav' }); + const nav = createElement('nav', { props: { id: 'nav' } }); nav.innerHTML = html; const classes = ['brand', 'sections', 'mobile', 'tools', 'search']; @@ -172,7 +173,7 @@ export default async function decorate(block) { }); // hamburger for mobile - const hamburger = createElement('div', 'nav-hamburger'); + const hamburger = createElement('div', { classes: 'nav-hamburger' }); hamburger.innerHTML = ``; @@ -202,14 +203,14 @@ export default async function decorate(block) { searchIcon.classList.add('fa', 'fa-search'); const searchIconWrapper = searchIcon.parentElement; searchIconWrapper.classList.add('search-icon-wrapper'); - const navSearchWrapper = createElement('div', 'nav-search-wrapper'); - const searchIconLink = createElement('a', '', { href: navSearchLinkHref }); - const searchWrapper = createElement('div', 'search-wrapper'); - const input = createElement('input', '', { type: 'search', placeholder: 'Search Mack Trucks' }); - const autosuggestWrapper = createElement('div', 'autosuggest-results'); - const closeBtnWrapper = createElement('div', 'search-close'); - const closeBtn = createElement('button', '', { type: 'button' }); - const closeBtnIcon = createElement('span', 'search-close-icon'); + const navSearchWrapper = createElement('div', { classes: 'nav-search-wrapper' }); + const searchIconLink = createElement('a', { props: { href: navSearchLinkHref } }); + const searchWrapper = createElement('div', { classes: 'search-wrapper' }); + const input = createElement('input', { props: { type: 'search', placeholder: 'Search Mack Trucks' } }); + const autosuggestWrapper = createElement('div', { classes: 'autosuggest-results' }); + const closeBtnWrapper = createElement('div', { classes: 'search-close' }); + const closeBtn = createElement('button', { props: { type: 'button' } }); + const closeBtnIcon = createElement('span', { classes: 'search-close-icon' }); let isShown = false; searchWrapper.appendChild(input); diff --git a/blocks/hero/hero.js b/blocks/hero/hero.js index 09920cb46..5fd283a27 100644 --- a/blocks/hero/hero.js +++ b/blocks/hero/hero.js @@ -1,11 +1,14 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; const decorateVideo = (link) => { const { parentElement } = link; - const video = createElement('video', ['hero-video', 'hide'], { - loop: 'loop', + const video = createElement('video', { + classes: ['hero-video', 'hide'], + props: { + loop: 'loop', + }, }); - const source = createElement('source', '', { src: link.href, type: 'video/mp4' }); + const source = createElement('source', { props: { src: link.href, type: 'video/mp4' } }); video.appendChild(source); parentElement.appendChild(video); link.remove(); @@ -27,8 +30,8 @@ export default function decorate(block) { const video = block.querySelector('a[href*=".mp4"]'); const videoWrapper = video && video.closest('p'); const videoLink = videoWrapper?.firstElementChild; - const contentContainer = createElement('div', 'hero-content-container'); - const mediaWrapper = createElement('div', 'hero-content-media'); + const contentContainer = createElement('div', { classes: 'hero-content-container' }); + const mediaWrapper = createElement('div', { classes: 'hero-content-media' }); // transform link into a video tag if (videoLink) decorateVideo(videoLink); diff --git a/blocks/hotspots/hotspots.js b/blocks/hotspots/hotspots.js index d688be3f2..869239342 100644 --- a/blocks/hotspots/hotspots.js +++ b/blocks/hotspots/hotspots.js @@ -1,5 +1,5 @@ import { decorateIcons } from '../../scripts/lib-franklin.js'; -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; /** * @typedef {Object} HotspotContent @@ -111,14 +111,19 @@ function switchToOtherHotspot(block, index) { } function addDesktopHotspotIcon(hotspot, block, main) { - const iconLink = createElement('a', 'hotspot-icon', { href: '#' }); + const iconLink = createElement('a', { + classes: 'hotspot-icon', + props: { href: '#' }, + }); iconLink.dataset.spot = hotspot.id; iconLink.style.left = hotspot.positionLeft; iconLink.style.top = hotspot.positionTop; - const image = createElement('img', null, { - src: '/icons/hotspot.png', - alt: hotspot.title.textContent, + const image = createElement('img', { + props: { + src: '/icons/hotspot.png', + alt: hotspot.title.textContent, + }, }); iconLink.appendChild(image); iconLink.onclick = (event) => handleClickHotspot( @@ -238,7 +243,7 @@ export function addSpot(block, hotspot) { * @param description {HTMLElement} */ function decorateImageAndTitle(mainDiv, firstPicture, title, description) { - const hotspot = createElement('div', ['hotspot', 'animated']); + const hotspot = createElement('div', { classes: ['hotspot', 'animated'] }); hotspot.innerHTML = ` @@ -262,21 +267,23 @@ function addMobileTabNavigation(block, title, hotspotAreaId) { // only one tab navigation is needed for the entire page let mobileNav = document.querySelector('main > div#hotspots-mobile-nav'); if (!mobileNav) { - mobileNav = createElement('div', null, { id: 'hotspots-mobile-nav' }); + mobileNav = createElement('div', { + props: { id: 'hotspots-mobile-nav' }, + }); - const ul = createElement('ul', 'featurenav-list'); + const ul = createElement('ul', { classes: 'featurenav-list' }); mobileNav.append(ul); document.querySelector('main').append(mobileNav); } - const li = createElement('li', 'featurenav-list-item'); + const li = createElement('li', { classes: 'featurenav-list-item' }); li.dataset.hotspotTargetId = hotspotAreaId; if (mobileNav.firstElementChild.childElementCount === 0) { li.classList.add('active'); } - const link = createElement('a', null, { href: `#${title.textContent}` }); + const link = createElement('a', { props: { href: `#${title.textContent}` } }); link.textContent = title.innerHTML; li.append(link); mobileNav.firstElementChild.append(li); diff --git a/blocks/magazine-subscribe/magazine-subscribe.js b/blocks/magazine-subscribe/magazine-subscribe.js index 7291373f2..0cf26ad5d 100644 --- a/blocks/magazine-subscribe/magazine-subscribe.js +++ b/blocks/magazine-subscribe/magazine-subscribe.js @@ -1,23 +1,26 @@ import { createElement, getTextLabel, -} from '../../scripts/scripts.js'; +} from '../../scripts/common.js'; export default async function decorate(block) { const children = block.querySelectorAll('p'); const subscribeText = getTextLabel('SUBSCRIBE TO BULLDOG'); const [picture, title, subtitle, text] = children; - const generalSection = createElement('div', 'magazine-subscribe-section'); - const imageSection = createElement('div', 'magazine-subscribe-image'); - const contentSection = createElement('div', 'magazine-subscribe-content'); + const generalSection = createElement('div', { classes: 'magazine-subscribe-section' }); + const imageSection = createElement('div', { classes: 'magazine-subscribe-image' }); + const contentSection = createElement('div', { classes: 'magazine-subscribe-content' }); title.classList.add('magazine-subscribe-title'); subtitle.classList.add('magazine-subscribe-subtitle'); text.classList.add('magazine-subscribe-text'); - const buttonContainer = createElement('div', ['magazine-subscribe-button', 'button-container'], { type: 'button' }); + const buttonContainer = createElement('div', { + classes: ['magazine-subscribe-button', 'button-container'], + props: { type: 'button' }, + }); - const button = createElement('button', 'subscribe-button'); + const button = createElement('button', { classes: 'subscribe-button' }); button.textContent = subscribeText; buttonContainer.append(button); diff --git a/blocks/news-list/news-list.js b/blocks/news-list/news-list.js index 866bf862d..fc6b0b5a7 100644 --- a/blocks/news-list/news-list.js +++ b/blocks/news-list/news-list.js @@ -1,4 +1,4 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; import { feedsInfo, getBodyBuilderNews, getMackNews, PagingInfo, } from '../../scripts/news.js'; @@ -15,7 +15,10 @@ export default async function decorate(block) { block.textContent = ''; // eslint-disable-next-line function-call-argument-newline,function-paren-newline - const rssLink = createElement('a', ['title-with-icon'], { href: feedsInfo[type].feedPath, target: '_blank' }); + const rssLink = createElement('a', { + classes: ['title-with-icon'], + props: { href: feedsInfo[type].feedPath, target: '_blank' }, + }); rssLink.textContent = 'News RSS'; block.append(rssLink); @@ -24,9 +27,9 @@ export default async function decorate(block) { ? await getBodyBuilderNews(pagingInfo) : await getMackNews(window.location.pathname, pagingInfo, filter); - const list = createElement('ul', ['news-sidebar-list']); + const list = createElement('ul', { classes: ['news-sidebar-list'] }); list.append(...posts.map((post) => { - const articleLink = createElement('a', [], { href: post.path }); + const articleLink = createElement('a', { props: { href: post.path } }); const heading = createElement('h3'); heading.textContent = (post.title && post.title !== '0') ? post.title : ''; diff --git a/blocks/news-sidebar/news-sidebar.js b/blocks/news-sidebar/news-sidebar.js index 0e95fa34c..a9cfc5507 100644 --- a/blocks/news-sidebar/news-sidebar.js +++ b/blocks/news-sidebar/news-sidebar.js @@ -1,4 +1,4 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; import { feedsInfo, getBodyBuilderNews, getMackNews, PagingInfo, @@ -15,10 +15,13 @@ export default async function decorate(block) { : await getMackNews(window.location.pathname, pagingInfo, ''); block.textContent = ''; - const list = createElement('ul', ['news-sidebar-list']); + const list = createElement('ul', { classes: ['news-sidebar-list'] }); block.append(list); - const rssLink = createElement('a', ['news-sidebar-rss-icon'], { href: feedsInfo[type].feedPath }); + const rssLink = createElement('a', { + classes: ['news-sidebar-rss-icon'], + props: { href: feedsInfo[type].feedPath }, + }); rssLink.textContent = 'News RSS'; list.appendChild(rssLink); @@ -27,7 +30,7 @@ export default async function decorate(block) { newsPage.forEach((newsData) => { const li = createElement('li'); - const newsItem = createElement('a', [], { href: newsData.path }); + const newsItem = createElement('a', { props: { href: newsData.path } }); if (window.location.pathname === newsData.path) { newsItem.classList.add('new-sidebar-active-link'); } @@ -53,11 +56,11 @@ export default async function decorate(block) { `; - const selectContainer = createElement('div', 'news-sidebar-select-container'); + const selectContainer = createElement('div', { classes: 'news-sidebar-select-container' }); const select = div.querySelector('select'); newsPage.forEach((item) => { - const option = createElement('option', [], { value: item.path }); + const option = createElement('option', { props: { value: item.path } }); // eslint-disable-next-line prefer-destructuring option.textContent = item.title.split('|')[0]; select.append(option); @@ -85,7 +88,11 @@ function getParentCategoryLink(pagePath) { // format the parent folder name to be more readable parentFolderName = parentFolderName?.replaceAll('-', ' '); - const category = createElement('a', [], { href: `/${parentFolders.join('/')}/` }); + const category = createElement('a', { + props: { + href: `/${parentFolders.join('/')}/`, + }, + }); category.textContent = parentFolderName; return category; } diff --git a/blocks/recent-articles/recent-articles.js b/blocks/recent-articles/recent-articles.js index 3de743849..0c2f3913e 100644 --- a/blocks/recent-articles/recent-articles.js +++ b/blocks/recent-articles/recent-articles.js @@ -1,4 +1,4 @@ -import { createElement, getTextLabel } from '../../scripts/scripts.js'; +import { createElement, getTextLabel } from '../../scripts/common.js'; import { createOptimizedPicture, getOrigin } from '../../scripts/lib-franklin.js'; const sectionTitle = getTextLabel('Recent article text'); @@ -39,19 +39,19 @@ export default async function decorate(block) { const filteredArticles = clearRepeatedArticles(sortedArticles); const selectedArticles = filteredArticles.slice(0, limit); - const recentArticlesSection = createElement('div', 'recent-articles-section'); - const recentArticlesTitle = createElement('h3', 'recent-articles-section-title'); + const recentArticlesSection = createElement('div', { classes: 'recent-articles-section' }); + const recentArticlesTitle = createElement('h3', { classes: 'recent-articles-section-title' }); recentArticlesTitle.innerText = sectionTitle; - const recentArticleList = createElement('ul', 'recent-articles-list'); + const recentArticleList = createElement('ul', { classes: 'recent-articles-list' }); selectedArticles.forEach((e, idx) => { const linkUrl = new URL(e.path, getOrigin()); const firstOrRest = (idx === 0) ? 'first' : 'rest'; - const item = createElement('li', `recent-articles-${firstOrRest}-item`); + const item = createElement('li', { classes: `recent-articles-${firstOrRest}-item` }); - const image = createElement('div', `recent-articles-${firstOrRest}-image`); + const image = createElement('div', { classes: `recent-articles-${firstOrRest}-image` }); const picture = createOptimizedPicture(e.image, e.title); const pictureTag = picture.outerHTML; image.innerHTML = ` @@ -60,16 +60,25 @@ export default async function decorate(block) { const categoriesWithDash = e.category.replaceAll(' ', '-').toLowerCase(); const categoryUrl = new URL(`magazine/categories/${categoriesWithDash}`, getOrigin()); - const category = createElement('a', `recent-articles-${firstOrRest}-category`, { href: categoryUrl }); + const category = createElement('a', { + classes: `recent-articles-${firstOrRest}-category`, + props: { href: categoryUrl }, + }); category.innerText = e.category; - const title = createElement('a', `recent-articles-${firstOrRest}-title`, { href: linkUrl }); + const title = createElement('a', { + classes: `recent-articles-${firstOrRest}-title`, + props: { href: linkUrl }, + }); title.innerText = e.title; - const subtitle = createElement('p', `recent-articles-${firstOrRest}-subtitle`); + const subtitle = createElement('p', { classes: `recent-articles-${firstOrRest}-subtitle` }); subtitle.innerText = e.subtitle; - const link = createElement('a', `recent-articles-${firstOrRest}-link`, { href: linkUrl }); + const link = createElement('a', { + classes: `recent-articles-${firstOrRest}-link`, + props: { href: linkUrl }, + }); link.innerText = readNowText; if (idx === 0) { diff --git a/blocks/recommendations/recommendations.js b/blocks/recommendations/recommendations.js index 44f290086..dd71e1b0b 100644 --- a/blocks/recommendations/recommendations.js +++ b/blocks/recommendations/recommendations.js @@ -1,4 +1,4 @@ -import { createElement, getTextLabel } from '../../scripts/scripts.js'; +import { createElement, getTextLabel } from '../../scripts/common.js'; import { getAllArticles, getLimit, clearRepeatedArticles } from '../recent-articles/recent-articles.js'; import { getMetadata, @@ -18,17 +18,17 @@ export default async function decorate(block) { const filteredArticles = clearRepeatedArticles(recommendedArticles); const selectedArticles = filteredArticles.slice(0, limit); - const recommendationsSection = createElement('div', 'recommendations-section'); - const recommendationsTitle = createElement('h3', 'recommendations-section-title'); + const recommendationsSection = createElement('div', { classes: 'recommendations-section' }); + const recommendationsTitle = createElement('h3', { classes: 'recommendations-section-title' }); recommendationsTitle.innerText = recommendationsText; - const recommendationsList = createElement('ul', 'recommendations-list'); + const recommendationsList = createElement('ul', { classes: 'recommendations-list' }); selectedArticles.forEach((e, idx) => { - const item = createElement('li', ['recommendations-item', `recommendations-item-${idx}`]); + const item = createElement('li', { classes: ['recommendations-item', `recommendations-item-${idx}`] }); const linkUrl = new URL(e.path, getOrigin()); - const image = createElement('div', 'recommendations-image'); + const image = createElement('div', { classes: 'recommendations-image' }); const picture = createOptimizedPicture(e.image, e.title); const pictureTag = picture.outerHTML; image.innerHTML = ` @@ -38,25 +38,40 @@ export default async function decorate(block) { const categoriesWithDash = e.category.replaceAll(' ', '-').toLowerCase(); const categoryUrl = new URL(`magazine/categories/${categoriesWithDash}`, getOrigin()); - const content = createElement('div', 'recommendations-text-content'); - const categoryLink = createElement('a', 'recommendations-category', { href: categoryUrl }); + const content = createElement('div', { classes: 'recommendations-text-content' }); + const categoryLink = createElement('a', { + classes: 'recommendations-category', + props: { href: categoryUrl }, + }); categoryLink.innerText = e.category; - const title = createElement('a', 'recommendations-title', { href: linkUrl }); + const title = createElement('a', { + classes: 'recommendations-title', + props: { href: linkUrl }, + }); title.innerText = e.title; - const truck = createElement('div', 'recommendations-truck'); - const truckText = createElement('p', 'recommendations-truck-text'); + const truck = createElement('div', { classes: 'recommendations-truck' }); + const truckText = createElement('p', { classes: 'recommendations-truck-text' }); truckText.innerText = e.truck; - const truckIcon = createElement('img', 'truck-icon', { src: '/icons/Truck_Key_icon.svg', alt: 'truck icon' }); + const truckIcon = createElement('img', { + classes: 'truck-icon', + props: { src: '/icons/Truck_Key_icon.svg', alt: 'truck icon' }, + }); if (e.truck.length !== 0) truck.append(truckIcon, truckText); - const link = createElement('a', 'recommendations-link', { href: linkUrl }); + const link = createElement('a', { + classes: 'recommendations-link', + props: { href: linkUrl }, + }); link.innerText = readNowText; content.append(categoryLink, title); - const subtitle = createElement('a', 'recommendations-subtitle', { href: linkUrl }); + const subtitle = createElement('a', { + classes: 'recommendations-subtitle', + props: { href: linkUrl }, + }); subtitle.innerText = e.subtitle; if (e.subtitle.length !== 0) { content.append(subtitle); diff --git a/blocks/sds/sds.js b/blocks/sds/sds.js index 406bddc4f..f52d70183 100644 --- a/blocks/sds/sds.js +++ b/blocks/sds/sds.js @@ -1,4 +1,4 @@ -import { createElement } from '../../scripts/scripts.js'; +import { createElement } from '../../scripts/common.js'; function newId() { return (Math.random() + 1).toString(36).substring(7); @@ -32,7 +32,7 @@ function normalizeCells(cells, rowheaderRole = 'rowheader', cellRole = 'cell') { if (cell.children.length === 0 && !cell.querySelector('p') && cell.textContent !== '') { const text = cell.innerHTML; const fragment = document.createRange().createContextualFragment(text); - const pWrapper = createElement('p', ''); + const pWrapper = createElement('p'); cell.textContent = ''; pWrapper.appendChild(fragment); cell.appendChild(pWrapper); @@ -68,7 +68,7 @@ export default function decorate(block) { if (header.children.length > 1) { header.className = 'column-header'; normalizeCells(header.children, 'rowheader', 'columnheader'); - const mobileColumnHeader = createElement('div', 'column-header-mobile'); + const mobileColumnHeader = createElement('div', { classes: 'column-header-mobile' }); const select = `