diff --git a/blocks/embed/embed.css b/blocks/embed/embed.css index 346beb49..15271bfa 100644 --- a/blocks/embed/embed.css +++ b/blocks/embed/embed.css @@ -11,12 +11,43 @@ .embed-video-element { aspect-ratio: 16/9; width: 100%; + display: block; } .embed .video-icon-wrapper { pointer-events: none; } +.embed .cookie-message { + aspect-ratio: 16/9; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; +} + +.embed .cookie-message__title, +.embed .cookie-message__text { + max-width: var(--text-block-max-width); + margin: 0 auto; + text-align: center; +} + +.embed .cookie-message__title { + font: var(--f-heading-5-font-size)/var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: var(--f-heading-5-letter-spacing); +} + +.embed .cookie-message__text { + font: var(--f-body-font-size)/var(--f-body-line-height) var(--font-family-body); +} + +.embed .cookie-message__text a { + color: inherit; + text-decoration: underline; +} + @media screen and (min-width: 768px) { .embed-video { width: 726px; @@ -35,4 +66,4 @@ .embed.embed-full-width .embed-video { width: 100%; } -} \ No newline at end of file +} diff --git a/blocks/embed/embed.js b/blocks/embed/embed.js index 3daca21d..c5c86a54 100644 --- a/blocks/embed/embed.js +++ b/blocks/embed/embed.js @@ -1,5 +1,6 @@ import { - selectVideoLink, addPlayIcon, showVideoModal, isLowResolutionVideoUrl, + selectVideoLink, addPlayIcon, + showVideoModal, isLowResolutionVideoUrl, createLowResolutionBanner, createIframe, } from '../../scripts/video-helper.js'; @@ -13,8 +14,13 @@ export default function decorate(block) { block.classList.remove('loop', 'autoplay', 'full-width'); videoWrapper.classList.add('embed-video'); + const preferredType = (() => { + if (isFullWidth) return 'local'; + return 'auto'; + })(); + const links = block.querySelectorAll('a'); - const selectedLink = selectVideoLink(links, isFullWidth ? 'local' : 'auto'); + const selectedLink = selectVideoLink(links, preferredType); const video = document.createElement('video'); const source = document.createElement('source'); diff --git a/blocks/v2-livestream-embed/v2-livestream-embed.css b/blocks/v2-livestream-embed/v2-livestream-embed.css new file mode 100644 index 00000000..e31a880b --- /dev/null +++ b/blocks/v2-livestream-embed/v2-livestream-embed.css @@ -0,0 +1,63 @@ +.v2-livestream-embed { + border-radius: var(--border-radius); + aspect-ratio: 16/9; + width: 100%; +} + +.v2-livestream-embed iframe { + display: block; +} + +.v2-livestream-embed .cookie-message { + aspect-ratio: 16/9; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; +} + +.v2-livestream-embed .cookie-message__title, +.v2-livestream-embed .cookie-message__text { + max-width: var(--text-block-max-width); + margin: 0 auto; + text-align: center; +} + +.v2-livestream-embed .cookie-message__title { + font: var(--f-heading-5-font-size)/var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: var(--f-heading-5-letter-spacing); +} + +.v2-livestream-embed .cookie-message__text { + font: var(--f-body-font-size)/var(--f-body-line-height) var(--font-family-body); +} + +.v2-livestream-embed .cookie-message__text a { + color: inherit; + text-decoration: underline; +} + +.v2-livestream-embed .cookie-message__text a:hover { + color: var(--c-grey-300); +} + +.v2-livestream-embed .cookie-message__button-container { + margin-top: 16px; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 16px; +} + +@media screen and (min-width: 1200px) { + .v2-livestream-embed .cookie-message__title { + font-size: var(--f-heading-4-font-size); + line-height: var(--f-heading-4-line-height); + } + + .v2-livestream-embed .cookie-message__text { + font-size: var(--f-body-2-font-size); + line-height: var(--f-body-2-line-height); + } +} diff --git a/blocks/v2-livestream-embed/v2-livestream-embed.js b/blocks/v2-livestream-embed/v2-livestream-embed.js new file mode 100644 index 00000000..13835558 --- /dev/null +++ b/blocks/v2-livestream-embed/v2-livestream-embed.js @@ -0,0 +1,113 @@ +/* eslint-disable no-console */ +/* eslint-disable no-unused-vars */ +/* eslint-disable max-len */ +import { loadScript } from '../../scripts/lib-franklin.js'; +import { createElement, getTextLabel, isExternalVideoAllowed } from '../../scripts/common.js'; +import { updateCookieValue } from '../../scripts/delayed.js'; +import { hideModal } from '../../common/modal/modal.js'; + +let player; + +function onPlayerReady(event) { + console.info('Event: onPlayerReady'); + event.target.playVideo(); +} + +function onPlayerError(event) { + console.warn('Event: onPlayerError'); + console.warn(event.data); +} + +function onPlayerAutoplayBlocked(event) { + console.warn('Event: onPlayerAutoplayBlocked'); + console.warn(event.data); +} + +export function playVideo() { + if (player && player.playVideo) { + player.playVideo(); + } +} + +export default function decorate(block) { + loadScript('https://www.youtube.com/iframe_api'); + + let [videoLink] = block.querySelectorAll('a'); + const [, videoId] = videoLink.getAttribute('href').split('/embed/'); + const [videoCode] = videoId.split('?'); + videoLink = videoCode; + + console.info(`video id: ${videoLink}`); + + if (!videoLink) { + block.innerHTML = ''; + /* eslint-disable-next-line no-console */ + console.warn('V2 Livestream Embed block: There is no video link. Please check the provided URL.'); + return; + } + + // eslint-disable-next-line func-names + window.onYouTubeIframeAPIReady = function () { + setTimeout(() => { + // eslint-disable-next-line no-undef + player = new YT.Player('livestream', { + events: { + onReady: onPlayerReady, + onError: onPlayerError, + onAutoplayBlocked: onPlayerAutoplayBlocked, + }, + }); + }, 3000); + }; + + if (!isExternalVideoAllowed()) { + const img = block.querySelector('picture img'); + block.innerHTML = ''; + + const cookieMsgContainer = createElement('div', { + classes: 'cookie-message', + }); + cookieMsgContainer.style.background = `linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, rgba(0, 0, 0, 0.80) 100%), url(${img.src}) center / cover no-repeat`; + + const cookieMessage = document.createRange().createContextualFragment(` + + ${getTextLabel('single video message text')} + + `); + + cookieMsgContainer.append(cookieMessage); + block.append(cookieMsgContainer); + + block.querySelector('.cookie-message__button-container .primary')?.addEventListener('click', () => { + const domain = 'localhost'; + const path = '/'; // assuming root path + const expirationDate = new Date(); + expirationDate.setFullYear(expirationDate.getFullYear() + 1); // 1 year from now + const sameSite = 'Lax'; + + console.log('updatecookie'); + + updateCookieValue('OptanonConsent=', 'C0005:0', 'C0005:1', domain, path, expirationDate, sameSite); + }); + + block.querySelector('.cookie-message__button-container .secondary')?.addEventListener('click', () => { + hideModal(); + }); + + return; + } + + const iframeYT = document.createRange().createContextualFragment(` + + `); + + block.innerHTML = ''; + + block.append(...iframeYT.childNodes); +} diff --git a/common/modal/modal.css b/common/modal/modal.css index b3783b4a..07f40484 100644 --- a/common/modal/modal.css +++ b/common/modal/modal.css @@ -236,3 +236,20 @@ width: 100%; } } + +.modal-reveal { + display: flex; + flex-direction: column; + justify-content: center; +} + +/* stylelint-disable-next-line no-descending-specificity */ +.modal-reveal .modal-content { + height: auto; + aspect-ratio: auto; +} + +.modal-reveal .section > div { + padding-top: 0; + padding-bottom: 0; +} diff --git a/common/modal/modal.js b/common/modal/modal.js index a4d0a4cb..23343571 100644 --- a/common/modal/modal.js +++ b/common/modal/modal.js @@ -35,8 +35,10 @@ const createModal = () => { modalBackground.setAttribute('role', 'dialog'); modalBackground.addEventListener('click', () => { - // eslint-disable-next-line no-use-before-define - hideModal(); + if (!modalBackground.classList.contains('modal-reveal')) { + // eslint-disable-next-line no-use-before-define + hideModal(); + } }); const keyDownAction = (event) => { diff --git a/placeholder.json b/placeholder.json index 180252bb..5000b7f2 100644 --- a/placeholder.json +++ b/placeholder.json @@ -228,8 +228,20 @@ "Text": "Model year" }, { - "Key": "go to top", - "Text": "Go to the top of the page" + "Key": "single video message title", + "Text": "Enable cookies to view the reveal in high resolution?" + }, + { + "Key": "single video message text", + "Text": "

Our media viewer uses social media cookies and/or similar technologies set by third-party services, as outlined in our Cookie Notice. We and our digital partners use cookies to improve your browsing experience, save your preferences and provide us with information on how you use our website.

You can click “Accept All Cookies” to view the reveal and to agree to the storing of cookies on your device and to our use of cookies. You can also configure or reject cookies by clicking on “Cookie Settings” or “Reject All.” Please note that rejecting cookies means that you will not be able to view the reveal. Please also note that selecting “Reject All” still implies that necessary cookies will remain.

" + }, + { + "Key": "single video message button", + "Text": "Accept All Cookies" + }, + { + "Key": "single video message button deny", + "Text": "Reject All" } ], ":type": "sheet" diff --git a/scripts/common.js b/scripts/common.js index 77e30b9a..adf6a36b 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -316,13 +316,17 @@ export const slugify = (text) => ( * Check if one trust group is checked. * @param {String} groupName the one trust croup like: C0002 */ -export function checkOneTruckGroup(groupName) { +export function checkOneTrustGroup(groupName) { const oneTrustCookie = decodeURIComponent(document.cookie.split(';').find((cookie) => cookie.trim().startsWith('OptanonConsent='))); return oneTrustCookie.includes(`${groupName}:1`); } export function isEloquaFormAllowed() { - return checkOneTruckGroup('C0004'); + return checkOneTrustGroup('C0004'); +} + +export function isExternalVideoAllowed() { + return checkOneTrustGroup('C0005'); } /* diff --git a/scripts/delayed.js b/scripts/delayed.js index ad4168a3..992f946f 100644 --- a/scripts/delayed.js +++ b/scripts/delayed.js @@ -1,3 +1,4 @@ +// eslint-disable no-console // eslint-disable-next-line import/no-cycle import { loadScript, sampleRUM } from './lib-franklin.js'; @@ -14,6 +15,40 @@ const cookieSetting = decodeURIComponent(document.cookie.split(';') const isPerformanceAllowed = cookieSetting.includes(COOKIES.performance); const isSocialAllowed = cookieSetting.includes(COOKIES.social); +export function updateCookieValue(cookieName, oldValue, newValue, domain, path, expires, sameSite) { + let cookies = decodeURIComponent(document.cookie).split(';'); + console.info(cookies); + + for (let i = 0; i < cookies.length; i++) { + let cookie = cookies[i].trim(); + if (cookie.startsWith(cookieName)) { + let cookieValue = cookie.substring(cookieName.length); + console.info(cookieValue); + if (cookieValue.includes(oldValue)) { + console.info(oldValue); + let updatedValue = encodeURIComponent(cookieValue.replace(oldValue, newValue)); + console.info(updatedValue); + let updatedCookie = cookieName + updatedValue; + console.info(updatedCookie); + if (domain) { + updatedCookie += `; domain=${domain}`; + } + if (path) { + updatedCookie += `; path=${path}`; + } + if (expires) { + updatedCookie += `; expires=${expires.toUTCString()}`; + } + if (sameSite) { + updatedCookie += `; SameSite=${sameSite}`; + } + document.cookie = updatedCookie; + } + break; + } + } +} + if (isPerformanceAllowed) { loadGoogleTagManager(); loadHotjar(); @@ -57,9 +92,14 @@ if (!window.location.pathname.includes('srcdoc') return s1 === s2; } + const isSingleVideo = document.cookie.split('; ').reduce((r, v) => { + const parts = v.split('='); + return parts[0] === 'isSingleVideo' ? decodeURIComponent(parts[1]) : r; + }, ''); + window.OneTrust.OnConsentChanged(() => { // reloading the page only when the active group has changed - if (!isSameGroups(currentOnetrustActiveGroups, window.OnetrustActiveGroups)) { + if (!isSameGroups(currentOnetrustActiveGroups, window.OnetrustActiveGroups) && isSingleVideo !== 'true') { window.location.reload(); } }); diff --git a/scripts/video-helper.js b/scripts/video-helper.js index ec4cb763..ceb55272 100644 --- a/scripts/video-helper.js +++ b/scripts/video-helper.js @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-cycle -import { createElement, getTextLabel } from './common.js'; +import { createElement, getTextLabel, isExternalVideoAllowed } from './common.js'; /* video helpers */ export function isLowResolutionVideoUrl(url) { @@ -13,12 +13,13 @@ export function isVideoLink(link) { export function selectVideoLink(links, preferredType) { const linksList = [...links]; - const optanonConsentCookieValue = decodeURIComponent(document.cookie.split(';').find((cookie) => cookie.trim().startsWith('OptanonConsent='))); - const cookieConsentForExternalVideos = optanonConsentCookieValue.includes('C0005:1'); - const shouldUseYouTubeLinks = cookieConsentForExternalVideos && preferredType !== 'local'; + const shouldUseYouTubeLinks = isExternalVideoAllowed() && preferredType !== 'local'; const youTubeLink = linksList.find((link) => link.getAttribute('href').includes('youtube.com/embed/')); const localMediaLink = linksList.find((link) => link.getAttribute('href').split('?')[0].endsWith('.mp4')); + if (preferredType === 'singleVideo' && youTubeLink) { + return youTubeLink; + } if (shouldUseYouTubeLinks && youTubeLink) { return youTubeLink; } @@ -28,13 +29,30 @@ export function selectVideoLink(links, preferredType) { export function createLowResolutionBanner() { const lowResolutionMessage = getTextLabel('Low resolution video message'); const changeCookieSettings = getTextLabel('Change cookie settings'); + let banner; - const banner = document.createElement('div'); - banner.classList.add('low-resolution-banner'); - banner.innerHTML = `${lowResolutionMessage} `; - banner.querySelector('button').addEventListener('click', () => { - window.OneTrust.ToggleInfoDisplay(); - }); + if (document.documentElement.classList.contains('redesign-v2')) { + banner = createElement('div', { classes: 'low-resolution-banner' }); + const bannerText = createElement('p'); + const bannerButton = createElement('button', { classes: ['button', 'secondary', 'dark'] }); + + bannerText.textContent = lowResolutionMessage; + bannerButton.textContent = changeCookieSettings; + + banner.appendChild(bannerText); + banner.appendChild(bannerButton); + + bannerButton.addEventListener('click', () => { + window.OneTrust.ToggleInfoDisplay(); + }); + } else { + banner = document.createElement('div'); + banner.classList.add('low-resolution-banner'); + banner.innerHTML = `${lowResolutionMessage} `; + banner.querySelector('button').addEventListener('click', () => { + window.OneTrust.ToggleInfoDisplay(); + }); + } return banner; } @@ -347,3 +365,10 @@ export const addMuteControls = (section) => { controls.addEventListener('click', () => toggleMute(video)); }; + +export function loadYouTubeIframeAPI() { + const tag = document.createElement('script'); + tag.src = 'https://www.youtube.com/iframe_api'; + const firstScriptTag = document.getElementsByTagName('script')[0]; + firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); +} diff --git a/styles/styles.css b/styles/styles.css index ff2aaebf..a3095c08 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -874,6 +874,33 @@ a.button.text-link-with-video::after { font-size: 18px; } +.redesign-v2 .low-resolution-banner { + margin-bottom: 16px; + padding: 16px; + background: var(--c-grey-4); + text-align: left; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 16px; + color: var(--c-white); +} + +.redesign-v2 .low-resolution-banner p { + margin: 0; +} + +.redesign-v2 .low-resolution-banner .button.secondary { + margin-left: auto; +} + +@media screen and (min-width: 1200px) { + .redesign-v2 .low-resolution-banner { + padding: 16px 32px; + } +} + /* pagination */ .pager .pagination ol li .last::before { font-family: var(--ff-fontawesome); diff --git a/test/scripts/common.test.js b/test/scripts/common.test.js index 4b4ebe1f..8e5eee33 100644 --- a/test/scripts/common.test.js +++ b/test/scripts/common.test.js @@ -206,12 +206,12 @@ describe('slugify', () => { }); }); -describe('checkOneTruckGroup', () => { +describe('checkOneTrustGroup', () => { it('should return true when the group is present with value 1', () => { // Simulate a cookie with the group 'group1' set to 1 document.cookie = 'OptanonConsent=group1:1;'; - const result = commonScript.checkOneTruckGroup('group1'); + const result = commonScript.checkOneTrustGroup('group1'); expect(result).to.be.true; }); @@ -219,7 +219,7 @@ describe('checkOneTruckGroup', () => { // Simulate a cookie with the group 'group2' set to 0 (or any value other than 1) document.cookie = 'OptanonConsent=group2:0;'; - const result = commonScript.checkOneTruckGroup('group2'); + const result = commonScript.checkOneTrustGroup('group2'); expect(result).to.be.false; }); @@ -227,7 +227,7 @@ describe('checkOneTruckGroup', () => { // Simulate an empty cookie document.cookie = ''; - const result = commonScript.checkOneTruckGroup('group3'); + const result = commonScript.checkOneTrustGroup('group3'); expect(result).to.be.false; }); @@ -235,7 +235,7 @@ describe('checkOneTruckGroup', () => { // Simulate a cookie with a URL-encoded group name document.cookie = 'OptanonConsent=group%204:1;'; - const result = commonScript.checkOneTruckGroup('group 4'); + const result = commonScript.checkOneTrustGroup('group 4'); expect(result).to.be.true; }); }); diff --git a/test/scripts/video-helper.test.js b/test/scripts/video-helper.test.js index 2e8957ad..5866b751 100644 --- a/test/scripts/video-helper.test.js +++ b/test/scripts/video-helper.test.js @@ -88,10 +88,10 @@ describe('selectVideoLink', () => { youtubeLink = commonScript.createElement('a', { props: { href: 'https://www.youtube.com/embed/example-video' } }); mp4Link = commonScript.createElement('a', { props: { href: 'https://example.com/example-video.mp4' } }); otherLink = commonScript.createElement('a', { props: { href: 'https://example.com/other-link' } }); - document.cookie = 'OptanonConsent=C0005:1'; }); it('should return YouTube link when preferredType is not "local" and YouTube link is available', () => { + document.cookie = 'OptanonConsent=C0005:1'; const links = [youtubeLink, mp4Link]; const result = videoHelper.selectVideoLink(links, 'external'); expect(result).to.equal(youtubeLink);