Skip to content

Commit

Permalink
Copy code from Mack Trucks
Browse files Browse the repository at this point in the history
  • Loading branch information
cogniSyb committed Jul 12, 2024
1 parent b2f261a commit 62fc86a
Show file tree
Hide file tree
Showing 8 changed files with 474 additions and 2 deletions.
135 changes: 135 additions & 0 deletions common/snackbar/snackbar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
@keyframes slide-from-start {
from {
transform: translateY(-100%);
}

to {
transform: translateY(0);
}
}

@keyframes slide-from-end {
from {
transform: translateY(100%);
}

to {
transform: translateY(0);
}
}

.snackbar-container {
position: fixed;
inset: 0;
padding: 64px 16px 16px;
display: flex;
flex-flow: column wrap;
place-content: var(--snackbar-position, center flex-end);
gap: 16px;
z-index: 1000;
pointer-events: none;
overflow: hidden;
}

.snackbar {
--color-icon: var(--c-primary-white);

width: 100%;
max-width: 400px;
background-color: var(--c-primary-black);
color: var(--c-primary-white);
padding: 12px 16px;
gap: 10px;
transition: transform var(--duration-small) var(--easing-entrance);
animation:
fade-in var(--duration-medium) var(--duration-small) var(--easing-entrance) forwards,
var(--snackbar-animation) var(--duration-large) var(--easing-entrance);
border-radius: 4px;
box-shadow: rgba(0 0 0 / 20%) 0 0.2px 2px, rgba(0 0 0 / 20%) 0 2px 4px;
display: flex;
opacity: 0;
pointer-events: auto;
will-change: transform;
line-height: 20px;
}

.snackbar--hide {
animation: fade-out var(--duration-medium) 0s var(--easing-entrance) forwards;
}

.snackbar--error {
background-color: var(--c-error);
}

.snackbar--success {
background-color: var(--c-success);
}

.snackbar p {
margin: 0;
align-self: center;
}

.snackbar .icon svg {
width: 24px;
height: 24px;
display: block;
}

.snackbar .button-container {
margin: -4px -8px -4px auto;
}

.snackbar__close-button {
padding: 0;
margin: -10px -16px -10px auto;
width: 44px;
height: 44px;
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
border: 0;
background: transparent;
cursor: pointer;
}

.snackbar__close-button:focus {
outline: 0;
}

.snackbar__close-button:focus-visible {
outline: 2px solid var(--border-focus);
outline-offset: -4px;
}

.snackbar__close-button .icon svg {
width: 32px;
height: 32px;
}

.snackbar--buttons-below {
flex-wrap: wrap;
}

.snackbar--buttons-below .icon + p {
width: calc(100% - 34px);
}

.snackbar--buttons-below .button-container {
width: 100%;
display: flex;
justify-content: end;
gap: 8px;
}

@media (prefers-reduced-motion) {
.snackbar,
.snackbar--hide {
animation: none;
}

.snackbar {
opacity: 1;
}
}
144 changes: 144 additions & 0 deletions common/snackbar/snackbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// eslint-disable-next-line import/no-cycle
import {
createElement,
decorateIcons,
getTextLabel,
} from '../../scripts/common.js';

const componentName = 'snackbar';

/**
* Initializes the snackbar container with the specified position.
*
* @param {('left'|'center'|'right')} [positionX='center'] - The horizontal position of the snackbar
* @param {('top'|'bottom')} [positionY='bottom'] - The vertical position of the snackbar
* @returns {HTMLElement} The created snackbar container element
*/
const initSnackbarContainer = (positionX, positionY) => {
const positionMapping = {
left: 'start',
center: 'center',
right: 'end',
top: 'start',
bottom: 'end',
};

const x = positionMapping[positionX] || 'center';
const y = positionMapping[positionY] || 'end';

const container = createElement('section', {
classes: [`${componentName}-container`],
props: { style: `--snackbar-position: ${x} ${y}; --snackbar-animation: slide-from-${y};` },
});
document.body.appendChild(container);
return container;
};

const handleCloseButtonClick = (event) => {
const snackbar = event.target.closest(`.${componentName}`);
if (snackbar) {
// eslint-disable-next-line no-use-before-define
removeSnackbar(snackbar);
}
};

export const removeSnackbar = (snackbar) => {
const container = snackbar.parentNode;
const closeButton = snackbar.querySelector(`.${componentName}__close-button`);
snackbar.classList.add(`${componentName}--hide`);

const animationEndHandler = () => {
container.removeChild(snackbar);
snackbar.removeEventListener('animationend', animationEndHandler);
};

const style = window.getComputedStyle(snackbar);
const animationDuration = parseFloat(style.animationDuration);

if (animationDuration > 0) {
snackbar.addEventListener('animationend', animationEndHandler);
} else {
container.removeChild(snackbar);
}

if (closeButton) {
closeButton.removeEventListener('click', handleCloseButtonClick);
}
};

