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 = `