const createSnackbar = (text, type, buttonsBelow, closeButton) => {
const getIcon = () => {
switch (type) {
case 'error':
return 'icon-remove';
case 'success':
return 'icon-checkmark-circle';
default:
return '';
}
};

const icon = getIcon();

const snackbar = document.createRange().createContextualFragment(`
<output role="status" class="${componentName} ${type ? `${componentName}--${type}` : ''} ${buttonsBelow ? `${componentName}--buttons-below` : ''}">
${type && icon ? `<span class="icon ${icon}"></span>` : ''}
<p>${text}</p>
${closeButton ? `<button
aria-label="${getTextLabel('Dismiss message')}"
class="${componentName}__close-button"
aria-controls="${componentName}"
>
<span class="icon icon-close" />
</button>` : ''}
</output>
`);

if (closeButton) {
snackbar.querySelector(`.${componentName}__close-button`).addEventListener('click', handleCloseButtonClick);
}
decorateIcons(snackbar);
return snackbar;
};

const addSnackbar = (container, snackbar) => {
if (container.children.length) {
removeSnackbar(container.children[0]);
}
container.appendChild(snackbar);
};

/**
* Displays a snackbar notification with the specified text, type, and configuration options.
*
* @param {string} text - The text to display in the snackbar
* @param {(''|'error'|'success')} [type=''] - The type of the snackbar
* @param {('left'|'center'|'right')} [positionX='center'] - The horizontal position of the snackbar
* @param {('top'|'bottom')} [positionY='bottom'] - The vertical position of the snackbar
* @param {boolean} [buttonsBelow=false] - Whether to display buttons below the snackbar
* @param {boolean} [closeButton=false] - Whether to display a close button on the snackbar
* @param {number} [duration=5000] - The duration in milliseconds for which the snackbar
* @param {boolean} [persistent=false] - Whether the snackbar should be stay on screen until closed
*/
export default function showSnackbar(
text,
type,
positionX = 'center',
positionY = 'bottom',
buttonsBelow = false,
closeButton = false,
duration = 5000,
persistent = false,
) {
const container = document.querySelector(`.${componentName}-container`) || initSnackbarContainer(positionX, positionY);
const snackbar = createSnackbar(text, type, buttonsBelow, closeButton);
addSnackbar(container, snackbar);

if (!persistent) {
setTimeout(() => {
if (container.children.length) {
removeSnackbar(container.children[0]);
}
}, duration);
}
}
3 changes: 3 additions & 0 deletions icons/checkmark-circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions icons/remove.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 28 additions & 1 deletion scripts/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -435,12 +435,18 @@ const formatValues = (values) => {
return obj;
};

const { searchUrls, cookieValues, magazineConfig } = await getConstantValues();
const {
searchUrls,
cookieValues,
magazineConfig,
tools,
} = await getConstantValues();

// This data comes from the sharepoint 'constants.xlsx' file
export const COOKIE_CONFIGS = formatValues(cookieValues.data);
export const SEARCH_URLS = formatValues(searchUrls.data);
export const MAGAZINE_CONFIGS = formatValues(magazineConfig.data);
export const TOOLS_CONFIGS = formatValues(tools?.data);

/**
* Check if one trust group is checked.
Expand Down Expand Up @@ -469,6 +475,27 @@ export function isSocialAllowed() {
return checkOneTrustGroup(SOCIAL_COOKIE);
}

/**
* See https://www.aem.live/developer/spreadsheets#arrays
* Converts a string representation of an array, removing all brackets, backslashes, and quotes,
* into an actual JavaScript array. Splits on commas, trims each string, and filters out empty
* strings to ensure all elements contain valid data.
*
* @param {string} inputString - The string to be converted. It should mimic a serialized array,
* often found in JSON-like structures where arrays are represented
* as strings due to data transmission constraints.
* @returns {string[]} An array of strings derived from the cleaned input string. Each element
* is a trimmed, non-empty string that was separated by a comma in the
* original input.
*/
export const formatStringToArray = (inputString) => {
// eslint-disable-next-line no-useless-escape
const cleanedString = inputString.replace(/[\[\]\\'"]+/g, '');
return cleanedString.split(',')
.map((item) => item.trim())
.filter((item) => item);
};

/*
The generateId function should be used only
for generating the id for UI elements
Expand Down
8 changes: 7 additions & 1 deletion scripts/delayed.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
COOKIE_CONFIGS,
} from './common.js';

const devHosts = ['localhost', 'hlx.page', 'hlx.live', 'aem.page', 'aem.live'];

// COOKIE ACCEPTANCE AND IDs default to false in case no ID is present
const {
FACEBOOK_PIXEL_ID = false,
Expand Down Expand Up @@ -54,7 +56,7 @@ document.addEventListener('click', (e) => {

// OneTrust Cookies Consent Notice start for volvotrucks.us
if (!window.location.pathname.includes('srcdoc')
&& !['localhost', 'hlx.page', 'hlx.live', 'aem.page', 'aem.live'].some((url) => window.location.host.includes(url))) {
&& !devHosts.some((url) => window.location.host.includes(url))) {
// when running on localhost in the block library host is empty but the path is srcdoc
// on localhost/hlx.page/hlx.live the consent notice is displayed every time the page opens,
// because the cookie is not persistent. To avoid this annoyance, disable unless on the
Expand Down Expand Up @@ -87,6 +89,10 @@ if (!window.location.pathname.includes('srcdoc')
};
}

if (devHosts.some((url) => window.location.host.includes(url))) {
import('./validate-elements.js');
}

// Google Analytics
async function loadGoogleTagManager() {
// google tag manager
Expand Down
Loading

0 comments on commit 62fc86a

Please sign in to comment.