From ae0fa673a1b4f1e1fb6056592c568248b1be444f Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Tue, 15 Oct 2024 23:53:59 +0530 Subject: [PATCH 01/36] updated feature with accessibility code --- libs/blocks/aside/aside.css | 5 ++ libs/blocks/aside/aside.js | 2 +- libs/blocks/brick/brick.css | 10 +++ libs/blocks/figure/figure.css | 1 + libs/blocks/figure/figure.js | 10 ++- libs/blocks/hero-marquee/hero-marquee.js | 2 +- libs/blocks/marquee/marquee.js | 2 +- libs/blocks/media/media.css | 2 +- libs/blocks/video/video.css | 85 ++++++++++++++++++++++++ libs/utils/decorate.js | 74 +++++++++++++++++++-- 10 files changed, 180 insertions(+), 13 deletions(-) diff --git a/libs/blocks/aside/aside.css b/libs/blocks/aside/aside.css index 7feb805ada..17d4327405 100644 --- a/libs/blocks/aside/aside.css +++ b/libs/blocks/aside/aside.css @@ -298,6 +298,7 @@ } .aside.rounded-corners .foreground .image img, +.aside.rounded-corners .foreground .image:not(:has(.video-container)) .pause-play-wrapper, .aside.rounded-corners .foreground .image video { border-radius: 16px; } @@ -1074,6 +1075,10 @@ max-width: 1396px; } + .aside.split .video-container .pause-play-wrapper img.accessibility-control { + width: auto; + } + .aside.split .desktop-wide img, .aside.split .desktop-wide video { aspect-ratio: var(--aspect-ratio-wide); diff --git a/libs/blocks/aside/aside.js b/libs/blocks/aside/aside.js index fc488d4ada..de5a041f62 100644 --- a/libs/blocks/aside/aside.js +++ b/libs/blocks/aside/aside.js @@ -197,7 +197,7 @@ function decorateLayout(el) { } const foregroundImage = foreground.querySelector(':scope > div:not(.text) img')?.closest('div'); const bgImage = el.querySelector(':scope > div:not(.text):not(.foreground) img')?.closest('div'); - const foregroundMedia = foreground.querySelector(':scope > div:not(.text) video, :scope > div:not(.text) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div'); + const foregroundMedia = foreground.querySelector(':scope > div:not(.text) .video-container,:scope > div:not(.text) video, :scope > div:not(.text) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div:not(.video-container)'); const bgMedia = el.querySelector(':scope > div:not(.text):not(.foreground) video, :scope > div:not(.text):not(.foreground) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div'); const image = foregroundImage ?? bgImage; const asideMedia = foregroundMedia ?? bgMedia ?? image; diff --git a/libs/blocks/brick/brick.css b/libs/blocks/brick/brick.css index 43cd31584d..1f58444f21 100644 --- a/libs/blocks/brick/brick.css +++ b/libs/blocks/brick/brick.css @@ -98,6 +98,10 @@ margin: 0; } +.brick .foreground div > .video-container { + margin: 0; +} + .brick .foreground div > * { margin-top: var(--spacing-xs); } @@ -342,6 +346,12 @@ position: absolute; } + .brick.split.row .foreground .brick-media .video-container img, + .brick.split.row .foreground .brick-media .video-container video { + width: 100%; + height: 100%; + } + .brick .foreground .brick-media video, .brick.split.row .foreground .brick-media video { object-fit: fill; diff --git a/libs/blocks/figure/figure.css b/libs/blocks/figure/figure.css index 28075d0fb4..c876ea06f8 100644 --- a/libs/blocks/figure/figure.css +++ b/libs/blocks/figure/figure.css @@ -69,6 +69,7 @@ .figure .figure { margin: 0 auto; max-width: 800px; + width: fit-content; } .figure-list { diff --git a/libs/blocks/figure/figure.js b/libs/blocks/figure/figure.js index cd0d80cd88..0019db8822 100644 --- a/libs/blocks/figure/figure.js +++ b/libs/blocks/figure/figure.js @@ -1,4 +1,4 @@ -import { applyHoverPlay, decorateAnchorVideo } from '../../utils/decorate.js'; +import { applyHoverPlay, decorateAnchorVideo, handlePause } from '../../utils/decorate.js'; import { createTag } from '../../utils/utils.js'; function buildCaption(pEl) { @@ -21,6 +21,7 @@ function decorateVideo(clone, figEl) { if (anchorTag && !anchorTag.hash) anchorTag.hash = '#autoplay'; if (anchorTag) decorateAnchorVideo({ src: anchorTag.href, anchorTag }); if (videoTag) { + const videoContainer = clone.querySelector('.video-container') || clone.querySelector('.pause-play-wrapper') || clone.querySelector('video'); videoTag.removeAttribute('data-mouseevent'); if (videoTag.dataset?.videoSource) { videoTag.appendChild( @@ -31,7 +32,10 @@ function decorateVideo(clone, figEl) { ); } applyHoverPlay(videoTag); - figEl.prepend(videoTag); + figEl.prepend(videoContainer); + const pausePlayWrapper = videoContainer.querySelector('.pause-play-wrapper') || videoContainer + pausePlayWrapper?.addEventListener('click', handlePause); + pausePlayWrapper?.addEventListener('keydown', handlePause) } } @@ -68,7 +72,7 @@ export function buildFigure(blockEl) { const link = clone.querySelector('a'); if (link) { const img = figEl.querySelector('picture') || figEl.querySelector('video'); - if (img) { + if (img && !link.classList.contains('pause-play-wrapper')) { // wrap picture or video in A tag link.textContent = ''; link.append(img); diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index e464c7d3c2..be1683ca54 100644 --- a/libs/blocks/hero-marquee/hero-marquee.js +++ b/libs/blocks/hero-marquee/hero-marquee.js @@ -183,7 +183,7 @@ export default async function init(el) { foreground.classList.add('foreground', `cols-${fRows.length}`); let copy = fRows[0]; const anyTag = foreground.querySelector('p, h1, h2, h3, h4, h5, h6'); - const asset = foreground.querySelector('div > picture, div > video, div > a[href*=".mp4"], div > a.image-link'); + const asset = foreground.querySelector('div > picture,div > .video-container,div > .pause-play-wrapper, div > video, div > a[href*=".mp4"], div > a.image-link'); const allRows = foreground.querySelectorAll('div > div'); copy = anyTag.closest('div'); copy.classList.add('copy'); diff --git a/libs/blocks/marquee/marquee.js b/libs/blocks/marquee/marquee.js index d747586fd3..e62546d76b 100644 --- a/libs/blocks/marquee/marquee.js +++ b/libs/blocks/marquee/marquee.js @@ -88,7 +88,7 @@ function decorateSplit(el, foreground, media) { let mediaCreditInner; const txtContent = media?.lastChild?.textContent?.trim(); - if (txtContent?.match(/^http.*\.mp4/) || media?.lastChild?.tagName === 'VIDEO') return; + if (txtContent?.match(/^http.*\.mp4/) || media.querySelector('video')) return; if (txtContent) { mediaCreditInner = createTag('p', { class: 'body-s' }, txtContent); } else if (media.lastElementChild?.tagName !== 'PICTURE') { diff --git a/libs/blocks/media/media.css b/libs/blocks/media/media.css index 169c6d07e5..5a1eaa8e47 100644 --- a/libs/blocks/media/media.css +++ b/libs/blocks/media/media.css @@ -394,7 +394,7 @@ div[class*="-up"] .media .foreground > .media-row { margin: 0 auto; } - .media.large > .foreground .media-row .image { min-width: 700px; } + .media.large > .foreground .media-row .image { min-width: fit-content; } .media[class*="-up"].large > .foreground .media-row .image, div[class*="-up"] .media.large > .foreground .media-row .image { min-width: 100%; } diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index f6ec58c065..b7ac7df4e4 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -6,3 +6,88 @@ video { max-width: 100%; height: auto; } + + +.pause-play-wrapper img { + min-height:40px !important; +} + +.video-container{ + display: flex; + position: relative; + height:100%; + width: fit-content; + margin: auto; +} + +.aside .video-container,.marquee .video-container,.quiz-marquee .video-container { + width: auto; +} + +.brick-media .video-container { + width: 100%; + height: 100%; + border-radius: inherit; +} + +.hero-marquee .background .video-container { + position: inherit; +} + +.pause-play-wrapper { + display: flex; + width: fit-content; + margin: auto; +} + +.video-container .pause-play-wrapper { + width: 40px; + height: 40px; + display: flex; + position: absolute; + right: 2%; + bottom: 2%; + margin: 0; + border-radius: 50%; + outline-color: var(--color-accent-focus-ring); + z-index: 2; + cursor: pointer; +} + +.brick .brick-media:not(:has(.video-container)) .pause-play-wrapper { + border-radius: 0; + border-top-right-radius: inherit; + border-bottom-right-radius: inherit; + width: auto; + height: 100%; + margin: 0; +} + +.marquee.split:not(:has(.video-container)) .pause-play-wrapper, +.hero-marquee:not(:has(.video-container)) .pause-play-wrapper { + width: auto; + height: 100%; +} + +.video-container .pause-play-wrapper img.accessibility-control { + width: auto; +} + +.video-container .pause-play-wrapper img.hidden{ + display: none; +} + +.media:not(.media-reverse-mobile, .media-reversed) .pause-play-wrapper { + left: 2%; +} + +.marquee .background .video-container { + display: contents; +} + +@media (min-width: 900px) { +.image:first-child .pause-play-wrapper,.hero-marquee.asset-left .pause-play-wrapper { + left: 2%; +} + +} diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 0482e40505..487c3f16e4 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -2,6 +2,7 @@ import { createTag, loadStyle, getConfig, createIntersectionObserver } from './u const { miloLibs, codeRoot } = getConfig(); +const accessibilityEnabled = true; export function decorateButtons(el, size) { const buttons = el.querySelectorAll('em a, strong a, p > a strong'); if (buttons.length === 0) return; @@ -228,12 +229,64 @@ function getVideoAttrs(hash, dataset) { return `${globalAttrs} controls`; } +export const syncPausePlayIcon = (video) => { + const playIcon = video.nextElementSibling?.querySelector('.play-icon'); + const pauseIcon = video.nextElementSibling?.querySelector('.pause-icon'); + if (video.paused || video.ended) { + playIcon?.classList.remove('hidden'); + pauseIcon?.classList.add('hidden'); + } else { + pauseIcon?.classList.remove('hidden'); + playIcon?.classList.add('hidden'); + } +} + +export const addAccessibilityControl = (videoString, videoAttributes) => { + if (accessibilityEnabled && !videoAttributes.includes('controls')) { + if (videoAttributes.includes('hoverplay')) { + return `${videoString} + `; + } else { + return `
${videoString} + + + + +
`; + } + } else { + return videoString; + } + +} + +export const handlePause = (event) => { + if (event.code !== 'Enter' && event.code !== 'Space' && !['focus', 'click', 'blur'].includes(event.type)) { + return; + } + event.preventDefault(); + const video = event.target.parentElement.parentElement.querySelector('video'); + if (event.type === 'blur') { + video.pause(); + } else if (video.paused || video.ended || event.type === 'focus') { + video.play(); + } else { + video.pause(); + } + syncPausePlayIcon(video); +} + export function applyHoverPlay(video) { if (!video) return; - if (video.hasAttribute('data-hoverplay') && !video.hasAttribute('data-mouseevent')) { - video.addEventListener('mouseenter', () => { video.play(); }); - video.addEventListener('mouseleave', () => { video.pause(); }); - video.setAttribute('data-mouseevent', true); + if (video.hasAttribute('data-hoverplay')) { + video.parentElement.addEventListener('focus', handlePause); + video.parentElement.addEventListener('blur', handlePause); + if (!video.hasAttribute('data-mouseevent')) { + video.addEventListener('mouseenter', () => { video.play(); }); + video.addEventListener('mouseleave', () => { video.pause(); }); + video.addEventListener('ended', () => { syncPausePlayIcon(video); }); + video.setAttribute('data-mouseevent', true); + } } } @@ -269,7 +322,7 @@ function getVideoIntersectionObserver() { const isHaveLoopAttr = video.getAttributeNames().includes('loop'); const { playedOnce = false } = video.dataset; const isPlaying = video.currentTime > 0 && !video.paused && !video.ended - && video.readyState > video.HAVE_CURRENT_DATA; + && video.readyState > video.HAVE_CURRENT_DATA; if (intersectionRatio <= 0.8) { video.pause(); @@ -329,7 +382,9 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { if (!src.length || !(anchorTag instanceof HTMLElement)) return; if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; const { dataset, parentElement } = anchorTag; - const video = ``; + const attrs = getVideoAttrs(anchorTag.hash, dataset); + let video = ``; + video = addAccessibilityControl(video, attrs); anchorTag.insertAdjacentHTML('afterend', video); const videoEl = parentElement.querySelector('video'); createIntersectionObserver({ @@ -339,6 +394,13 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { videoEl?.appendChild(createTag('source', { src, type: 'video/mp4' })); }, }); + + const pausePlayWrapper = anchorTag.nextElementSibling.querySelector('.pause-play-wrapper'); + pausePlayWrapper?.addEventListener('click', handlePause); + pausePlayWrapper?.addEventListener('keydown', handlePause); + if (attrs.includes('autoplay')) { + videoEl.addEventListener('ended', () => { syncPausePlayIcon(videoEl); }); + } applyHoverPlay(videoEl); applyInViewPortPlay(videoEl); anchorTag.remove(); From b9ff89aa3e0fd30077942b73405a9a13aaf3552d Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Wed, 16 Oct 2024 12:46:17 +0530 Subject: [PATCH 02/36] video accessiblity added for carousels --- libs/blocks/carousel/carousel.js | 6 +++--- libs/utils/decorate.js | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libs/blocks/carousel/carousel.js b/libs/blocks/carousel/carousel.js index d454e46288..3bd0a6f2fa 100644 --- a/libs/blocks/carousel/carousel.js +++ b/libs/blocks/carousel/carousel.js @@ -156,7 +156,7 @@ function moveSlides(event, carouselElements, jumpToIndex) { referenceSlide.classList.remove('reference-slide'); referenceSlide.style.order = null; activeSlide.classList.remove('active'); - activeSlide.querySelectorAll('a').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', -1); }); + activeSlide.querySelectorAll('a,video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', -1); }); activeSlideIndicator.classList.remove('active'); activeSlideIndicator.setAttribute('tabindex', -1); @@ -218,10 +218,10 @@ function moveSlides(event, carouselElements, jumpToIndex) { if (index < show) { tabIndex = 0; } - slide.querySelectorAll('a').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', tabIndex); }); + slide.querySelectorAll('a,:not(.video-container,.pause-play-wrapper) > video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', tabIndex); }); }); } else { - activeSlide.querySelectorAll('a').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', 0); }); + activeSlide.querySelectorAll('a,:not(.video-container,.pause-play-wrapper) > video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', 0); }); } activeSlideIndicator.classList.add('active'); activeSlideIndicator.setAttribute('tabindex', 0); diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 487c3f16e4..4293c948ce 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -241,14 +241,14 @@ export const syncPausePlayIcon = (video) => { } } -export const addAccessibilityControl = (videoString, videoAttributes) => { +export const addAccessibilityControl = (videoString, videoAttributes, tabIndex = 0) => { if (accessibilityEnabled && !videoAttributes.includes('controls')) { if (videoAttributes.includes('hoverplay')) { - return `${videoString} + return `${videoString} `; } else { return `
${videoString} - + @@ -383,8 +383,10 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; const { dataset, parentElement } = anchorTag; const attrs = getVideoAttrs(anchorTag.hash, dataset); - let video = ``; - video = addAccessibilityControl(video, attrs); + const tabIndex = anchorTag.tabIndex || 0; + const videoIndex = (tabIndex===-1)?"tabindex=-1":''; + let video = ``; + video = addAccessibilityControl(video, attrs, tabIndex); anchorTag.insertAdjacentHTML('afterend', video); const videoEl = parentElement.querySelector('video'); createIntersectionObserver({ From d0b659fe32b172fe76b4017e605c8dfec33b5765 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Thu, 17 Oct 2024 00:04:26 +0530 Subject: [PATCH 03/36] added opt-out functionality --- libs/blocks/figure/figure.js | 6 ++++-- libs/blocks/media/media.css | 2 +- libs/utils/decorate.js | 35 ++++++++++++++++++++++++----------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/libs/blocks/figure/figure.js b/libs/blocks/figure/figure.js index 0019db8822..c38f987e38 100644 --- a/libs/blocks/figure/figure.js +++ b/libs/blocks/figure/figure.js @@ -34,8 +34,10 @@ function decorateVideo(clone, figEl) { applyHoverPlay(videoTag); figEl.prepend(videoContainer); const pausePlayWrapper = videoContainer.querySelector('.pause-play-wrapper') || videoContainer - pausePlayWrapper?.addEventListener('click', handlePause); - pausePlayWrapper?.addEventListener('keydown', handlePause) + if (pausePlayWrapper?.querySelector('.accessibility-control')) { + pausePlayWrapper?.addEventListener('click', handlePause); + pausePlayWrapper?.addEventListener('keydown', handlePause); + } } } diff --git a/libs/blocks/media/media.css b/libs/blocks/media/media.css index 5a1eaa8e47..169c6d07e5 100644 --- a/libs/blocks/media/media.css +++ b/libs/blocks/media/media.css @@ -394,7 +394,7 @@ div[class*="-up"] .media .foreground > .media-row { margin: 0 auto; } - .media.large > .foreground .media-row .image { min-width: fit-content; } + .media.large > .foreground .media-row .image { min-width: 700px; } .media[class*="-up"].large > .foreground .media-row .image, div[class*="-up"] .media.large > .foreground .media-row .image { min-width: 100%; } diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 4293c948ce..a36c37cc22 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -2,7 +2,6 @@ import { createTag, loadStyle, getConfig, createIntersectionObserver } from './u const { miloLibs, codeRoot } = getConfig(); -const accessibilityEnabled = true; export function decorateButtons(el, size) { const buttons = el.querySelectorAll('em a, strong a, p > a strong'); if (buttons.length === 0) return; @@ -242,7 +241,7 @@ export const syncPausePlayIcon = (video) => { } export const addAccessibilityControl = (videoString, videoAttributes, tabIndex = 0) => { - if (accessibilityEnabled && !videoAttributes.includes('controls')) { + if (!videoAttributes.includes('controls')) { if (videoAttributes.includes('hoverplay')) { return `${videoString} `; @@ -290,6 +289,17 @@ export function applyHoverPlay(video) { } } +export function applyAccessibiltyEvents(videoEl, attrs) { + const pausePlayWrapper = videoEl.parentElement.querySelector('.pause-play-wrapper') || videoEl.closest('.pause-play-wrapper'); + if (pausePlayWrapper?.querySelector('.accessibility-control')) { + pausePlayWrapper?.addEventListener('click', handlePause); + pausePlayWrapper?.addEventListener('keydown', handlePause); + } + if (attrs.includes('autoplay')) { + videoEl.addEventListener('ended', () => { syncPausePlayIcon(videoEl); }); + } +} + function setObjectFitAndPos(text, pic, bgEl, objFitOptions) { const backgroundConfig = text.split(',').map((c) => c.toLowerCase().trim()); const fitOption = objFitOptions.filter((c) => backgroundConfig.includes(c)); @@ -379,14 +389,17 @@ export async function loadCDT(el, classList) { } export function decorateAnchorVideo({ src = '', anchorTag }) { + const accessibilityEnabled = isAccessible(anchorTag); if (!src.length || !(anchorTag instanceof HTMLElement)) return; if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; const { dataset, parentElement } = anchorTag; const attrs = getVideoAttrs(anchorTag.hash, dataset); const tabIndex = anchorTag.tabIndex || 0; - const videoIndex = (tabIndex===-1)?"tabindex=-1":''; + const videoIndex = (tabIndex === -1) ? "tabindex=-1" : ''; let video = ``; - video = addAccessibilityControl(video, attrs, tabIndex); + if (accessibilityEnabled) { + video = addAccessibilityControl(video, attrs, tabIndex); + } anchorTag.insertAdjacentHTML('afterend', video); const videoEl = parentElement.querySelector('video'); createIntersectionObserver({ @@ -396,14 +409,14 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { videoEl?.appendChild(createTag('source', { src, type: 'video/mp4' })); }, }); - - const pausePlayWrapper = anchorTag.nextElementSibling.querySelector('.pause-play-wrapper'); - pausePlayWrapper?.addEventListener('click', handlePause); - pausePlayWrapper?.addEventListener('keydown', handlePause); - if (attrs.includes('autoplay')) { - videoEl.addEventListener('ended', () => { syncPausePlayIcon(videoEl); }); - } + accessibilityEnabled && applyAccessibiltyEvents(videoEl, attrs); applyHoverPlay(videoEl); applyInViewPortPlay(videoEl); anchorTag.remove(); } + +export function isAccessible(anchorTag) { + let section = anchorTag.closest('div[class="section"]'); + let block = Array.from(section.children).filter(block => block.contains(anchorTag))[0]; + return !block.classList.contains('hide-controls'); +} From 66b50a8c565702fcb78ad013f0144642dfb37515 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Thu, 17 Oct 2024 13:16:26 +0530 Subject: [PATCH 04/36] fixed linting errors --- libs/blocks/figure/figure.js | 8 ++------ libs/utils/decorate.js | 35 ++++++++++++++++------------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/libs/blocks/figure/figure.js b/libs/blocks/figure/figure.js index c38f987e38..896930f045 100644 --- a/libs/blocks/figure/figure.js +++ b/libs/blocks/figure/figure.js @@ -1,4 +1,4 @@ -import { applyHoverPlay, decorateAnchorVideo, handlePause } from '../../utils/decorate.js'; +import { applyHoverPlay, decorateAnchorVideo, applyAccessibiltyEvents } from '../../utils/decorate.js'; import { createTag } from '../../utils/utils.js'; function buildCaption(pEl) { @@ -32,12 +32,8 @@ function decorateVideo(clone, figEl) { ); } applyHoverPlay(videoTag); + applyAccessibiltyEvents(videoTag); figEl.prepend(videoContainer); - const pausePlayWrapper = videoContainer.querySelector('.pause-play-wrapper') || videoContainer - if (pausePlayWrapper?.querySelector('.accessibility-control')) { - pausePlayWrapper?.addEventListener('click', handlePause); - pausePlayWrapper?.addEventListener('keydown', handlePause); - } } } diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index a36c37cc22..6f05045640 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -228,7 +228,7 @@ function getVideoAttrs(hash, dataset) { return `${globalAttrs} controls`; } -export const syncPausePlayIcon = (video) => { +export function syncPausePlayIcon(video) { const playIcon = video.nextElementSibling?.querySelector('.play-icon'); const pauseIcon = video.nextElementSibling?.querySelector('.pause-icon'); if (video.paused || video.ended) { @@ -240,26 +240,23 @@ export const syncPausePlayIcon = (video) => { } } -export const addAccessibilityControl = (videoString, videoAttributes, tabIndex = 0) => { +export function addAccessibilityControl(videoString, videoAttributes, tabIndex = 0) { if (!videoAttributes.includes('controls')) { if (videoAttributes.includes('hoverplay')) { return `${videoString} `; - } else { - return `
${videoString} + } + return `
${videoString}
`; - } - } else { - return videoString; } - + return videoString; } -export const handlePause = (event) => { +export function handlePause(event) { if (event.code !== 'Enter' && event.code !== 'Space' && !['focus', 'click', 'blur'].includes(event.type)) { return; } @@ -289,13 +286,13 @@ export function applyHoverPlay(video) { } } -export function applyAccessibiltyEvents(videoEl, attrs) { +export function applyAccessibiltyEvents(videoEl) { const pausePlayWrapper = videoEl.parentElement.querySelector('.pause-play-wrapper') || videoEl.closest('.pause-play-wrapper'); if (pausePlayWrapper?.querySelector('.accessibility-control')) { pausePlayWrapper?.addEventListener('click', handlePause); pausePlayWrapper?.addEventListener('keydown', handlePause); } - if (attrs.includes('autoplay')) { + if (videoEl.hasAttribute('autoplay')) { videoEl.addEventListener('ended', () => { syncPausePlayIcon(videoEl); }); } } @@ -388,6 +385,12 @@ export async function loadCDT(el, classList) { } } +export function isAccessible(anchorTag) { + const section = anchorTag.closest('div[class="section"]'); + const [block] = Array.from(section.children).filter((element) => element.contains(anchorTag)); + return !block.classList.contains('hide-controls'); +} + export function decorateAnchorVideo({ src = '', anchorTag }) { const accessibilityEnabled = isAccessible(anchorTag); if (!src.length || !(anchorTag instanceof HTMLElement)) return; @@ -395,7 +398,7 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { const { dataset, parentElement } = anchorTag; const attrs = getVideoAttrs(anchorTag.hash, dataset); const tabIndex = anchorTag.tabIndex || 0; - const videoIndex = (tabIndex === -1) ? "tabindex=-1" : ''; + const videoIndex = (tabIndex === -1) ? 'tabindex=-1' : ''; let video = ``; if (accessibilityEnabled) { video = addAccessibilityControl(video, attrs, tabIndex); @@ -409,14 +412,8 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { videoEl?.appendChild(createTag('source', { src, type: 'video/mp4' })); }, }); - accessibilityEnabled && applyAccessibiltyEvents(videoEl, attrs); + if (accessibilityEnabled) applyAccessibiltyEvents(videoEl); applyHoverPlay(videoEl); applyInViewPortPlay(videoEl); anchorTag.remove(); } - -export function isAccessible(anchorTag) { - let section = anchorTag.closest('div[class="section"]'); - let block = Array.from(section.children).filter(block => block.contains(anchorTag))[0]; - return !block.classList.contains('hide-controls'); -} From 853df470dc86b071319fd95017258514cc7a1b64 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Thu, 17 Oct 2024 22:38:18 +0530 Subject: [PATCH 05/36] fixed unit test cases --- libs/blocks/video/video.css | 26 +++++++++++++------------- libs/utils/decorate.js | 13 ++++++++----- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index b7ac7df4e4..f957e6c60d 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -30,9 +30,6 @@ video { border-radius: inherit; } -.hero-marquee .background .video-container { - position: inherit; -} .pause-play-wrapper { display: flex; @@ -54,6 +51,14 @@ video { cursor: pointer; } +.editorial-card .video-container, +.editorial-card:not(:has(.video-container)) .pause-play-wrapper, +.hero-marquee:not(:has(.video-container)) .pause-play-wrapper, +.marquee.split:not(:has(.video-container)) .pause-play-wrapper { + width: auto; + height: 100%; +} + .brick .brick-media:not(:has(.video-container)) .pause-play-wrapper { border-radius: 0; border-top-right-radius: inherit; @@ -63,10 +68,8 @@ video { margin: 0; } -.marquee.split:not(:has(.video-container)) .pause-play-wrapper, -.hero-marquee:not(:has(.video-container)) .pause-play-wrapper { - width: auto; - height: 100%; +.hero-marquee .background .video-container { + position: inherit; } .video-container .pause-play-wrapper img.accessibility-control { @@ -77,17 +80,14 @@ video { display: none; } -.media:not(.media-reverse-mobile, .media-reversed) .pause-play-wrapper { - left: 2%; -} - .marquee .background .video-container { display: contents; } @media (min-width: 900px) { -.image:first-child .pause-play-wrapper,.hero-marquee.asset-left .pause-play-wrapper { +.media:not(.media-reverse-mobile, .media-reversed) .pause-play-wrapper, +.aside:not(.split-left) .pause-play-wrapper, +.hero-marquee.asset-left .pause-play-wrapper { left: 2%; } - } diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 6f05045640..7a2dd641a9 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -248,8 +248,8 @@ export function addAccessibilityControl(videoString, videoAttributes, tabIndex = } return `
${videoString} - - + pause icon + play icon
`; } @@ -387,14 +387,17 @@ export async function loadCDT(el, classList) { export function isAccessible(anchorTag) { const section = anchorTag.closest('div[class="section"]'); - const [block] = Array.from(section.children).filter((element) => element.contains(anchorTag)); - return !block.classList.contains('hide-controls'); + if (section) { + const [block] = Array.from(section.children).filter((element) => element.contains(anchorTag)); + return !block.classList.contains('hide-controls'); + } + return true; } export function decorateAnchorVideo({ src = '', anchorTag }) { - const accessibilityEnabled = isAccessible(anchorTag); if (!src.length || !(anchorTag instanceof HTMLElement)) return; if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; + const accessibilityEnabled = isAccessible(anchorTag); const { dataset, parentElement } = anchorTag; const attrs = getVideoAttrs(anchorTag.hash, dataset); const tabIndex = anchorTag.tabIndex || 0; From 48517ac94c95a7428840e9fdfabbdda84f927470 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Fri, 18 Oct 2024 13:14:24 +0530 Subject: [PATCH 06/36] fixed adobe tv issue --- libs/blocks/adobetv/adobetv.css | 1 + libs/utils/decorate.js | 1 + 2 files changed, 2 insertions(+) diff --git a/libs/blocks/adobetv/adobetv.css b/libs/blocks/adobetv/adobetv.css index 7907e053ca..28322d4585 100644 --- a/libs/blocks/adobetv/adobetv.css +++ b/libs/blocks/adobetv/adobetv.css @@ -1,4 +1,5 @@ @import url('../../styles/iframe.css'); +@import url('../video/video.css'); a[href*='.mp4'].hide-video { visibility: hidden !important; diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 7a2dd641a9..cacc5b41cd 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -257,6 +257,7 @@ export function addAccessibilityControl(videoString, videoAttributes, tabIndex = } export function handlePause(event) { + event.stopPropagation(); if (event.code !== 'Enter' && event.code !== 'Space' && !['focus', 'click', 'blur'].includes(event.type)) { return; } From e39ce89d45ea41cd7f5b38109450cfb00642e6c9 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Fri, 18 Oct 2024 18:16:48 +0530 Subject: [PATCH 07/36] hover and focus added --- libs/blocks/video/video.css | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index f957e6c60d..31061fc5c1 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -45,12 +45,22 @@ video { right: 2%; bottom: 2%; margin: 0; + background:#242424; border-radius: 50%; - outline-color: var(--color-accent-focus-ring); z-index: 2; cursor: pointer; } +.video-container .pause-play-wrapper:hover { + background:#0000; +} + +.video-container .pause-play-wrapper:focus-visible { + background:#0000; + border: 3px solid var(--color-white); + outline: var(--color-accent-focus-ring) solid 2px; +} + .editorial-card .video-container, .editorial-card:not(:has(.video-container)) .pause-play-wrapper, .hero-marquee:not(:has(.video-container)) .pause-play-wrapper, From 1bb2332a23bdcb305da626a2446de6ca90feea94 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Thu, 24 Oct 2024 16:02:47 +0530 Subject: [PATCH 08/36] controls positioned for rtl --- libs/blocks/aside/aside.js | 2 +- libs/blocks/figure/figure.js | 2 +- libs/blocks/hero-marquee/hero-marquee.js | 2 +- libs/blocks/video/video.css | 52 ++++++++++++++---------- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/libs/blocks/aside/aside.js b/libs/blocks/aside/aside.js index de5a041f62..b61b860031 100644 --- a/libs/blocks/aside/aside.js +++ b/libs/blocks/aside/aside.js @@ -197,7 +197,7 @@ function decorateLayout(el) { } const foregroundImage = foreground.querySelector(':scope > div:not(.text) img')?.closest('div'); const bgImage = el.querySelector(':scope > div:not(.text):not(.foreground) img')?.closest('div'); - const foregroundMedia = foreground.querySelector(':scope > div:not(.text) .video-container,:scope > div:not(.text) video, :scope > div:not(.text) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div:not(.video-container)'); + const foregroundMedia = foreground.querySelector(':scope > div:not(.text) :is(.video-container, video, a[href*=".mp4"], a[href*="tv.adobe.com"])')?.closest('div:not(.video-container)'); const bgMedia = el.querySelector(':scope > div:not(.text):not(.foreground) video, :scope > div:not(.text):not(.foreground) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div'); const image = foregroundImage ?? bgImage; const asideMedia = foregroundMedia ?? bgMedia ?? image; diff --git a/libs/blocks/figure/figure.js b/libs/blocks/figure/figure.js index 896930f045..c9e462516f 100644 --- a/libs/blocks/figure/figure.js +++ b/libs/blocks/figure/figure.js @@ -21,7 +21,7 @@ function decorateVideo(clone, figEl) { if (anchorTag && !anchorTag.hash) anchorTag.hash = '#autoplay'; if (anchorTag) decorateAnchorVideo({ src: anchorTag.href, anchorTag }); if (videoTag) { - const videoContainer = clone.querySelector('.video-container') || clone.querySelector('.pause-play-wrapper') || clone.querySelector('video'); + const videoContainer = clone.querySelector('.video-container, .pause-play-wrapper, video'); videoTag.removeAttribute('data-mouseevent'); if (videoTag.dataset?.videoSource) { videoTag.appendChild( diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index be1683ca54..302c8376f8 100644 --- a/libs/blocks/hero-marquee/hero-marquee.js +++ b/libs/blocks/hero-marquee/hero-marquee.js @@ -183,7 +183,7 @@ export default async function init(el) { foreground.classList.add('foreground', `cols-${fRows.length}`); let copy = fRows[0]; const anyTag = foreground.querySelector('p, h1, h2, h3, h4, h5, h6'); - const asset = foreground.querySelector('div > picture,div > .video-container,div > .pause-play-wrapper, div > video, div > a[href*=".mp4"], div > a.image-link'); + const asset = foreground.querySelector('div > picture, div > .video-container, div > .pause-play-wrapper, div > video, div > a[href*=".mp4"], div > a.image-link'); const allRows = foreground.querySelectorAll('div > div'); copy = anyTag.closest('div'); copy.classList.add('copy'); diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 31061fc5c1..87392345a7 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -7,20 +7,19 @@ video { height: auto; } - -.pause-play-wrapper img { - min-height:40px !important; +:is(.marquee, .aside.split) .pause-play-wrapper img.accessibility-control { + min-height: 40px; } .video-container{ display: flex; position: relative; - height:100%; + height: 100%; width: fit-content; margin: auto; } -.aside .video-container,.marquee .video-container,.quiz-marquee .video-container { +:is(.aside, .marquee, .quiz-marquee) .video-container { width: auto; } @@ -30,7 +29,6 @@ video { border-radius: inherit; } - .pause-play-wrapper { display: flex; width: fit-content; @@ -38,33 +36,31 @@ video { } .video-container .pause-play-wrapper { - width: 40px; - height: 40px; + width: 40px !important; + height: 40px !important; display: flex; position: absolute; - right: 2%; bottom: 2%; + right: 2%; margin: 0; - background:#242424; + background: #242424; border-radius: 50%; z-index: 2; cursor: pointer; } .video-container .pause-play-wrapper:hover { - background:#0000; + background: #000; } .video-container .pause-play-wrapper:focus-visible { - background:#0000; + background: #000; border: 3px solid var(--color-white); outline: var(--color-accent-focus-ring) solid 2px; } -.editorial-card .video-container, -.editorial-card:not(:has(.video-container)) .pause-play-wrapper, -.hero-marquee:not(:has(.video-container)) .pause-play-wrapper, -.marquee.split:not(:has(.video-container)) .pause-play-wrapper { +:is(.editorial-card, .hero-marquee, .marquee):not(:has(.video-container)) .pause-play-wrapper, +.editorial-card .video-container { width: auto; height: 100%; } @@ -86,7 +82,7 @@ video { width: auto; } -.video-container .pause-play-wrapper img.hidden{ +.video-container .pause-play-wrapper img.hidden { display: none; } @@ -95,9 +91,21 @@ video { } @media (min-width: 900px) { -.media:not(.media-reverse-mobile, .media-reversed) .pause-play-wrapper, -.aside:not(.split-left) .pause-play-wrapper, -.hero-marquee.asset-left .pause-play-wrapper { - left: 2%; -} + .media:not(.media-reverse-mobile, .media-reversed) .video-container .pause-play-wrapper, + .hero-marquee.asset-left .video-container .pause-play-wrapper, + .aside:not(.split) .video-container .pause-play-wrapper { + left: 2%; + } + + [dir="rtl"] :is(.marquee.split, .media:is(.media-reverse-mobile, .media-reversed), + .hero-marquee:not(.asset-left) .foreground-media, .aside.split, + .aside .foreground .image:nth-last-child(1), .brick.media-right) .video-container .pause-play-wrapper { + left: 2%; + right: auto; + } + + .section[class*="-up"] .media .foreground .image:first-child .video-container .pause-play-wrapper { + left: auto; + right: 2%; + } } From 1b7a37927786de7f4abaa5e897070b7309f1c41c Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Thu, 24 Oct 2024 17:43:58 +0530 Subject: [PATCH 09/36] hide-controls hash params added --- libs/utils/decorate.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 8be5ffeade..84fa380541 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -387,18 +387,14 @@ export async function loadCDT(el, classList) { } export function isAccessible(anchorTag) { - const section = anchorTag.closest('div[class="section"]'); - if (section) { - const [block] = Array.from(section.children).filter((element) => element.contains(anchorTag)); - return !block.classList.contains('hide-controls'); - } - return true; + return !anchorTag.hash.includes('hide-controls'); } export function decorateAnchorVideo({ src = '', anchorTag }) { if (!src.length || !(anchorTag instanceof HTMLElement)) return; - if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; const accessibilityEnabled = isAccessible(anchorTag); + anchorTag.hash = anchorTag.hash.replace('#hide-controls', ''); + if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; const { dataset, parentElement } = anchorTag; const attrs = getVideoAttrs(anchorTag.hash, dataset); const tabIndex = anchorTag.tabIndex || 0; From ae4d004ea1286a347bc86d4756d6ae9ca46bfd4a Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Thu, 24 Oct 2024 22:12:00 +0530 Subject: [PATCH 10/36] how to block controls position bug fix --- libs/blocks/how-to/how-to.js | 4 +++- libs/blocks/video/video.css | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libs/blocks/how-to/how-to.js b/libs/blocks/how-to/how-to.js index ce7cf146df..81c81c1960 100644 --- a/libs/blocks/how-to/how-to.js +++ b/libs/blocks/how-to/how-to.js @@ -47,7 +47,9 @@ const setJsonLd = (heading, description, mainImage, stepsLd) => { }; const getImage = (el) => el.querySelector('picture') || el.querySelector('a[href$=".svg"'); -const getVideo = (el) => el.querySelector('video') || el.querySelector('.milo-video'); +const getVideo = (el) =>{ + return el.querySelector('.video-container, .pause-play-wrapper, video, .milo-video'); +} const getHowToInfo = (el) => { const infoDiv = el.querySelector(':scope > div > div'); diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 87392345a7..3038a482a2 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -5,6 +5,7 @@ a[href*='.mp4'].hide-video { video { max-width: 100%; height: auto; + object-fit: cover; } :is(.marquee, .aside.split) .pause-play-wrapper img.accessibility-control { @@ -104,7 +105,8 @@ video { right: auto; } - .section[class*="-up"] .media .foreground .image:first-child .video-container .pause-play-wrapper { + :is(.section[class*="-up"] .media .foreground .image:first-child, .aside .foreground .image:nth-last-child(1)) + .video-container .pause-play-wrapper { left: auto; right: 2%; } From 34db839aed54858fd3cbbc59dcf08dc7635001e3 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Tue, 29 Oct 2024 23:23:39 +0530 Subject: [PATCH 11/36] dark mode|bug fixes --- libs/blocks/video/video.css | 32 ++++++++++++++++++++++++-------- libs/utils/decorate.js | 25 ++++++++++++++++--------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 3038a482a2..643dcb39dd 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -12,7 +12,7 @@ video { min-height: 40px; } -.video-container{ +.video-container { display: flex; position: relative; height: 100%; @@ -37,26 +37,42 @@ video { } .video-container .pause-play-wrapper { - width: 40px !important; - height: 40px !important; - display: flex; + width: 45px !important; + height: 45px !important; position: absolute; bottom: 2%; right: 2%; margin: 0; - background: #242424; + justify-content: center; + align-items: center; border-radius: 50%; z-index: 2; cursor: pointer; } -.video-container .pause-play-wrapper:hover { +.video-container .pause-play-wrapper .offset-filler { + display: inherit; + justify-content: center; + align-items: center; + width: 40px; + height: 40px; + border-radius:inherit; + background: #242424; + border: 2px solid white; + outline: 1px solid white; +} + +.dark .video-container .pause-play-wrapper:focus .offset-filler { + outline: 1px solid #000; +} + +.video-container .pause-play-wrapper .offset-filler:hover { background: #000; + outline: var(--color-accent-focus-ring) solid 2px; } .video-container .pause-play-wrapper:focus-visible { background: #000; - border: 3px solid var(--color-white); outline: var(--color-accent-focus-ring) solid 2px; } @@ -93,7 +109,7 @@ video { @media (min-width: 900px) { .media:not(.media-reverse-mobile, .media-reversed) .video-container .pause-play-wrapper, - .hero-marquee.asset-left .video-container .pause-play-wrapper, + :is(.marquee.row-reversed .asset, .hero-marquee.asset-left) .video-container .pause-play-wrapper, .aside:not(.split) .video-container .pause-play-wrapper { left: 2%; } diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 84fa380541..6a1d8832ff 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -241,16 +241,23 @@ export function syncPausePlayIcon(video) { } export function addAccessibilityControl(videoString, videoAttributes, tabIndex = 0) { + const labels = { + accessibilityLabel: 'play/pause motion', + playLabel: 'play icon', + pauseLabel: 'pause icon' + } if (!videoAttributes.includes('controls')) { if (videoAttributes.includes('hoverplay')) { - return `${videoString} + return `${videoString} `; } return `
${videoString} - - pause icon - play icon - + +
+ ${labels.pauseLabel} + ${labels.playLabel} +
+
`; } return videoString; @@ -290,8 +297,8 @@ export function applyHoverPlay(video) { export function applyAccessibiltyEvents(videoEl) { const pausePlayWrapper = videoEl.parentElement.querySelector('.pause-play-wrapper') || videoEl.closest('.pause-play-wrapper'); if (pausePlayWrapper?.querySelector('.accessibility-control')) { - pausePlayWrapper?.addEventListener('click', handlePause); - pausePlayWrapper?.addEventListener('keydown', handlePause); + pausePlayWrapper.addEventListener('click', handlePause); + pausePlayWrapper.addEventListener('keydown', handlePause); } if (videoEl.hasAttribute('autoplay')) { videoEl.addEventListener('ended', () => { syncPausePlayIcon(videoEl); }); @@ -387,13 +394,13 @@ export async function loadCDT(el, classList) { } export function isAccessible(anchorTag) { - return !anchorTag.hash.includes('hide-controls'); + return !anchorTag.hash.includes('_hide-controls'); } export function decorateAnchorVideo({ src = '', anchorTag }) { if (!src.length || !(anchorTag instanceof HTMLElement)) return; const accessibilityEnabled = isAccessible(anchorTag); - anchorTag.hash = anchorTag.hash.replace('#hide-controls', ''); + anchorTag.hash = anchorTag.hash.replace('#_hide-controls', ''); if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; const { dataset, parentElement } = anchorTag; const attrs = getVideoAttrs(anchorTag.hash, dataset); From b2332535a279e06afbc38576c74af906bd3c2986 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Tue, 29 Oct 2024 23:27:53 +0530 Subject: [PATCH 12/36] code enhancement --- libs/blocks/hero-marquee/hero-marquee.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index 302c8376f8..7197879adb 100644 --- a/libs/blocks/hero-marquee/hero-marquee.js +++ b/libs/blocks/hero-marquee/hero-marquee.js @@ -183,7 +183,7 @@ export default async function init(el) { foreground.classList.add('foreground', `cols-${fRows.length}`); let copy = fRows[0]; const anyTag = foreground.querySelector('p, h1, h2, h3, h4, h5, h6'); - const asset = foreground.querySelector('div > picture, div > .video-container, div > .pause-play-wrapper, div > video, div > a[href*=".mp4"], div > a.image-link'); + const asset = foreground.querySelector('div > picture, :is(.video-container, .pause-play-wrapper), div > video, div > a[href*=".mp4"], div > a.image-link'); const allRows = foreground.querySelectorAll('div > div'); copy = anyTag.closest('div'); copy.classList.add('copy'); From 0ac85c9ce71dcd44c96d46baf93ac5667e65497d Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Wed, 30 Oct 2024 11:51:02 +0530 Subject: [PATCH 13/36] pause-play bug mouse click bug fix --- libs/utils/decorate.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 6a1d8832ff..bd12e6bca1 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -248,10 +248,10 @@ export function addAccessibilityControl(videoString, videoAttributes, tabIndex = } if (!videoAttributes.includes('controls')) { if (videoAttributes.includes('hoverplay')) { - return `${videoString} + return `${videoString} `; } - return `
${videoString} + return `
${videoString}
${labels.pauseLabel} @@ -269,7 +269,7 @@ export function handlePause(event) { return; } event.preventDefault(); - const video = event.target.parentElement.parentElement.querySelector('video'); + const video = event.target.closest('.video-holder').parentElement.querySelector('video'); if (event.type === 'blur') { video.pause(); } else if (video.paused || video.ended || event.type === 'focus') { From 45f7091144ec1e1d2271225504571c635c18b397 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Wed, 30 Oct 2024 16:37:32 +0530 Subject: [PATCH 14/36] marquee dark mode|positioning fix --- libs/blocks/video/video.css | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 643dcb39dd..f9efa69584 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -58,11 +58,13 @@ video { height: 40px; border-radius:inherit; background: #242424; +} + +:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper .offset-filler { border: 2px solid white; - outline: 1px solid white; } -.dark .video-container .pause-play-wrapper:focus .offset-filler { +:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper:focus .offset-filler { outline: 1px solid #000; } @@ -71,6 +73,11 @@ video { outline: var(--color-accent-focus-ring) solid 2px; } +.video-container .pause-play-wrapper:focus .offset-filler { + border: 2px solid white; + outline: 1px solid white; +} + .video-container .pause-play-wrapper:focus-visible { background: #000; outline: var(--color-accent-focus-ring) solid 2px; @@ -109,21 +116,21 @@ video { @media (min-width: 900px) { .media:not(.media-reverse-mobile, .media-reversed) .video-container .pause-play-wrapper, - :is(.marquee.row-reversed .asset, .hero-marquee.asset-left) .video-container .pause-play-wrapper, + :is(.marquee.row-reversed .asset, .marquee-anchors, .hero-marquee.asset-left) .video-container .pause-play-wrapper, .aside:not(.split) .video-container .pause-play-wrapper { left: 2%; } - [dir="rtl"] :is(.marquee.split, .media:is(.media-reverse-mobile, .media-reversed), - .hero-marquee:not(.asset-left) .foreground-media, .aside.split, - .aside .foreground .image:nth-last-child(1), .brick.media-right) .video-container .pause-play-wrapper { - left: 2%; - right: auto; - } - :is(.section[class*="-up"] .media .foreground .image:first-child, .aside .foreground .image:nth-last-child(1)) .video-container .pause-play-wrapper { left: auto; right: 2%; } + + [dir="rtl"] :is(.marquee.split:not(.row-reversed), .media:is(.media-reverse-mobile, .media-reversed), + .hero-marquee:not(.asset-left) :is([class*="foreground"]), .aside.split, + .aside .foreground.container .image:nth-last-child(1), .brick.media-right, .how-to) .video-container .pause-play-wrapper { + left: 2%; + right: auto; + } } From 287ecb1ff6c6bd53281d6d680f43eedfcf8fe0e3 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Mon, 4 Nov 2024 22:37:13 +0530 Subject: [PATCH 15/36] code enhancement --- libs/blocks/figure/figure.css | 1 - libs/blocks/how-to/how-to.js | 4 +--- libs/blocks/video/video.css | 9 +++++++-- libs/utils/decorate.js | 24 +++++++++--------------- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/libs/blocks/figure/figure.css b/libs/blocks/figure/figure.css index c876ea06f8..28075d0fb4 100644 --- a/libs/blocks/figure/figure.css +++ b/libs/blocks/figure/figure.css @@ -69,7 +69,6 @@ .figure .figure { margin: 0 auto; max-width: 800px; - width: fit-content; } .figure-list { diff --git a/libs/blocks/how-to/how-to.js b/libs/blocks/how-to/how-to.js index 81c81c1960..8080319fd0 100644 --- a/libs/blocks/how-to/how-to.js +++ b/libs/blocks/how-to/how-to.js @@ -47,9 +47,7 @@ const setJsonLd = (heading, description, mainImage, stepsLd) => { }; const getImage = (el) => el.querySelector('picture') || el.querySelector('a[href$=".svg"'); -const getVideo = (el) =>{ - return el.querySelector('.video-container, .pause-play-wrapper, video, .milo-video'); -} +const getVideo = (el) => el.querySelector('.video-container, .pause-play-wrapper, video, .milo-video'); const getHowToInfo = (el) => { const infoDiv = el.querySelector(':scope > div > div'); diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index f9efa69584..baa07a0c40 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -56,7 +56,7 @@ video { align-items: center; width: 40px; height: 40px; - border-radius:inherit; + border-radius: inherit; background: #242424; } @@ -73,7 +73,7 @@ video { outline: var(--color-accent-focus-ring) solid 2px; } -.video-container .pause-play-wrapper:focus .offset-filler { +.video-container .pause-play-wrapper:focus-visible .offset-filler { border: 2px solid white; outline: 1px solid white; } @@ -83,6 +83,11 @@ video { outline: var(--color-accent-focus-ring) solid 2px; } +.video-container .pause-play-wrapper .offset-filler.is-playing .play-icon, +.video-container .pause-play-wrapper .offset-filler:not(.is-playing) .pause-icon { + display: none; +} + :is(.editorial-card, .hero-marquee, .marquee):not(:has(.video-container)) .pause-play-wrapper, .editorial-card .video-container { width: auto; diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index bd12e6bca1..4191781274 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -1,6 +1,7 @@ import { createTag, loadStyle, getConfig, createIntersectionObserver } from './utils.js'; const { miloLibs, codeRoot } = getConfig(); +const HIDE_CONTROLS = '_hide-controls'; export function decorateButtons(el, size) { const buttons = el.querySelectorAll('em a, strong a, p > a strong'); @@ -229,15 +230,8 @@ function getVideoAttrs(hash, dataset) { } export function syncPausePlayIcon(video) { - const playIcon = video.nextElementSibling?.querySelector('.play-icon'); - const pauseIcon = video.nextElementSibling?.querySelector('.pause-icon'); - if (video.paused || video.ended) { - playIcon?.classList.remove('hidden'); - pauseIcon?.classList.add('hidden'); - } else { - pauseIcon?.classList.remove('hidden'); - playIcon?.classList.add('hidden'); - } + const offsetFiller= video.closest('.video-holder').querySelector('.offset-filler'); + offsetFiller?.classList.toggle('is-playing'); } export function addAccessibilityControl(videoString, videoAttributes, tabIndex = 0) { @@ -248,14 +242,14 @@ export function addAccessibilityControl(videoString, videoAttributes, tabIndex = } if (!videoAttributes.includes('controls')) { if (videoAttributes.includes('hoverplay')) { - return `${videoString} + return `${videoString} `; } return `
${videoString} - `; @@ -394,13 +388,13 @@ export async function loadCDT(el, classList) { } export function isAccessible(anchorTag) { - return !anchorTag.hash.includes('_hide-controls'); + return !anchorTag.hash.includes(HIDE_CONTROLS); } export function decorateAnchorVideo({ src = '', anchorTag }) { if (!src.length || !(anchorTag instanceof HTMLElement)) return; const accessibilityEnabled = isAccessible(anchorTag); - anchorTag.hash = anchorTag.hash.replace('#_hide-controls', ''); + anchorTag.hash = anchorTag.hash.replace(`#${HIDE_CONTROLS}`, ''); if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; const { dataset, parentElement } = anchorTag; const attrs = getVideoAttrs(anchorTag.hash, dataset); From 51e2de39b92a811656f0b49d711265065845689d Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Mon, 4 Nov 2024 23:56:02 +0530 Subject: [PATCH 16/36] handled marquee backward compatiblity --- libs/blocks/marquee/marquee.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/blocks/marquee/marquee.js b/libs/blocks/marquee/marquee.js index e62546d76b..ca6fbcee71 100644 --- a/libs/blocks/marquee/marquee.js +++ b/libs/blocks/marquee/marquee.js @@ -88,7 +88,7 @@ function decorateSplit(el, foreground, media) { let mediaCreditInner; const txtContent = media?.lastChild?.textContent?.trim(); - if (txtContent?.match(/^http.*\.mp4/) || media.querySelector('video')) return; + if (txtContent?.match(/^http.*\.mp4/) || media?.lastChild?.tagName === 'VIDEO'|| media.querySelector('.video-holder video')) return; if (txtContent) { mediaCreditInner = createTag('p', { class: 'body-s' }, txtContent); } else if (media.lastElementChild?.tagName !== 'PICTURE') { From 90b734f6d45dbcba522a3be667a1f13c72865172 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Tue, 5 Nov 2024 23:40:55 +0530 Subject: [PATCH 17/36] Added placeholder for labels|indexed video aria-labels --- libs/utils/decorate.js | 45 ++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 4191781274..c5d3db9e40 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -1,7 +1,11 @@ import { createTag, loadStyle, getConfig, createIntersectionObserver } from './utils.js'; +import { replaceKeyArray } from '../features/placeholders.js'; +import { getFedsPlaceholderConfig } from '../blocks/global-navigation/utilities/utilities.js'; const { miloLibs, codeRoot } = getConfig(); const HIDE_CONTROLS = '_hide-controls'; +const allVideos = [...document.querySelectorAll('a.video')]; +let labels = {}; export function decorateButtons(el, size) { const buttons = el.querySelectorAll('em a, strong a, p > a strong'); @@ -230,26 +234,32 @@ function getVideoAttrs(hash, dataset) { } export function syncPausePlayIcon(video) { - const offsetFiller= video.closest('.video-holder').querySelector('.offset-filler'); + const offsetFiller = video.closest('.video-holder').querySelector('.offset-filler'); + const anchorTag = video.closest('.video-holder').querySelector('a') offsetFiller?.classList.toggle('is-playing'); + const isPlaying = offsetFiller?.classList.contains('is-playing'); + const ariaLabel = isPlaying ? labels.pauseMotion : labels.playMotion; + anchorTag.setAttribute('aria-label', ariaLabel + " " + anchorTag.getAttribute('video-index')); + anchorTag.setAttribute('aria-pressed', isPlaying ? "true" : "false"); } -export function addAccessibilityControl(videoString, videoAttributes, tabIndex = 0) { - const labels = { - accessibilityLabel: 'play/pause motion', - playLabel: 'play icon', - pauseLabel: 'pause icon' - } +export async function addAccessibilityControl(videoString, videoAttributes, tabIndex = 0, indexOfVideo) { + const [pauseMotion, playMotion, pauseIcon, playIcon] = await replaceKeyArray( + ['pause-motion', 'play-motion', 'pause-icon', 'play-icon'], + getFedsPlaceholderConfig(), + ); + labels = { playMotion, pauseMotion, pauseIcon, playIcon } + let ariaLabel = videoAttributes.includes('autoplay') ? labels.pauseMotion : labels.playMotion; if (!videoAttributes.includes('controls')) { if (videoAttributes.includes('hoverplay')) { - return `${videoString} + return `${videoString} `; } return `
${videoString} - +
- - + ${labels.pauseIcon} + ${labels.playIcon}
`; @@ -257,6 +267,14 @@ export function addAccessibilityControl(videoString, videoAttributes, tabIndex = return videoString; } +function getVideoIndex(anchorTag) { + const index = allVideos.findIndex((video) => video === anchorTag); + if (index === 0 && allVideos.length === 1) { + return ""; + } + return index + 1; +} + export function handlePause(event) { event.stopPropagation(); if (event.code !== 'Enter' && event.code !== 'Space' && !['focus', 'click', 'blur'].includes(event.type)) { @@ -391,7 +409,7 @@ export function isAccessible(anchorTag) { return !anchorTag.hash.includes(HIDE_CONTROLS); } -export function decorateAnchorVideo({ src = '', anchorTag }) { +export async function decorateAnchorVideo({ src = '', anchorTag }) { if (!src.length || !(anchorTag instanceof HTMLElement)) return; const accessibilityEnabled = isAccessible(anchorTag); anchorTag.hash = anchorTag.hash.replace(`#${HIDE_CONTROLS}`, ''); @@ -401,8 +419,9 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { const tabIndex = anchorTag.tabIndex || 0; const videoIndex = (tabIndex === -1) ? 'tabindex=-1' : ''; let video = ``; + const indexOfVideo = getVideoIndex(anchorTag); if (accessibilityEnabled) { - video = addAccessibilityControl(video, attrs, tabIndex); + video = await addAccessibilityControl(video, attrs, tabIndex, indexOfVideo); } anchorTag.insertAdjacentHTML('afterend', video); const videoEl = parentElement.querySelector('video'); From a54bbc307b10d2a38bc9a24f85e82ee9f7331d41 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Wed, 6 Nov 2024 00:09:20 +0530 Subject: [PATCH 18/36] aria-label added for hover play videos --- libs/utils/decorate.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index c5d3db9e40..516b4a257f 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -235,12 +235,12 @@ function getVideoAttrs(hash, dataset) { export function syncPausePlayIcon(video) { const offsetFiller = video.closest('.video-holder').querySelector('.offset-filler'); - const anchorTag = video.closest('.video-holder').querySelector('a') + const anchorTag = video.closest('.video-holder').querySelector('a'); offsetFiller?.classList.toggle('is-playing'); const isPlaying = offsetFiller?.classList.contains('is-playing'); const ariaLabel = isPlaying ? labels.pauseMotion : labels.playMotion; - anchorTag.setAttribute('aria-label', ariaLabel + " " + anchorTag.getAttribute('video-index')); - anchorTag.setAttribute('aria-pressed', isPlaying ? "true" : "false"); + anchorTag?.setAttribute('aria-label', ariaLabel + " " + anchorTag.getAttribute('video-index')); + anchorTag?.setAttribute('aria-pressed', isPlaying ? "true" : "false"); } export async function addAccessibilityControl(videoString, videoAttributes, tabIndex = 0, indexOfVideo) { @@ -252,7 +252,7 @@ export async function addAccessibilityControl(videoString, videoAttributes, tabI let ariaLabel = videoAttributes.includes('autoplay') ? labels.pauseMotion : labels.playMotion; if (!videoAttributes.includes('controls')) { if (videoAttributes.includes('hoverplay')) { - return `${videoString} + return `${videoString} `; } return `
${videoString} From 2707f51994c104fe7087b6b11932ca49873b1cb1 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Wed, 6 Nov 2024 23:13:56 +0530 Subject: [PATCH 19/36] async awaited decorateVideo in video.js and other linting errors --- libs/blocks/marquee/marquee.js | 2 +- libs/blocks/video/video.js | 4 ++-- libs/utils/decorate.js | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libs/blocks/marquee/marquee.js b/libs/blocks/marquee/marquee.js index ca6fbcee71..21c3e8c8a5 100644 --- a/libs/blocks/marquee/marquee.js +++ b/libs/blocks/marquee/marquee.js @@ -88,7 +88,7 @@ function decorateSplit(el, foreground, media) { let mediaCreditInner; const txtContent = media?.lastChild?.textContent?.trim(); - if (txtContent?.match(/^http.*\.mp4/) || media?.lastChild?.tagName === 'VIDEO'|| media.querySelector('.video-holder video')) return; + if (txtContent?.match(/^http.*\.mp4/) || media?.lastChild?.tagName === 'VIDEO' || media.querySelector('.video-holder video')) return; if (txtContent) { mediaCreditInner = createTag('p', { class: 'body-s' }, txtContent); } else if (media.lastElementChild?.tagName !== 'PICTURE') { diff --git a/libs/blocks/video/video.js b/libs/blocks/video/video.js index 3decc59e15..c5cf9a1db0 100644 --- a/libs/blocks/video/video.js +++ b/libs/blocks/video/video.js @@ -1,7 +1,7 @@ import { getConfig } from '../../utils/utils.js'; import { decorateAnchorVideo } from '../../utils/decorate.js'; -export default function init(a) { +export default async function init(a) { a.classList.add('hide-video'); if (!a.parentNode) { a.remove(); @@ -17,7 +17,7 @@ export default function init(a) { const mediaFilename = pathname.split('/').pop(); videoPath = `${root}${mediaFilename}`; } - decorateAnchorVideo({ + await decorateAnchorVideo({ src: videoPath, anchorTag: a, }); diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 516b4a257f..304246372b 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -239,25 +239,25 @@ export function syncPausePlayIcon(video) { offsetFiller?.classList.toggle('is-playing'); const isPlaying = offsetFiller?.classList.contains('is-playing'); const ariaLabel = isPlaying ? labels.pauseMotion : labels.playMotion; - anchorTag?.setAttribute('aria-label', ariaLabel + " " + anchorTag.getAttribute('video-index')); - anchorTag?.setAttribute('aria-pressed', isPlaying ? "true" : "false"); + anchorTag?.setAttribute('aria-label', `${ariaLabel} ${anchorTag.getAttribute('video-index')}`); + anchorTag?.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); } -export async function addAccessibilityControl(videoString, videoAttributes, tabIndex = 0, indexOfVideo) { +export async function addAccessibilityControl(videoString, videoAttrs, indexOfVideo, tabIndex = 0) { const [pauseMotion, playMotion, pauseIcon, playIcon] = await replaceKeyArray( ['pause-motion', 'play-motion', 'pause-icon', 'play-icon'], getFedsPlaceholderConfig(), ); - labels = { playMotion, pauseMotion, pauseIcon, playIcon } - let ariaLabel = videoAttributes.includes('autoplay') ? labels.pauseMotion : labels.playMotion; - if (!videoAttributes.includes('controls')) { - if (videoAttributes.includes('hoverplay')) { - return `${videoString} - `; + labels = { playMotion, pauseMotion, pauseIcon, playIcon }; + const ariaLabel = videoAttrs.includes('autoplay') ? labels.pauseMotion : labels.playMotion; + if (!videoAttrs.includes('controls')) { + if (videoAttrs.includes('hoverplay')) { + return `${videoString} `; } return `
${videoString} -
+
${labels.pauseIcon} ${labels.playIcon}
@@ -270,7 +270,7 @@ export async function addAccessibilityControl(videoString, videoAttributes, tabI function getVideoIndex(anchorTag) { const index = allVideos.findIndex((video) => video === anchorTag); if (index === 0 && allVideos.length === 1) { - return ""; + return ''; } return index + 1; } @@ -421,7 +421,7 @@ export async function decorateAnchorVideo({ src = '', anchorTag }) { let video = ``; const indexOfVideo = getVideoIndex(anchorTag); if (accessibilityEnabled) { - video = await addAccessibilityControl(video, attrs, tabIndex, indexOfVideo); + video = await addAccessibilityControl(video, attrs, indexOfVideo, tabIndex); } anchorTag.insertAdjacentHTML('afterend', video); const videoEl = parentElement.querySelector('video'); From 4444c4cc00c626049b92a78f6d3d7ce33aec4e0e Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Thu, 7 Nov 2024 15:03:15 +0530 Subject: [PATCH 20/36] video indexes added --- libs/blocks/adobetv/adobetv.js | 5 +- libs/blocks/video/video.css | 5 + libs/blocks/video/video.js | 1 + libs/utils/decorate.js | 39 +++++--- libs/utils/utils.js | 9 +- test/blocks/adobetv/adobetv.test.js | 6 +- test/blocks/how-to/how-to.test.js | 2 +- test/blocks/how-to/mocks/body.html | 2 +- test/blocks/video/mocks/body.html | 9 +- test/blocks/video/video.test.js | 138 ++++++++++++++++++++++++---- 10 files changed, 177 insertions(+), 39 deletions(-) diff --git a/libs/blocks/adobetv/adobetv.js b/libs/blocks/adobetv/adobetv.js index 85928d1c03..27e26aeff7 100644 --- a/libs/blocks/adobetv/adobetv.js +++ b/libs/blocks/adobetv/adobetv.js @@ -1,14 +1,15 @@ import { decorateAnchorVideo } from '../../utils/decorate.js'; -export default function init(a) { +export default async function init(a) { a.classList.add('hide-video'); const bgBlocks = ['aside', 'marquee', 'hero-marquee']; if (a.href.includes('.mp4') && bgBlocks.some((b) => a.closest(`.${b}`))) { a.classList.add('hide'); if (!a.parentNode) return; - decorateAnchorVideo({ + await decorateAnchorVideo({ src: a.href, anchorTag: a, + indexOfVideo: a.getAttribute('indexOfBlock'), }); } else { const embed = `
diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index baa07a0c40..f7b9384928 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -126,6 +126,11 @@ video { left: 2%; } + [dir="rtl"] .brick .brick-media:not(:has(.video-container)) .pause-play-wrapper { + border-top-left-radius: inherit; + border-bottom-left-radius: inherit; + } + :is(.section[class*="-up"] .media .foreground .image:first-child, .aside .foreground .image:nth-last-child(1)) .video-container .pause-play-wrapper { left: auto; diff --git a/libs/blocks/video/video.js b/libs/blocks/video/video.js index c5cf9a1db0..7c3b0dd0dd 100644 --- a/libs/blocks/video/video.js +++ b/libs/blocks/video/video.js @@ -20,5 +20,6 @@ export default async function init(a) { await decorateAnchorVideo({ src: videoPath, anchorTag: a, + indexOfVideo: a.getAttribute('indexOfBlock'), }); } diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 304246372b..c5cd45a1b6 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -4,7 +4,8 @@ import { getFedsPlaceholderConfig } from '../blocks/global-navigation/utilities/ const { miloLibs, codeRoot } = getConfig(); const HIDE_CONTROLS = '_hide-controls'; -const allVideos = [...document.querySelectorAll('a.video')]; +let firstVideo = null; +let maxVideo = 0; let labels = {}; export function decorateButtons(el, size) { @@ -249,14 +250,14 @@ export async function addAccessibilityControl(videoString, videoAttrs, indexOfVi getFedsPlaceholderConfig(), ); labels = { playMotion, pauseMotion, pauseIcon, playIcon }; - const ariaLabel = videoAttrs.includes('autoplay') ? labels.pauseMotion : labels.playMotion; + const ariaLabel = `${videoAttrs.includes('autoplay') ? labels.pauseMotion : labels.playMotion} ${indexOfVideo === '1' ? '' : indexOfVideo}`.trim(); if (!videoAttrs.includes('controls')) { if (videoAttrs.includes('hoverplay')) { - return `${videoString} `; } return `
${videoString} - +
${labels.pauseIcon} ${labels.playIcon} @@ -267,14 +268,6 @@ export async function addAccessibilityControl(videoString, videoAttrs, indexOfVi return videoString; } -function getVideoIndex(anchorTag) { - const index = allVideos.findIndex((video) => video === anchorTag); - if (index === 0 && allVideos.length === 1) { - return ''; - } - return index + 1; -} - export function handlePause(event) { event.stopPropagation(); if (event.code !== 'Enter' && event.code !== 'Space' && !['focus', 'click', 'blur'].includes(event.type)) { @@ -409,7 +402,19 @@ export function isAccessible(anchorTag) { return !anchorTag.hash.includes(HIDE_CONTROLS); } -export async function decorateAnchorVideo({ src = '', anchorTag }) { +function updateFirstVideo() { + if (firstVideo != null && firstVideo?.controls === false && maxVideo > 1) { + let videoHolder = firstVideo.closest('.video-holder'); + if (videoHolder.nodeName !== 'A') { + videoHolder = videoHolder.querySelector('a.pause-play-wrapper'); + } + const firstVideoLabel = videoHolder.getAttribute('aria-label'); + videoHolder.setAttribute('aria-label', `${firstVideoLabel} 1`); + firstVideo = null; + } +} + +export async function decorateAnchorVideo({ src = '', anchorTag, indexOfVideo }) { if (!src.length || !(anchorTag instanceof HTMLElement)) return; const accessibilityEnabled = isAccessible(anchorTag); anchorTag.hash = anchorTag.hash.replace(`#${HIDE_CONTROLS}`, ''); @@ -419,12 +424,18 @@ export async function decorateAnchorVideo({ src = '', anchorTag }) { const tabIndex = anchorTag.tabIndex || 0; const videoIndex = (tabIndex === -1) ? 'tabindex=-1' : ''; let video = ``; - const indexOfVideo = getVideoIndex(anchorTag); if (accessibilityEnabled) { video = await addAccessibilityControl(video, attrs, indexOfVideo, tabIndex); } anchorTag.insertAdjacentHTML('afterend', video); + if (indexOfVideo > maxVideo) { + maxVideo = indexOfVideo; + } const videoEl = parentElement.querySelector('video'); + if (indexOfVideo === '1') { + firstVideo = videoEl; + } + updateFirstVideo(); createIntersectionObserver({ el: videoEl, options: { rootMargin: '1000px' }, diff --git a/libs/utils/utils.js b/libs/utils/utils.js index abe839d943..eee7bd0bcf 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -113,6 +113,7 @@ const DO_NOT_INLINE = [ 'columns', 'z-pattern', ]; +let videoBlockCounter = 0; const ENVS = { stage: { @@ -1265,7 +1266,13 @@ async function processSection(section, config, isDoc) { const loadBlocks = [...stylePromises]; if (section.preloadLinks.length) { const [modals, blocks] = partition(section.preloadLinks, (block) => block.classList.contains('modal')); - await Promise.all(blocks.map((block) => loadBlock(block))); + await Promise.all(blocks.map((block) => { + if (block.innerHTML.includes('mp4')) { + videoBlockCounter += 1; + block.setAttribute('indexOfBlock', videoBlockCounter); + } + return loadBlock(block); + })); modals.forEach((block) => loadBlock(block)); } diff --git a/test/blocks/adobetv/adobetv.test.js b/test/blocks/adobetv/adobetv.test.js index a54528e496..223622c7bd 100644 --- a/test/blocks/adobetv/adobetv.test.js +++ b/test/blocks/adobetv/adobetv.test.js @@ -1,8 +1,10 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; import { waitForElement } from '../../helpers/waitfor.js'; +import { setConfig } from '../../../libs/utils/utils.js'; document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +setConfig({}); const { default: init } = await import('../../../libs/blocks/adobetv/adobetv.js'); describe('adobetv autoblock', () => { @@ -10,7 +12,7 @@ describe('adobetv autoblock', () => { const wrapper = document.body.querySelector('.adobe-tv'); const a = wrapper.querySelector(':scope > a'); - init(a); + await init(a); const iframe = await waitForElement('.adobe-tv iframe'); expect(wrapper.querySelector(':scope > a')).to.be.null; expect(iframe).to.be.exist; @@ -20,7 +22,7 @@ describe('adobetv autoblock', () => { const wrapper = document.body.querySelector('#adobetvAsBg'); const a = wrapper.querySelector(':scope a[href*=".mp4"]'); - init(a); + await init(a); const video = await waitForElement('#adobetvAsBg video'); expect(wrapper.querySelector(':scope a[href*=".mp4"]')).to.be.null; expect(video).to.be.exist; diff --git a/test/blocks/how-to/how-to.test.js b/test/blocks/how-to/how-to.test.js index fb20b1f962..1e0207d3cf 100644 --- a/test/blocks/how-to/how-to.test.js +++ b/test/blocks/how-to/how-to.test.js @@ -105,7 +105,7 @@ describe('How To', () => { it('Mp4 Link video', async () => { const howTo = document.getElementById('test6'); - videoinit(howTo.querySelector('a')); + await videoinit(howTo.querySelector('a')); init(howTo); const video = howTo.querySelector('video'); expect(video).to.exist; diff --git a/test/blocks/how-to/mocks/body.html b/test/blocks/how-to/mocks/body.html index ff4fe17059..1c2525625c 100644 --- a/test/blocks/how-to/mocks/body.html +++ b/test/blocks/how-to/mocks/body.html @@ -117,7 +117,7 @@

How to compress a PDF online (

diff --git a/test/blocks/video/mocks/body.html b/test/blocks/video/mocks/body.html index b3f1963c85..1c59b91e82 100644 --- a/test/blocks/video/mocks/body.html +++ b/test/blocks/video/mocks/body.html @@ -16,7 +16,7 @@
-
+ @@ -86,4 +86,9 @@ https://main--milo--adobecom.hlx.page/media_1e798d01c6ddc7e7eadc8f134d69e4f8d7193fdbb6.mp4#autoplay1#_hoverplay#viewportplay
+ diff --git a/test/blocks/video/video.test.js b/test/blocks/video/video.test.js index e55c4e2915..eaaccc6073 100644 --- a/test/blocks/video/video.test.js +++ b/test/blocks/video/video.test.js @@ -4,7 +4,7 @@ import sinon from 'sinon'; import { waitFor, waitForElement } from '../../helpers/waitfor.js'; import { setConfig, createTag } from '../../../libs/utils/utils.js'; -import { decorateAnchorVideo } from '../../../libs/utils/decorate.js'; +import { decorateAnchorVideo, handlePause, applyHoverPlay } from '../../../libs/utils/decorate.js'; setConfig({}); const { default: init } = await import('../../../libs/blocks/video/video.js'); @@ -41,7 +41,7 @@ describe('video uploaded using franklin bot', () => { a.textContent = href; block.append(a); - init(a); + await init(a); const video = await waitForElement('.video.normal video'); expect(video).to.exist; }); @@ -53,7 +53,7 @@ describe('video uploaded using franklin bot', () => { a.textContent = href; block.append(a); - init(a); + await init(a); const video = await waitForElement('.video.autoplay video'); expect(video.hasAttribute('autoplay')).to.be.true; }); @@ -65,7 +65,7 @@ describe('video uploaded using franklin bot', () => { a.textContent = href; block.append(a); - init(a); + await init(a); const video = await waitForElement('.video.no-loop video'); expect(video.hasAttribute('loop')).to.be.false; }); @@ -77,7 +77,7 @@ describe('video uploaded using franklin bot', () => { a.textContent = href; block.append(a); - init(a); + await init(a); const video = await waitForElement('.video.no-loop.hoverplay video'); expect(video.hasAttribute('loop')).to.be.false; expect(video.hasAttribute('data-hoverplay')).to.be.true; @@ -88,7 +88,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - init(a); + await init(a); const video = block.querySelector('video'); expect(video.hasAttribute('loop')).to.be.true; expect(video.hasAttribute('data-hoverplay')).to.be.false; @@ -99,7 +99,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - init(a); + await init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-hoverplay')).to.be.true; }); @@ -109,11 +109,117 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - init(a); + await init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.true; }); + it('accessibility controls should pause autoplay videos', async () => { + const block = document.querySelector('.video.autoplay.viewportplay'); + const fetchStub = sinon.stub(window, 'fetch'); + fetchStub.resolves({ + total: 19, + offset: 0, + limit: 19, + data: [ + { + key: 'play-motion', + value: 'Play', + }, + { + key: 'pause-motion', + value: 'Pause', + }, + { + key: 'play-icon', + value: 'play icon', + }, + { + key: 'pause-icon', + value: 'pause icon', + }, + ], + ':type': 'sheet', + }); + const a = block.querySelector('a'); + block.append(a); + await init(a); + await new Promise((resolve) => { setTimeout(resolve, 50); }); + const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); + const video = block.querySelector('video'); + pausePlayWrapper.click(); + expect(video.paused).to.be.true; + }); + + it('accessibility controls should play autoplay videos after pausing', async () => { + const block = document.querySelector('.video.autoplay.viewportplay'); + const a = block.querySelector('a'); + block.append(a); + await init(a); + await new Promise((resolve) => { setTimeout(resolve, 50); }); + const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); + const video = block.querySelector('video'); + pausePlayWrapper.click(); + await new Promise((resolve) => { setTimeout(resolve, 50); }); + await pausePlayWrapper.click(); + expect(video.paused).to.be.false; + }); + + it('aria-label should not have index when page has only one video', async () => { + const block = document.querySelector('.video.autoplay.single'); + const a = block.querySelector('a'); + a.setAttribute('indexOfBlock', 1); + block.append(a); + await init(a); + await new Promise((resolve) => { setTimeout(resolve, 50); }); + const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); + const airaLabel = pausePlayWrapper.getAttribute('aria-label'); + expect(airaLabel).to.equals('Pause'); + }); + + it('aria-label should have index when page has only one video', async () => { + const block = document.querySelector('.video.autoplay.single'); + const block2 = document.querySelector('.video.autoplay.second'); + const a = block.querySelector('a'); + const a2 = block2.querySelector('a'); + a.setAttribute('indexOfBlock', 1); + a2.setAttribute('indexOfBlock', 2); + block.append(a); + block2.append(a2); + await init(a); + await init(a2); + await new Promise((resolve) => { setTimeout(resolve, 50); }); + const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); + const airaLabel = pausePlayWrapper.getAttribute('aria-label'); + expect(airaLabel).to.equals('Pause 1'); + }); + + it('handlePause should return undefined if called with unknown event', async () => { + const event = {}; + event.stopPropagation = sinon.stub(); + const x = handlePause(event); + expect(x).to.be.undefined; + }); + + it('video should be paused on focus out or blur', async () => { + const block = document.querySelector('.video.autoplay1.hoverplay.no-viewportplay'); + const a = block.querySelector('a'); + block.append(a); + await init(a); + await new Promise((resolve) => { setTimeout(resolve, 50); }); + const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); + const video = block.querySelector('video'); + pausePlayWrapper.focus(); + await new Promise((resolve) => { setTimeout(resolve, 50); }); + pausePlayWrapper.blur(); + expect(video.paused).to.be.true; + }); + + it('should return undefined if video is not present', async () => { + const returnValue = applyHoverPlay(); + expect(returnValue).to.be.undefined; + }); + it('play video when element reached 80% viewport', async () => { const block = document.querySelector('.video.autoplay.viewportplay.scrolled-80'); const a = block.querySelector('a'); @@ -122,7 +228,7 @@ describe('video uploaded using franklin bot', () => { requestAnimationFrame(resolve); }); - init(a); + await init(a); const video = block.querySelector('video'); const playSpy = sinon.spy(video, 'play'); const pauseSpy = sinon.spy(video, 'pause'); @@ -156,7 +262,7 @@ describe('video uploaded using franklin bot', () => { requestAnimationFrame(resolve); }); - init(a); + await init(a); const video = block.querySelector('video'); const playSpy = sinon.spy(video, 'play'); const pauseSpy = sinon.spy(video, 'pause'); @@ -179,7 +285,7 @@ describe('video uploaded using franklin bot', () => { await nextFrame(); await new Promise((resolve) => { - setTimeout(resolve, 100); + setTimeout(resolve, 200); }); assert.isTrue(pauseSpy.calledOnce); video.dispatchEvent(new Event('ended')); @@ -203,7 +309,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - init(a); + await init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.true; }); @@ -213,7 +319,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - init(a); + await init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.false; }); @@ -223,7 +329,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - init(a); + await init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.false; }); @@ -233,7 +339,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - init(a); + await init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.false; }); @@ -243,7 +349,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - init(a); + await init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.false; }); From f268e20469e3b31ccb725733bec14f607853b919 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Sat, 9 Nov 2024 23:02:38 +0530 Subject: [PATCH 21/36] random video index and unit test cases updated --- libs/blocks/adobetv/adobetv.js | 5 +- libs/blocks/figure/figure.js | 7 +- libs/blocks/video/video.js | 5 +- libs/utils/decorate.js | 86 ++++++++++++++--------- libs/utils/utils.js | 9 +-- test/blocks/figure/figure.test.js | 3 +- test/blocks/video/video.test.js | 111 +++++++++++++++--------------- 7 files changed, 119 insertions(+), 107 deletions(-) diff --git a/libs/blocks/adobetv/adobetv.js b/libs/blocks/adobetv/adobetv.js index 27e26aeff7..85928d1c03 100644 --- a/libs/blocks/adobetv/adobetv.js +++ b/libs/blocks/adobetv/adobetv.js @@ -1,15 +1,14 @@ import { decorateAnchorVideo } from '../../utils/decorate.js'; -export default async function init(a) { +export default function init(a) { a.classList.add('hide-video'); const bgBlocks = ['aside', 'marquee', 'hero-marquee']; if (a.href.includes('.mp4') && bgBlocks.some((b) => a.closest(`.${b}`))) { a.classList.add('hide'); if (!a.parentNode) return; - await decorateAnchorVideo({ + decorateAnchorVideo({ src: a.href, anchorTag: a, - indexOfVideo: a.getAttribute('indexOfBlock'), }); } else { const embed = `
diff --git a/libs/blocks/figure/figure.js b/libs/blocks/figure/figure.js index c9e462516f..d4ca6a1e60 100644 --- a/libs/blocks/figure/figure.js +++ b/libs/blocks/figure/figure.js @@ -1,4 +1,4 @@ -import { applyHoverPlay, decorateAnchorVideo, applyAccessibiltyEvents } from '../../utils/decorate.js'; +import { applyHoverPlay, decorateAnchorVideo, applyAccessibiltyEvents, decoratePausePlayWrapper } from '../../utils/decorate.js'; import { createTag } from '../../utils/utils.js'; function buildCaption(pEl) { @@ -32,7 +32,10 @@ function decorateVideo(clone, figEl) { ); } applyHoverPlay(videoTag); - applyAccessibiltyEvents(videoTag); + if (!videoTag.controls) { + applyAccessibiltyEvents(videoTag); + decoratePausePlayWrapper(videoTag, 'autoplay'); + } figEl.prepend(videoContainer); } } diff --git a/libs/blocks/video/video.js b/libs/blocks/video/video.js index 7c3b0dd0dd..3decc59e15 100644 --- a/libs/blocks/video/video.js +++ b/libs/blocks/video/video.js @@ -1,7 +1,7 @@ import { getConfig } from '../../utils/utils.js'; import { decorateAnchorVideo } from '../../utils/decorate.js'; -export default async function init(a) { +export default function init(a) { a.classList.add('hide-video'); if (!a.parentNode) { a.remove(); @@ -17,9 +17,8 @@ export default async function init(a) { const mediaFilename = pathname.split('/').pop(); videoPath = `${root}${mediaFilename}`; } - await decorateAnchorVideo({ + decorateAnchorVideo({ src: videoPath, anchorTag: a, - indexOfVideo: a.getAttribute('indexOfBlock'), }); } diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index c5cd45a1b6..2935caccd6 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -5,9 +5,8 @@ import { getFedsPlaceholderConfig } from '../blocks/global-navigation/utilities/ const { miloLibs, codeRoot } = getConfig(); const HIDE_CONTROLS = '_hide-controls'; let firstVideo = null; -let maxVideo = 0; -let labels = {}; - +let labels = null; +let videoCounter = 0; export function decorateButtons(el, size) { const buttons = el.querySelectorAll('em a, strong a, p > a strong'); if (buttons.length === 0) return; @@ -209,7 +208,7 @@ export function getImgSrc(pic) { return source?.srcset ? `poster='${source.srcset}'` : ''; } -function getVideoAttrs(hash, dataset) { +export function getVideoAttrs(hash, dataset) { const isAutoplay = hash?.includes('autoplay'); const isAutoplayOnce = hash?.includes('autoplay1'); const playOnHover = hash?.includes('hoverplay'); @@ -235,32 +234,28 @@ function getVideoAttrs(hash, dataset) { } export function syncPausePlayIcon(video) { - const offsetFiller = video.closest('.video-holder').querySelector('.offset-filler'); - const anchorTag = video.closest('.video-holder').querySelector('a'); - offsetFiller?.classList.toggle('is-playing'); - const isPlaying = offsetFiller?.classList.contains('is-playing'); - const ariaLabel = isPlaying ? labels.pauseMotion : labels.playMotion; - anchorTag?.setAttribute('aria-label', `${ariaLabel} ${anchorTag.getAttribute('video-index')}`); - anchorTag?.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); + if (!video.getAttributeNames().join().includes('hoverplay')) { + const offsetFiller = video.closest('.video-holder').querySelector('.offset-filler'); + const anchorTag = video.closest('.video-holder').querySelector('a'); + offsetFiller?.classList.toggle('is-playing'); + const isPlaying = offsetFiller?.classList.contains('is-playing'); + const indexOfVideo = (anchorTag.getAttribute('video-index') === '1' && videoCounter === 1) ? '' : anchorTag.getAttribute('video-index'); + const ariaLabel = `${isPlaying ? labels?.pauseMotion : labels?.playMotion} ${indexOfVideo}`.trim(); + anchorTag?.setAttribute('aria-label', `${ariaLabel} `); + anchorTag?.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); + } } -export async function addAccessibilityControl(videoString, videoAttrs, indexOfVideo, tabIndex = 0) { - const [pauseMotion, playMotion, pauseIcon, playIcon] = await replaceKeyArray( - ['pause-motion', 'play-motion', 'pause-icon', 'play-icon'], - getFedsPlaceholderConfig(), - ); - labels = { playMotion, pauseMotion, pauseIcon, playIcon }; - const ariaLabel = `${videoAttrs.includes('autoplay') ? labels.pauseMotion : labels.playMotion} ${indexOfVideo === '1' ? '' : indexOfVideo}`.trim(); +export function addAccessibilityControl(videoString, videoAttrs, indexOfVideo, tabIndex = 0) { if (!videoAttrs.includes('controls')) { if (videoAttrs.includes('hoverplay')) { - return `${videoString} `; + return `${videoString} `; } return `
${videoString} - +
- ${labels.pauseIcon} - ${labels.playIcon} + +
`; @@ -403,8 +398,8 @@ export function isAccessible(anchorTag) { } function updateFirstVideo() { - if (firstVideo != null && firstVideo?.controls === false && maxVideo > 1) { - let videoHolder = firstVideo.closest('.video-holder'); + if (firstVideo != null && firstVideo?.controls === false && videoCounter > 1) { + let videoHolder = document.querySelector('[video-index="1"]') || firstVideo.closest('.video-holder'); if (videoHolder.nodeName !== 'A') { videoHolder = videoHolder.querySelector('a.pause-play-wrapper'); } @@ -414,7 +409,29 @@ function updateFirstVideo() { } } -export async function decorateAnchorVideo({ src = '', anchorTag, indexOfVideo }) { +function updateAriaLabel(videoEl, videoAttrs) { + if (!videoEl.getAttributeNames().join().includes('hoverplay')) { + const pausePlayWrapper = videoEl.parentElement.querySelector('.pause-play-wrapper') || videoEl.closest('.pause-play-wrapper'); + const indexOfVideo = pausePlayWrapper.getAttribute('video-index'); + const ariaLabel = `${videoAttrs.includes('autoplay') ? labels.pauseMotion : labels.playMotion} ${indexOfVideo === '1' && videoCounter === 1 ? '' : indexOfVideo}`.trim(); + pausePlayWrapper.setAttribute('aria-label', ariaLabel); + updateFirstVideo(); + } +} + +export function decoratePausePlayWrapper(videoEl, videoAttrs) { + if (labels === null) { + replaceKeyArray(['pause-motion', 'play-motion', 'pause-icon', 'play-icon'], getFedsPlaceholderConfig()) + .then(([pauseMotion, playMotion, pauseIcon, playIcon]) => { + labels = { playMotion, pauseMotion, pauseIcon, playIcon }; + updateAriaLabel(videoEl, videoAttrs); + }); + } else { + updateAriaLabel(videoEl, videoAttrs); + } +} + +export function decorateAnchorVideo({ src = '', anchorTag }) { if (!src.length || !(anchorTag instanceof HTMLElement)) return; const accessibilityEnabled = isAccessible(anchorTag); anchorTag.hash = anchorTag.hash.replace(`#${HIDE_CONTROLS}`, ''); @@ -424,18 +441,16 @@ export async function decorateAnchorVideo({ src = '', anchorTag, indexOfVideo }) const tabIndex = anchorTag.tabIndex || 0; const videoIndex = (tabIndex === -1) ? 'tabindex=-1' : ''; let video = ``; + videoCounter += 1; + const indexOfVideo = videoCounter; if (accessibilityEnabled) { - video = await addAccessibilityControl(video, attrs, indexOfVideo, tabIndex); + video = addAccessibilityControl(video, attrs, indexOfVideo, tabIndex); } anchorTag.insertAdjacentHTML('afterend', video); - if (indexOfVideo > maxVideo) { - maxVideo = indexOfVideo; - } const videoEl = parentElement.querySelector('video'); - if (indexOfVideo === '1') { + if (indexOfVideo === 1) { firstVideo = videoEl; } - updateFirstVideo(); createIntersectionObserver({ el: videoEl, options: { rootMargin: '1000px' }, @@ -443,7 +458,12 @@ export async function decorateAnchorVideo({ src = '', anchorTag, indexOfVideo }) videoEl?.appendChild(createTag('source', { src, type: 'video/mp4' })); }, }); - if (accessibilityEnabled) applyAccessibiltyEvents(videoEl); + if (accessibilityEnabled) { + applyAccessibiltyEvents(videoEl); + if (!videoEl.controls) { + decoratePausePlayWrapper(videoEl, attrs); + } + } applyHoverPlay(videoEl); applyInViewPortPlay(videoEl); anchorTag.remove(); diff --git a/libs/utils/utils.js b/libs/utils/utils.js index eee7bd0bcf..abe839d943 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -113,7 +113,6 @@ const DO_NOT_INLINE = [ 'columns', 'z-pattern', ]; -let videoBlockCounter = 0; const ENVS = { stage: { @@ -1266,13 +1265,7 @@ async function processSection(section, config, isDoc) { const loadBlocks = [...stylePromises]; if (section.preloadLinks.length) { const [modals, blocks] = partition(section.preloadLinks, (block) => block.classList.contains('modal')); - await Promise.all(blocks.map((block) => { - if (block.innerHTML.includes('mp4')) { - videoBlockCounter += 1; - block.setAttribute('indexOfBlock', videoBlockCounter); - } - return loadBlock(block); - })); + await Promise.all(blocks.map((block) => loadBlock(block))); modals.forEach((block) => loadBlock(block)); } diff --git a/test/blocks/figure/figure.test.js b/test/blocks/figure/figure.test.js index 61ee37b909..6f9d7ab0bf 100644 --- a/test/blocks/figure/figure.test.js +++ b/test/blocks/figure/figure.test.js @@ -1,10 +1,11 @@ import { expect } from '@esm-bundle/chai'; import { readFile } from '@web/test-runner-commands'; +import { setConfig } from '../../../libs/utils/utils.js'; document.body.innerHTML = await readFile({ path: './mocks/body.html' }); const ogDocument = document.body.innerHTML; - const { default: init } = await import('../../../libs/blocks/figure/figure.js'); +setConfig({}); describe('init', () => { afterEach(() => { diff --git a/test/blocks/video/video.test.js b/test/blocks/video/video.test.js index eaaccc6073..4e76213686 100644 --- a/test/blocks/video/video.test.js +++ b/test/blocks/video/video.test.js @@ -4,20 +4,42 @@ import sinon from 'sinon'; import { waitFor, waitForElement } from '../../helpers/waitfor.js'; import { setConfig, createTag } from '../../../libs/utils/utils.js'; -import { decorateAnchorVideo, handlePause, applyHoverPlay } from '../../../libs/utils/decorate.js'; +import { decorateAnchorVideo, handlePause, applyHoverPlay, decoratePausePlayWrapper } from '../../../libs/utils/decorate.js'; setConfig({}); const { default: init } = await import('../../../libs/blocks/video/video.js'); describe('video uploaded using franklin bot', () => { + let clock; beforeEach(async () => { + clock = sinon.useFakeTimers({ + toFake: ['setTimeout'], + shouldAdvanceTime: true, + }); document.body.innerHTML = await readFile({ path: './mocks/body.html' }); }); afterEach(() => { + clock.restore(); document.body.innerHTML = ''; }); + it('aria-label should not have index when page has only one video', async () => { + const block = document.querySelector('.video.autoplay.single'); + const block2 = document.querySelector('.video.autoplay.second'); + const a = block.querySelector('a'); + const a2 = block2.querySelector('a'); + block.append(a); + block2.append(a2); + init(a); + init(a2); + await new Promise((resolve) => { setTimeout(resolve, 1000); }); + await clock.runAllAsync(); + const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); + const videoIndex = pausePlayWrapper.getAttribute('video-index'); + expect(videoIndex).to.exist; + }); + it('removes the element, if it does not have a parent node', (done) => { const anchor = createTag('a'); anchor.remove = () => done(); @@ -41,7 +63,7 @@ describe('video uploaded using franklin bot', () => { a.textContent = href; block.append(a); - await init(a); + init(a); const video = await waitForElement('.video.normal video'); expect(video).to.exist; }); @@ -53,7 +75,7 @@ describe('video uploaded using franklin bot', () => { a.textContent = href; block.append(a); - await init(a); + init(a); const video = await waitForElement('.video.autoplay video'); expect(video.hasAttribute('autoplay')).to.be.true; }); @@ -65,7 +87,7 @@ describe('video uploaded using franklin bot', () => { a.textContent = href; block.append(a); - await init(a); + init(a); const video = await waitForElement('.video.no-loop video'); expect(video.hasAttribute('loop')).to.be.false; }); @@ -77,7 +99,7 @@ describe('video uploaded using franklin bot', () => { a.textContent = href; block.append(a); - await init(a); + init(a); const video = await waitForElement('.video.no-loop.hoverplay video'); expect(video.hasAttribute('loop')).to.be.false; expect(video.hasAttribute('data-hoverplay')).to.be.true; @@ -88,7 +110,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - await init(a); + init(a); const video = block.querySelector('video'); expect(video.hasAttribute('loop')).to.be.true; expect(video.hasAttribute('data-hoverplay')).to.be.false; @@ -99,7 +121,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - await init(a); + init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-hoverplay')).to.be.true; }); @@ -109,7 +131,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - await init(a); + init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.true; }); @@ -141,57 +163,30 @@ describe('video uploaded using franklin bot', () => { ], ':type': 'sheet', }); + const a = block.querySelector('a'); block.append(a); - await init(a); - await new Promise((resolve) => { setTimeout(resolve, 50); }); - const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); + init(a); const video = block.querySelector('video'); + decoratePausePlayWrapper(video, ''); + const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); pausePlayWrapper.click(); - expect(video.paused).to.be.true; + await new Promise((resolve) => { setTimeout(resolve, 1000); }); + await clock.runAllAsync(); + expect(pausePlayWrapper.ariaPressed).to.eql('false'); }); it('accessibility controls should play autoplay videos after pausing', async () => { const block = document.querySelector('.video.autoplay.viewportplay'); const a = block.querySelector('a'); block.append(a); - await init(a); - await new Promise((resolve) => { setTimeout(resolve, 50); }); + init(a); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); - const video = block.querySelector('video'); pausePlayWrapper.click(); - await new Promise((resolve) => { setTimeout(resolve, 50); }); - await pausePlayWrapper.click(); - expect(video.paused).to.be.false; - }); - - it('aria-label should not have index when page has only one video', async () => { - const block = document.querySelector('.video.autoplay.single'); - const a = block.querySelector('a'); - a.setAttribute('indexOfBlock', 1); - block.append(a); - await init(a); - await new Promise((resolve) => { setTimeout(resolve, 50); }); - const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); - const airaLabel = pausePlayWrapper.getAttribute('aria-label'); - expect(airaLabel).to.equals('Pause'); - }); - - it('aria-label should have index when page has only one video', async () => { - const block = document.querySelector('.video.autoplay.single'); - const block2 = document.querySelector('.video.autoplay.second'); - const a = block.querySelector('a'); - const a2 = block2.querySelector('a'); - a.setAttribute('indexOfBlock', 1); - a2.setAttribute('indexOfBlock', 2); - block.append(a); - block2.append(a2); - await init(a); - await init(a2); - await new Promise((resolve) => { setTimeout(resolve, 50); }); - const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); - const airaLabel = pausePlayWrapper.getAttribute('aria-label'); - expect(airaLabel).to.equals('Pause 1'); + await new Promise((resolve) => { setTimeout(resolve, 500); }); + await clock.runAllAsync(); + pausePlayWrapper.click(); + expect(pausePlayWrapper.querySelector('.is-playing')).to.exist; }); it('handlePause should return undefined if called with unknown event', async () => { @@ -205,12 +200,14 @@ describe('video uploaded using franklin bot', () => { const block = document.querySelector('.video.autoplay1.hoverplay.no-viewportplay'); const a = block.querySelector('a'); block.append(a); - await init(a); - await new Promise((resolve) => { setTimeout(resolve, 50); }); + init(a); + await new Promise((resolve) => { setTimeout(resolve, 0); }); + await clock.runAllAsync(); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); const video = block.querySelector('video'); pausePlayWrapper.focus(); - await new Promise((resolve) => { setTimeout(resolve, 50); }); + await new Promise((resolve) => { setTimeout(resolve, 0); }); + await clock.runAllAsync(); pausePlayWrapper.blur(); expect(video.paused).to.be.true; }); @@ -228,7 +225,7 @@ describe('video uploaded using franklin bot', () => { requestAnimationFrame(resolve); }); - await init(a); + init(a); const video = block.querySelector('video'); const playSpy = sinon.spy(video, 'play'); const pauseSpy = sinon.spy(video, 'pause'); @@ -262,7 +259,7 @@ describe('video uploaded using franklin bot', () => { requestAnimationFrame(resolve); }); - await init(a); + init(a); const video = block.querySelector('video'); const playSpy = sinon.spy(video, 'play'); const pauseSpy = sinon.spy(video, 'pause'); @@ -309,7 +306,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - await init(a); + init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.true; }); @@ -319,7 +316,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - await init(a); + init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.false; }); @@ -329,7 +326,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - await init(a); + init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.false; }); @@ -339,7 +336,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - await init(a); + init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.false; }); @@ -349,7 +346,7 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); block.append(a); - await init(a); + init(a); const video = block.querySelector('video'); expect(video.hasAttribute('data-play-viewport')).to.be.false; }); From e5f53cdbe0785a979711f51a796390a556943fd8 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Mon, 11 Nov 2024 17:26:47 +0530 Subject: [PATCH 22/36] daa-ll is synced along with aria-label --- libs/utils/decorate.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 2935caccd6..991cf7474b 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -240,9 +240,13 @@ export function syncPausePlayIcon(video) { offsetFiller?.classList.toggle('is-playing'); const isPlaying = offsetFiller?.classList.contains('is-playing'); const indexOfVideo = (anchorTag.getAttribute('video-index') === '1' && videoCounter === 1) ? '' : anchorTag.getAttribute('video-index'); - const ariaLabel = `${isPlaying ? labels?.pauseMotion : labels?.playMotion} ${indexOfVideo}`.trim(); + const changedLabel = `${isPlaying ? labels?.pauseMotion : labels?.playMotion}`; + const oldLabel = `${!isPlaying ? labels?.pauseMotion : labels?.playMotion}`; + const ariaLabel = `${changedLabel} ${indexOfVideo}`.trim(); anchorTag?.setAttribute('aria-label', `${ariaLabel} `); anchorTag?.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); + const daaLL = anchorTag.getAttribute('daa-ll'); + daaLL && anchorTag.setAttribute('daa-ll', daaLL.replace(oldLabel, changedLabel)); } } From 43a13b58dc4f002542f853483521fb718fb61973 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Mon, 11 Nov 2024 22:47:07 +0530 Subject: [PATCH 23/36] code enhancement --- libs/blocks/video/video.css | 12 ++++---- libs/utils/decorate.js | 58 +++++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index f7b9384928..abeedb78a5 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -57,7 +57,7 @@ video { width: 40px; height: 40px; border-radius: inherit; - background: #242424; + background: var(--color-gray-800); } :is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper .offset-filler { @@ -103,6 +103,11 @@ video { margin: 0; } +[dir="rtl"] .brick .brick-media:not(:has(.video-container)) .pause-play-wrapper { + border-top-left-radius: inherit; + border-bottom-left-radius: inherit; +} + .hero-marquee .background .video-container { position: inherit; } @@ -126,11 +131,6 @@ video { left: 2%; } - [dir="rtl"] .brick .brick-media:not(:has(.video-container)) .pause-play-wrapper { - border-top-left-radius: inherit; - border-bottom-left-radius: inherit; - } - :is(.section[class*="-up"] .media .foreground .image:first-child, .aside .foreground .image:nth-last-child(1)) .video-container .pause-play-wrapper { left: auto; diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 991cf7474b..e0348f166d 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -1,12 +1,17 @@ import { createTag, loadStyle, getConfig, createIntersectionObserver } from './utils.js'; -import { replaceKeyArray } from '../features/placeholders.js'; -import { getFedsPlaceholderConfig } from '../blocks/global-navigation/utilities/utilities.js'; const { miloLibs, codeRoot } = getConfig(); const HIDE_CONTROLS = '_hide-controls'; let firstVideo = null; -let labels = null; +let videoLabels = { + playMotion: 'Play', + pauseMotion: 'Pause', + pauseIcon: 'Pause icon', + playIcon: 'Play icon', + hasFetched: false, +}; let videoCounter = 0; + export function decorateButtons(el, size) { const buttons = el.querySelectorAll('em a, strong a, p > a strong'); if (buttons.length === 0) return; @@ -234,35 +239,37 @@ export function getVideoAttrs(hash, dataset) { } export function syncPausePlayIcon(video) { - if (!video.getAttributeNames().join().includes('hoverplay')) { + if (!video.getAttributeNames().includes('data-hoverplay')) { const offsetFiller = video.closest('.video-holder').querySelector('.offset-filler'); const anchorTag = video.closest('.video-holder').querySelector('a'); offsetFiller?.classList.toggle('is-playing'); const isPlaying = offsetFiller?.classList.contains('is-playing'); const indexOfVideo = (anchorTag.getAttribute('video-index') === '1' && videoCounter === 1) ? '' : anchorTag.getAttribute('video-index'); - const changedLabel = `${isPlaying ? labels?.pauseMotion : labels?.playMotion}`; - const oldLabel = `${!isPlaying ? labels?.pauseMotion : labels?.playMotion}`; + const changedLabel = `${isPlaying ? videoLabels?.pauseMotion : videoLabels?.playMotion}`; + const oldLabel = `${!isPlaying ? videoLabels?.pauseMotion : videoLabels?.playMotion}`; const ariaLabel = `${changedLabel} ${indexOfVideo}`.trim(); anchorTag?.setAttribute('aria-label', `${ariaLabel} `); anchorTag?.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); const daaLL = anchorTag.getAttribute('daa-ll'); - daaLL && anchorTag.setAttribute('daa-ll', daaLL.replace(oldLabel, changedLabel)); + if (daaLL) { + anchorTag.setAttribute('daa-ll', daaLL.replace(oldLabel, changedLabel)); + } } } export function addAccessibilityControl(videoString, videoAttrs, indexOfVideo, tabIndex = 0) { if (!videoAttrs.includes('controls')) { if (videoAttrs.includes('hoverplay')) { - return `${videoString} `; + return `${videoString}`; } return `
${videoString} -
- - -
-
-
`; +
+ + +
+ +
`; } return videoString; } @@ -414,22 +421,29 @@ function updateFirstVideo() { } function updateAriaLabel(videoEl, videoAttrs) { - if (!videoEl.getAttributeNames().join().includes('hoverplay')) { + if (!videoEl.getAttributeNames().includes('data-hoverplay')) { const pausePlayWrapper = videoEl.parentElement.querySelector('.pause-play-wrapper') || videoEl.closest('.pause-play-wrapper'); + const pauseIcon = pausePlayWrapper.querySelector('.pause-icon'); + const playIcon = pausePlayWrapper.querySelector('.play-icon'); const indexOfVideo = pausePlayWrapper.getAttribute('video-index'); - const ariaLabel = `${videoAttrs.includes('autoplay') ? labels.pauseMotion : labels.playMotion} ${indexOfVideo === '1' && videoCounter === 1 ? '' : indexOfVideo}`.trim(); + const ariaLabel = `${videoAttrs.includes('autoplay') ? videoLabels.pauseMotion : videoLabels.playMotion} ${indexOfVideo === '1' && videoCounter === 1 ? '' : indexOfVideo}`.trim(); pausePlayWrapper.setAttribute('aria-label', ariaLabel); + pauseIcon.setAttribute('alt', videoLabels.pauseMotion); + playIcon.setAttribute('alt', videoLabels.playMotion); updateFirstVideo(); } } export function decoratePausePlayWrapper(videoEl, videoAttrs) { - if (labels === null) { - replaceKeyArray(['pause-motion', 'play-motion', 'pause-icon', 'play-icon'], getFedsPlaceholderConfig()) - .then(([pauseMotion, playMotion, pauseIcon, playIcon]) => { - labels = { playMotion, pauseMotion, pauseIcon, playIcon }; - updateAriaLabel(videoEl, videoAttrs); - }); + if (!videoLabels.hasFetched) { + Promise.all([import('../features/placeholders.js'), import('../blocks/global-navigation/utilities/utilities.js')]).then(([{ replaceKeyArray }, { getFedsPlaceholderConfig }]) => { + replaceKeyArray(['pause-motion', 'play-motion', 'pause-icon', 'play-icon'], getFedsPlaceholderConfig()) + .then(([pauseMotion, playMotion, pauseIcon, playIcon]) => { + videoLabels = { playMotion, pauseMotion, pauseIcon, playIcon }; + videoLabels.hasFetched = true; + updateAriaLabel(videoEl, videoAttrs); + }); + }); } else { updateAriaLabel(videoEl, videoAttrs); } From cdd5eacbfeb21f766722aab502e3efac7b168db8 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Tue, 12 Nov 2024 09:55:53 +0530 Subject: [PATCH 24/36] nala test fix|code coverege --- nala/blocks/marquee/marquee.test.js | 6 +++--- test/blocks/video/video.test.js | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/nala/blocks/marquee/marquee.test.js b/nala/blocks/marquee/marquee.test.js index 246503a78b..d5ecdd594c 100644 --- a/nala/blocks/marquee/marquee.test.js +++ b/nala/blocks/marquee/marquee.test.js @@ -543,7 +543,7 @@ test.describe('Milo Marquee Block test suite', () => { await test.step('step-3: Verify analytic attributes', async () => { await expect(await marquee.marqueeSmallDark).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); - await expect(await marquee.blueButtonL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + await expect(await marquee.blueButtonL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); }); await test.step('step-4: Verify the accessibility test on the Marquee (small) background video playsinline block', async () => { @@ -581,7 +581,7 @@ test.describe('Milo Marquee Block test suite', () => { await test.step('step-3: Verify analytic attributes', async () => { await expect(await marquee.marqueeLargeLight).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); - await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); await expect(await marquee.actionLink2).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 2, data.h2Text)); }); }); @@ -615,7 +615,7 @@ test.describe('Milo Marquee Block test suite', () => { await test.step('step-3: Verify analytic attributes', async () => { await expect(await marquee.marqueeLargeDark).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 2)); - await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); }); }); diff --git a/test/blocks/video/video.test.js b/test/blocks/video/video.test.js index 4e76213686..b0f355299e 100644 --- a/test/blocks/video/video.test.js +++ b/test/blocks/video/video.test.js @@ -32,12 +32,15 @@ describe('video uploaded using franklin bot', () => { block.append(a); block2.append(a2); init(a); - init(a2); - await new Promise((resolve) => { setTimeout(resolve, 1000); }); + await new Promise((resolve) => { setTimeout(resolve, 600); }); await clock.runAllAsync(); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); + pausePlayWrapper.removeAttribute('video-index'); + init(a2); + await new Promise((resolve) => { setTimeout(resolve, 500); }); + await clock.runAllAsync(); const videoIndex = pausePlayWrapper.getAttribute('video-index'); - expect(videoIndex).to.exist; + expect(videoIndex).to.be.null; }); it('removes the element, if it does not have a parent node', (done) => { @@ -183,6 +186,7 @@ describe('video uploaded using franklin bot', () => { init(a); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); pausePlayWrapper.click(); + pausePlayWrapper.setAttribute('daa-ll', 'pause-motion'); await new Promise((resolve) => { setTimeout(resolve, 500); }); await clock.runAllAsync(); pausePlayWrapper.click(); From 1bc65be6810bee987a668c50c89983c3db7d9e60 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Tue, 12 Nov 2024 11:05:14 +0530 Subject: [PATCH 25/36] nala test bug fix --- nala/blocks/marquee/marquee.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nala/blocks/marquee/marquee.test.js b/nala/blocks/marquee/marquee.test.js index d5ecdd594c..ed09b274ce 100644 --- a/nala/blocks/marquee/marquee.test.js +++ b/nala/blocks/marquee/marquee.test.js @@ -570,7 +570,7 @@ test.describe('Milo Marquee Block test suite', () => { await expect(await marquee.headingXXL).toContainText(data.h2Text); await expect(await marquee.bodyXL).toContainText(data.bodyText); await expect(await marquee.blueButtonXL).toContainText(data.blueButtonText); - await expect(await marquee.actionLink2).toContainText(data.linkText); + await expect(await marquee.actionLink3).toContainText(data.linkText); await expect(await marquee.backgroundVideoDesktop).toBeVisible(); expect(await webUtil.verifyAttributes(marquee.backgroundVideoDesktop, marquee.attributes['backgroundVideo.inline'])).toBeTruthy(); From f0de063b2755ee665f5ffa123b042c7c2a48bb9c Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Tue, 12 Nov 2024 11:28:08 +0530 Subject: [PATCH 26/36] nala test fix --- nala/blocks/marquee/marquee.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nala/blocks/marquee/marquee.test.js b/nala/blocks/marquee/marquee.test.js index ed09b274ce..9f9905e41d 100644 --- a/nala/blocks/marquee/marquee.test.js +++ b/nala/blocks/marquee/marquee.test.js @@ -582,7 +582,7 @@ test.describe('Milo Marquee Block test suite', () => { await test.step('step-3: Verify analytic attributes', async () => { await expect(await marquee.marqueeLargeLight).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); - await expect(await marquee.actionLink2).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 2, data.h2Text)); + await expect(await marquee.actionLink3).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 3, data.h2Text)); }); }); From 7e47a2ce3af0ac3da3abde5e71fdfdcb73d15e73 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Tue, 12 Nov 2024 13:53:24 +0530 Subject: [PATCH 27/36] right-left positioning is done for screens > 600px and a img fix --- libs/blocks/video/video.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index abeedb78a5..60173781ff 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -112,7 +112,7 @@ video { position: inherit; } -.video-container .pause-play-wrapper img.accessibility-control { +:is(.video-container .pause-play-wrapper, .aside.split.split-left .split-image) img.accessibility-control { width: auto; } @@ -124,7 +124,7 @@ video { display: contents; } -@media (min-width: 900px) { +@media (min-width: 600px) { .media:not(.media-reverse-mobile, .media-reversed) .video-container .pause-play-wrapper, :is(.marquee.row-reversed .asset, .marquee-anchors, .hero-marquee.asset-left) .video-container .pause-play-wrapper, .aside:not(.split) .video-container .pause-play-wrapper { From 6522411c885361b762ced7dcda5222737823c1d9 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Wed, 13 Nov 2024 16:01:00 +0530 Subject: [PATCH 28/36] getFedsconfig moved to feds file|url fetched from fedRoot function --- libs/blocks/aside/aside.css | 4 -- libs/blocks/aside/aside.js | 3 +- libs/blocks/brick/brick.css | 1 - libs/blocks/carousel/carousel.js | 6 +-- libs/blocks/figure/figure.js | 9 ++--- libs/blocks/global-footer/global-footer.js | 3 +- .../features/profile/dropdown.js | 3 +- .../features/search/gnav-search.js | 2 +- .../global-navigation/global-navigation.js | 6 +-- .../global-navigation/utilities/utilities.js | 20 +--------- libs/blocks/video/video.css | 10 ++--- libs/features/webapp-prompt/webapp-prompt.js | 2 +- libs/utils/decorate.js | 37 +++++++++---------- libs/utils/federated.js | 18 +++++++++ .../utilities/utilities.test.js | 2 +- test/blocks/video/video.test.js | 7 +--- 16 files changed, 60 insertions(+), 73 deletions(-) diff --git a/libs/blocks/aside/aside.css b/libs/blocks/aside/aside.css index 17d4327405..54615ee978 100644 --- a/libs/blocks/aside/aside.css +++ b/libs/blocks/aside/aside.css @@ -1075,10 +1075,6 @@ max-width: 1396px; } - .aside.split .video-container .pause-play-wrapper img.accessibility-control { - width: auto; - } - .aside.split .desktop-wide img, .aside.split .desktop-wide video { aspect-ratio: var(--aspect-ratio-wide); diff --git a/libs/blocks/aside/aside.js b/libs/blocks/aside/aside.js index b61b860031..27939645ac 100644 --- a/libs/blocks/aside/aside.js +++ b/libs/blocks/aside/aside.js @@ -197,7 +197,8 @@ function decorateLayout(el) { } const foregroundImage = foreground.querySelector(':scope > div:not(.text) img')?.closest('div'); const bgImage = el.querySelector(':scope > div:not(.text):not(.foreground) img')?.closest('div'); - const foregroundMedia = foreground.querySelector(':scope > div:not(.text) :is(.video-container, video, a[href*=".mp4"], a[href*="tv.adobe.com"])')?.closest('div:not(.video-container)'); + const foregroundMedia = foreground.querySelector(':scope > div:not(.text) :is(.video-container, video, a[href*=".mp4"], a[href*="tv.adobe.com"])') + ?.closest('div:not(.video-container)'); const bgMedia = el.querySelector(':scope > div:not(.text):not(.foreground) video, :scope > div:not(.text):not(.foreground) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div'); const image = foregroundImage ?? bgImage; const asideMedia = foregroundMedia ?? bgMedia ?? image; diff --git a/libs/blocks/brick/brick.css b/libs/blocks/brick/brick.css index 1f58444f21..4ed08eb815 100644 --- a/libs/blocks/brick/brick.css +++ b/libs/blocks/brick/brick.css @@ -349,7 +349,6 @@ .brick.split.row .foreground .brick-media .video-container img, .brick.split.row .foreground .brick-media .video-container video { width: 100%; - height: 100%; } .brick .foreground .brick-media video, diff --git a/libs/blocks/carousel/carousel.js b/libs/blocks/carousel/carousel.js index e7979d9dad..ea375291bb 100644 --- a/libs/blocks/carousel/carousel.js +++ b/libs/blocks/carousel/carousel.js @@ -168,7 +168,7 @@ function moveSlides(event, carouselElements, jumpToIndex) { referenceSlide.classList.remove('reference-slide'); referenceSlide.style.order = null; activeSlide.classList.remove('active'); - activeSlide.querySelectorAll('a,video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', -1); }); + activeSlide.querySelectorAll('a, video').forEach((focusableElement) => focusableElement.setAttribute('tabindex', -1)); activeSlideIndicator.classList.remove('active'); activeSlideIndicator.setAttribute('tabindex', -1); @@ -230,10 +230,10 @@ function moveSlides(event, carouselElements, jumpToIndex) { if (index < show) { tabIndex = 0; } - slide.querySelectorAll('a,:not(.video-container,.pause-play-wrapper) > video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', tabIndex); }); + slide.querySelectorAll('a,:not(.video-container, .pause-play-wrapper) > video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', tabIndex); }); }); } else { - activeSlide.querySelectorAll('a,:not(.video-container,.pause-play-wrapper) > video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', 0); }); + activeSlide.querySelectorAll('a,:not(.video-container, .pause-play-wrapper) > video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', 0); }); } activeSlideIndicator.classList.add('active'); activeSlideIndicator.setAttribute('tabindex', 0); diff --git a/libs/blocks/figure/figure.js b/libs/blocks/figure/figure.js index d4ca6a1e60..b32ef360fc 100644 --- a/libs/blocks/figure/figure.js +++ b/libs/blocks/figure/figure.js @@ -1,4 +1,4 @@ -import { applyHoverPlay, decorateAnchorVideo, applyAccessibiltyEvents, decoratePausePlayWrapper } from '../../utils/decorate.js'; +import { applyHoverPlay, decorateAnchorVideo, applyAccessibilityEvents, decoratePausePlayWrapper, isAccessible } from '../../utils/decorate.js'; import { createTag } from '../../utils/utils.js'; function buildCaption(pEl) { @@ -21,7 +21,6 @@ function decorateVideo(clone, figEl) { if (anchorTag && !anchorTag.hash) anchorTag.hash = '#autoplay'; if (anchorTag) decorateAnchorVideo({ src: anchorTag.href, anchorTag }); if (videoTag) { - const videoContainer = clone.querySelector('.video-container, .pause-play-wrapper, video'); videoTag.removeAttribute('data-mouseevent'); if (videoTag.dataset?.videoSource) { videoTag.appendChild( @@ -32,11 +31,11 @@ function decorateVideo(clone, figEl) { ); } applyHoverPlay(videoTag); - if (!videoTag.controls) { - applyAccessibiltyEvents(videoTag); + if (!videoTag.controls && isAccessible(anchorTag)) { + applyAccessibilityEvents(videoTag); decoratePausePlayWrapper(videoTag, 'autoplay'); } - figEl.prepend(videoContainer); + figEl.prepend(clone.querySelector('.video-container, .pause-play-wrapper, video')); } } diff --git a/libs/blocks/global-footer/global-footer.js b/libs/blocks/global-footer/global-footer.js index 2cd9f35559..7fe74c487d 100644 --- a/libs/blocks/global-footer/global-footer.js +++ b/libs/blocks/global-footer/global-footer.js @@ -9,7 +9,6 @@ import { } from '../../utils/utils.js'; import { - getFedsPlaceholderConfig, getExperienceName, getAnalyticsValue, loadDecorateMenu, @@ -23,7 +22,7 @@ import { isDarkMode, } from '../global-navigation/utilities/utilities.js'; -import { getFederatedUrl } from '../../utils/federated.js'; +import { getFederatedUrl, getFedsPlaceholderConfig } from '../../utils/federated.js'; import { replaceKey } from '../../features/placeholders.js'; diff --git a/libs/blocks/global-navigation/features/profile/dropdown.js b/libs/blocks/global-navigation/features/profile/dropdown.js index 836f9cd834..d874963e82 100644 --- a/libs/blocks/global-navigation/features/profile/dropdown.js +++ b/libs/blocks/global-navigation/features/profile/dropdown.js @@ -1,6 +1,7 @@ import { getConfig } from '../../../../utils/utils.js'; -import { toFragment, getFedsPlaceholderConfig, trigger, closeAllDropdowns, logErrorFor } from '../../utilities/utilities.js'; +import { toFragment, trigger, closeAllDropdowns, logErrorFor } from '../../utilities/utilities.js'; import { replaceKeyArray } from '../../../../features/placeholders.js'; +import { getFedsPlaceholderConfig } from '../../../../utils/federated.js'; const getLanguage = (ietfLocale) => { if (!ietfLocale.length) return 'en'; diff --git a/libs/blocks/global-navigation/features/search/gnav-search.js b/libs/blocks/global-navigation/features/search/gnav-search.js index ca6df552b8..76532c5d31 100644 --- a/libs/blocks/global-navigation/features/search/gnav-search.js +++ b/libs/blocks/global-navigation/features/search/gnav-search.js @@ -1,6 +1,5 @@ import { toFragment, - getFedsPlaceholderConfig, isDesktop, setCurtainState, trigger, @@ -10,6 +9,7 @@ import { import { replaceKeyArray } from '../../../../features/placeholders.js'; import { getConfig } from '../../../../utils/utils.js'; import { debounce } from '../../../../utils/action.js'; +import { getFedsPlaceholderConfig } from '../../../../utils/federated.js'; const CONFIG = { suggestions: { diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index 4a1903171a..a1879aa779 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -13,7 +13,6 @@ import { getActiveLink, getAnalyticsValue, getExperienceName, - getFedsPlaceholderConfig, hasActiveLink, isActiveLink, icons, @@ -40,6 +39,7 @@ import { setDisableAEDState, getDisableAEDState, } from './utilities/utilities.js'; +import { getFedsPlaceholderConfig } from '../../utils/federated.js'; import { replaceKey, replaceKeyArray } from '../../features/placeholders.js'; @@ -679,7 +679,7 @@ class Gnav { return this.loadDelayed().then(() => { this.blocks.search.instance = new this.Search(this.blocks.search.config); - }).catch(() => {}); + }).catch(() => { }); }; isToggleExpanded = () => this.elements.mobileToggle?.getAttribute('aria-expanded') === 'true'; @@ -773,7 +773,7 @@ class Gnav { if (allSvgImgs.length === 2) return allSvgImgs[1]; const images = blockLinks.filter((blockLink) => imgRegex.test(blockLink.href) - || imgRegex.test(blockLink.textContent)); + || imgRegex.test(blockLink.textContent)); if (images.length === 2) return getBrandImage(images[1], isBrandImage); } const svgImg = rawBlock.querySelector('picture img[src$=".svg"]'); diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index 79104db68c..3848fdc601 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -1,7 +1,7 @@ import { getConfig, getMetadata, loadStyle, loadLana, decorateLinks, localizeLink, } from '../../../utils/utils.js'; -import { getFederatedContentRoot, getFederatedUrl } from '../../../utils/federated.js'; +import { getFederatedContentRoot, getFederatedUrl, getFedsPlaceholderConfig } from '../../../utils/federated.js'; import { processTrackingLabels } from '../../../martech/attributes.js'; import { replaceText } from '../../../features/placeholders.js'; @@ -107,24 +107,6 @@ export const federatePictureSources = ({ section, forceFederate } = {}) => { }); }; -let fedsPlaceholderConfig; -export const getFedsPlaceholderConfig = ({ useCache = true } = {}) => { - if (useCache && fedsPlaceholderConfig) return fedsPlaceholderConfig; - - const { locale, placeholders } = getConfig(); - const libOrigin = getFederatedContentRoot(); - - fedsPlaceholderConfig = { - locale: { - ...locale, - contentRoot: `${libOrigin}${locale.prefix}/federal/globalnav`, - }, - placeholders, - }; - - return fedsPlaceholderConfig; -}; - export function getAnalyticsValue(str, index) { if (typeof str !== 'string' || !str.length) return str; diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 60173781ff..39ac055fd1 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -37,8 +37,8 @@ video { } .video-container .pause-play-wrapper { - width: 45px !important; - height: 45px !important; + width: 45px; + height: 45px; position: absolute; bottom: 2%; right: 2%; @@ -61,7 +61,7 @@ video { } :is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper .offset-filler { - border: 2px solid white; + border: 2px solid #fff; } :is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper:focus .offset-filler { @@ -74,8 +74,8 @@ video { } .video-container .pause-play-wrapper:focus-visible .offset-filler { - border: 2px solid white; - outline: 1px solid white; + border: 2px solid #fff; + outline: 1px solid #fff; } .video-container .pause-play-wrapper:focus-visible { diff --git a/libs/features/webapp-prompt/webapp-prompt.js b/libs/features/webapp-prompt/webapp-prompt.js index ea9bc072d0..894d010d0b 100644 --- a/libs/features/webapp-prompt/webapp-prompt.js +++ b/libs/features/webapp-prompt/webapp-prompt.js @@ -1,5 +1,4 @@ import { - getFedsPlaceholderConfig, getUserProfile, icons, lanaLog, @@ -7,6 +6,7 @@ import { } from '../../blocks/global-navigation/utilities/utilities.js'; import { getConfig, decorateSVG } from '../../utils/utils.js'; import { replaceKey, replaceText } from '../placeholders.js'; +import { getFedsPlaceholderConfig } from '../../utils/federated.js'; export const DISMISSAL_CONFIG = { animationCount: 2, diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index eb88042336..d80f9ce8d9 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -1,4 +1,5 @@ import { createTag, loadStyle, getConfig, createIntersectionObserver } from './utils.js'; +import { getFederatedContentRoot, getFedsPlaceholderConfig } from "./federated.js" const { miloLibs, codeRoot } = getConfig(); const HIDE_CONTROLS = '_hide-controls'; @@ -257,27 +258,24 @@ export function syncPausePlayIcon(video) { anchorTag?.setAttribute('aria-label', `${ariaLabel} `); anchorTag?.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); const daaLL = anchorTag.getAttribute('daa-ll'); - if (daaLL) { - anchorTag.setAttribute('daa-ll', daaLL.replace(oldLabel, changedLabel)); - } + if (daaLL) anchorTag.setAttribute('daa-ll', daaLL.replace(oldLabel, changedLabel)); } } export function addAccessibilityControl(videoString, videoAttrs, indexOfVideo, tabIndex = 0) { - if (!videoAttrs.includes('controls')) { - if (videoAttrs.includes('hoverplay')) { - return `${videoString}`; - } - return `
${videoString} + if (videoAttrs.includes('controls')) return videoString; + const fedRoot = getFederatedContentRoot(); + if (videoAttrs.includes('hoverplay')) { + return `${videoString}`; + } + return `
${videoString}
- - + +
`; - } - return videoString; } export function handlePause(event) { @@ -311,7 +309,7 @@ export function applyHoverPlay(video) { } } -export function applyAccessibiltyEvents(videoEl) { +export function applyAccessibilityEvents(videoEl) { const pausePlayWrapper = videoEl.parentElement.querySelector('.pause-play-wrapper') || videoEl.closest('.pause-play-wrapper'); if (pausePlayWrapper?.querySelector('.accessibility-control')) { pausePlayWrapper.addEventListener('click', handlePause); @@ -411,15 +409,13 @@ export async function loadCDT(el, classList) { } export function isAccessible(anchorTag) { - return !anchorTag.hash.includes(HIDE_CONTROLS); + return !anchorTag?.hash.includes(HIDE_CONTROLS); } function updateFirstVideo() { if (firstVideo != null && firstVideo?.controls === false && videoCounter > 1) { let videoHolder = document.querySelector('[video-index="1"]') || firstVideo.closest('.video-holder'); - if (videoHolder.nodeName !== 'A') { - videoHolder = videoHolder.querySelector('a.pause-play-wrapper'); - } + if (videoHolder.nodeName !== 'A') videoHolder = videoHolder.querySelector('a.pause-play-wrapper'); const firstVideoLabel = videoHolder.getAttribute('aria-label'); videoHolder.setAttribute('aria-label', `${firstVideoLabel} 1`); firstVideo = null; @@ -432,7 +428,8 @@ function updateAriaLabel(videoEl, videoAttrs) { const pauseIcon = pausePlayWrapper.querySelector('.pause-icon'); const playIcon = pausePlayWrapper.querySelector('.play-icon'); const indexOfVideo = pausePlayWrapper.getAttribute('video-index'); - const ariaLabel = `${videoAttrs.includes('autoplay') ? videoLabels.pauseMotion : videoLabels.playMotion} ${indexOfVideo === '1' && videoCounter === 1 ? '' : indexOfVideo}`.trim(); + let ariaLabel = `${videoAttrs.includes('autoplay') ? videoLabels.pauseMotion : videoLabels.playMotion}`; + ariaLabel = ariaLabel.concat(`${indexOfVideo === '1' && videoCounter === 1 ? '' : indexOfVideo}`); pausePlayWrapper.setAttribute('aria-label', ariaLabel); pauseIcon.setAttribute('alt', videoLabels.pauseMotion); playIcon.setAttribute('alt', videoLabels.playMotion); @@ -442,7 +439,7 @@ function updateAriaLabel(videoEl, videoAttrs) { export function decoratePausePlayWrapper(videoEl, videoAttrs) { if (!videoLabels.hasFetched) { - Promise.all([import('../features/placeholders.js'), import('../blocks/global-navigation/utilities/utilities.js')]).then(([{ replaceKeyArray }, { getFedsPlaceholderConfig }]) => { + import('../features/placeholders.js').then(({ replaceKeyArray }) => { replaceKeyArray(['pause-motion', 'play-motion', 'pause-icon', 'play-icon'], getFedsPlaceholderConfig()) .then(([pauseMotion, playMotion, pauseIcon, playIcon]) => { videoLabels = { playMotion, pauseMotion, pauseIcon, playIcon }; @@ -483,7 +480,7 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { }, }); if (accessibilityEnabled) { - applyAccessibiltyEvents(videoEl); + applyAccessibilityEvents(videoEl); if (!videoEl.controls) { decoratePausePlayWrapper(videoEl, attrs); } diff --git a/libs/utils/federated.js b/libs/utils/federated.js index 59b93ca59a..0e8e9ffba7 100644 --- a/libs/utils/federated.js +++ b/libs/utils/federated.js @@ -39,3 +39,21 @@ export const getFederatedUrl = (url = '') => { } return url; }; + +let fedsPlaceholderConfig; +export const getFedsPlaceholderConfig = ({ useCache = true } = {}) => { + if (useCache && fedsPlaceholderConfig) return fedsPlaceholderConfig; + + const { locale, placeholders } = getConfig(); + const libOrigin = getFederatedContentRoot(); + + fedsPlaceholderConfig = { + locale: { + ...locale, + contentRoot: `${libOrigin}${locale.prefix}/federal/globalnav`, + }, + placeholders, + }; + + return fedsPlaceholderConfig; +}; diff --git a/test/blocks/global-navigation/utilities/utilities.test.js b/test/blocks/global-navigation/utilities/utilities.test.js index 7d06b9e528..dddd165a60 100644 --- a/test/blocks/global-navigation/utilities/utilities.test.js +++ b/test/blocks/global-navigation/utilities/utilities.test.js @@ -3,7 +3,6 @@ import sinon from 'sinon'; import { fetchAndProcessPlainHtml, toFragment, - getFedsPlaceholderConfig, federatePictureSources, getAnalyticsValue, decorateCta, @@ -18,6 +17,7 @@ import { import { setConfig, getConfig } from '../../../../libs/utils/utils.js'; import { createFullGlobalNavigation, config } from '../test-utilities.js'; import mepInBlock from '../mocks/mep-config.js'; +import { getFedsPlaceholderConfig } from '../../../../libs/utils/federated.js'; const baseHost = 'https://main--federal--adobecom.hlx.page'; describe('global navigation utilities', () => { diff --git a/test/blocks/video/video.test.js b/test/blocks/video/video.test.js index b0f355299e..1591350a76 100644 --- a/test/blocks/video/video.test.js +++ b/test/blocks/video/video.test.js @@ -29,8 +29,6 @@ describe('video uploaded using franklin bot', () => { const block2 = document.querySelector('.video.autoplay.second'); const a = block.querySelector('a'); const a2 = block2.querySelector('a'); - block.append(a); - block2.append(a2); init(a); await new Promise((resolve) => { setTimeout(resolve, 600); }); await clock.runAllAsync(); @@ -168,13 +166,12 @@ describe('video uploaded using franklin bot', () => { }); const a = block.querySelector('a'); - block.append(a); init(a); const video = block.querySelector('video'); decoratePausePlayWrapper(video, ''); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); pausePlayWrapper.click(); - await new Promise((resolve) => { setTimeout(resolve, 1000); }); + await new Promise((resolve) => { setTimeout(resolve, 500); }); await clock.runAllAsync(); expect(pausePlayWrapper.ariaPressed).to.eql('false'); }); @@ -182,7 +179,6 @@ describe('video uploaded using franklin bot', () => { it('accessibility controls should play autoplay videos after pausing', async () => { const block = document.querySelector('.video.autoplay.viewportplay'); const a = block.querySelector('a'); - block.append(a); init(a); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); pausePlayWrapper.click(); @@ -203,7 +199,6 @@ describe('video uploaded using franklin bot', () => { it('video should be paused on focus out or blur', async () => { const block = document.querySelector('.video.autoplay1.hoverplay.no-viewportplay'); const a = block.querySelector('a'); - block.append(a); init(a); await new Promise((resolve) => { setTimeout(resolve, 0); }); await clock.runAllAsync(); From 185bf61ee7540cff640b0a60c96a879774a2740c Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Wed, 13 Nov 2024 16:09:45 +0530 Subject: [PATCH 29/36] linting fix --- libs/blocks/aside/aside.js | 2 +- libs/utils/decorate.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/blocks/aside/aside.js b/libs/blocks/aside/aside.js index 27939645ac..b5aa53fccc 100644 --- a/libs/blocks/aside/aside.js +++ b/libs/blocks/aside/aside.js @@ -198,7 +198,7 @@ function decorateLayout(el) { const foregroundImage = foreground.querySelector(':scope > div:not(.text) img')?.closest('div'); const bgImage = el.querySelector(':scope > div:not(.text):not(.foreground) img')?.closest('div'); const foregroundMedia = foreground.querySelector(':scope > div:not(.text) :is(.video-container, video, a[href*=".mp4"], a[href*="tv.adobe.com"])') - ?.closest('div:not(.video-container)'); + ?.closest('div:not(.video-container)'); const bgMedia = el.querySelector(':scope > div:not(.text):not(.foreground) video, :scope > div:not(.text):not(.foreground) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div'); const image = foregroundImage ?? bgImage; const asideMedia = foregroundMedia ?? bgMedia ?? image; diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index d80f9ce8d9..c928fd473a 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -1,5 +1,5 @@ import { createTag, loadStyle, getConfig, createIntersectionObserver } from './utils.js'; -import { getFederatedContentRoot, getFedsPlaceholderConfig } from "./federated.js" +import { getFederatedContentRoot, getFedsPlaceholderConfig } from './federated.js'; const { miloLibs, codeRoot } = getConfig(); const HIDE_CONTROLS = '_hide-controls'; From f9dac71180259bfb5861f03fc4020b7a18b9c80f Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Thu, 14 Nov 2024 22:24:55 +0530 Subject: [PATCH 30/36] icons adapted to the figma --- libs/blocks/video/video.css | 7 +++--- test/blocks/video/video.test.js | 43 +++++++++++++++------------------ 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 39ac055fd1..1834cd7cff 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -64,13 +64,12 @@ video { border: 2px solid #fff; } -:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper:focus .offset-filler { +:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper:focus-visible .offset-filler { outline: 1px solid #000; } .video-container .pause-play-wrapper .offset-filler:hover { background: #000; - outline: var(--color-accent-focus-ring) solid 2px; } .video-container .pause-play-wrapper:focus-visible .offset-filler { @@ -127,7 +126,7 @@ video { @media (min-width: 600px) { .media:not(.media-reverse-mobile, .media-reversed) .video-container .pause-play-wrapper, :is(.marquee.row-reversed .asset, .marquee-anchors, .hero-marquee.asset-left) .video-container .pause-play-wrapper, - .aside:not(.split) .video-container .pause-play-wrapper { + :is(.aside:not(.split), .aside.split.split-right) .video-container .pause-play-wrapper { left: 2%; } @@ -138,7 +137,7 @@ video { } [dir="rtl"] :is(.marquee.split:not(.row-reversed), .media:is(.media-reverse-mobile, .media-reversed), - .hero-marquee:not(.asset-left) :is([class*="foreground"]), .aside.split, + .hero-marquee:not(.asset-left) :is([class*="foreground"]), .aside.split:not(.split-right), .aside .foreground.container .image:nth-last-child(1), .brick.media-right, .how-to) .video-container .pause-play-wrapper { left: 2%; right: auto; diff --git a/test/blocks/video/video.test.js b/test/blocks/video/video.test.js index 1591350a76..c4e167cb20 100644 --- a/test/blocks/video/video.test.js +++ b/test/blocks/video/video.test.js @@ -11,6 +11,7 @@ const { default: init } = await import('../../../libs/blocks/video/video.js'); describe('video uploaded using franklin bot', () => { let clock; + const callback = sinon.spy(); beforeEach(async () => { clock = sinon.useFakeTimers({ toFake: ['setTimeout'], @@ -30,12 +31,12 @@ describe('video uploaded using franklin bot', () => { const a = block.querySelector('a'); const a2 = block2.querySelector('a'); init(a); - await new Promise((resolve) => { setTimeout(resolve, 600); }); + setTimeout(callback, 600); await clock.runAllAsync(); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); pausePlayWrapper.removeAttribute('video-index'); init(a2); - await new Promise((resolve) => { setTimeout(resolve, 500); }); + setTimeout(callback, 500); await clock.runAllAsync(); const videoIndex = pausePlayWrapper.getAttribute('video-index'); expect(videoIndex).to.be.null; @@ -171,7 +172,7 @@ describe('video uploaded using franklin bot', () => { decoratePausePlayWrapper(video, ''); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); pausePlayWrapper.click(); - await new Promise((resolve) => { setTimeout(resolve, 500); }); + setTimeout(callback, 500); await clock.runAllAsync(); expect(pausePlayWrapper.ariaPressed).to.eql('false'); }); @@ -183,7 +184,7 @@ describe('video uploaded using franklin bot', () => { const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); pausePlayWrapper.click(); pausePlayWrapper.setAttribute('daa-ll', 'pause-motion'); - await new Promise((resolve) => { setTimeout(resolve, 500); }); + setTimeout(callback, 500); await clock.runAllAsync(); pausePlayWrapper.click(); expect(pausePlayWrapper.querySelector('.is-playing')).to.exist; @@ -200,12 +201,12 @@ describe('video uploaded using franklin bot', () => { const block = document.querySelector('.video.autoplay1.hoverplay.no-viewportplay'); const a = block.querySelector('a'); init(a); - await new Promise((resolve) => { setTimeout(resolve, 0); }); + setTimeout(callback, 0); await clock.runAllAsync(); const pausePlayWrapper = block.querySelector('.pause-play-wrapper'); const video = block.querySelector('video'); pausePlayWrapper.focus(); - await new Promise((resolve) => { setTimeout(resolve, 0); }); + setTimeout(callback, 0); await clock.runAllAsync(); pausePlayWrapper.blur(); expect(video.paused).to.be.true; @@ -232,9 +233,8 @@ describe('video uploaded using franklin bot', () => { await waitFor(intersectionObserverAddsSource); video.scrollIntoView(); await nextFrame(); - await new Promise((resolve) => { - setTimeout(resolve, 100); - }); + setTimeout(callback, 100); + await clock.runAllAsync(); assert.isTrue(playSpy.calledOnce); // push the video out of the viewport @@ -243,9 +243,8 @@ describe('video uploaded using franklin bot', () => { video.parentNode.insertBefore(div, video); await nextFrame(); - await new Promise((resolve) => { - setTimeout(resolve, 100); - }); + setTimeout(callback, 100); + await clock.runAllAsync(); assert.isTrue(pauseSpy.calledOnce); expect(video.hasAttribute('data-play-viewport')).to.be.true; }); @@ -269,9 +268,8 @@ describe('video uploaded using franklin bot', () => { video.addEventListener('ended', endedSpy); video.scrollIntoView(); await nextFrame(); - await new Promise((resolve) => { - setTimeout(resolve, 100); - }); + setTimeout(callback, 100); + await clock.runAllAsync(); assert.isTrue(playSpy.calledOnce); // push the video out of the viewport @@ -280,21 +278,18 @@ describe('video uploaded using franklin bot', () => { video.parentNode.insertBefore(div, video); await nextFrame(); - await new Promise((resolve) => { - setTimeout(resolve, 200); - }); + setTimeout(callback, 200); + await clock.runAllAsync(); assert.isTrue(pauseSpy.calledOnce); video.dispatchEvent(new Event('ended')); await nextFrame(); - await new Promise((resolve) => { - setTimeout(resolve, 100); - }); + setTimeout(callback, 100); + await clock.runAllAsync(); video.scrollIntoView(); await nextFrame(); - await new Promise((resolve) => { - setTimeout(resolve, 100); - }); + setTimeout(callback, 100); + await clock.runAllAsync(); expect(playSpy.callCount).to.equal(1); expect(video.hasAttribute('data-play-viewport')).to.be.true; From db007bdf899dd9095dd2a56e3c54a7edc97a68e6 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Fri, 15 Nov 2024 10:26:46 +0530 Subject: [PATCH 31/36] carousel and how-to fix with other minor fixes --- libs/blocks/carousel/carousel.css | 2 +- libs/blocks/figure/figure.js | 4 ++-- libs/blocks/video/video.css | 4 ++++ libs/utils/decorate.js | 6 +++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libs/blocks/carousel/carousel.css b/libs/blocks/carousel/carousel.css index 41e70a1ef2..08bffdf704 100644 --- a/libs/blocks/carousel/carousel.css +++ b/libs/blocks/carousel/carousel.css @@ -397,7 +397,7 @@ html[dir="rtl"] .carousel-slides .section.carousel-slide { overflow: hidden; } -.carousel .carousel-slide > div p > video { +.carousel .carousel-slide > div p :is(.video-holder, video) { width: 100%; height: auto; } diff --git a/libs/blocks/figure/figure.js b/libs/blocks/figure/figure.js index b32ef360fc..40c9e40c28 100644 --- a/libs/blocks/figure/figure.js +++ b/libs/blocks/figure/figure.js @@ -1,4 +1,4 @@ -import { applyHoverPlay, decorateAnchorVideo, applyAccessibilityEvents, decoratePausePlayWrapper, isAccessible } from '../../utils/decorate.js'; +import { applyHoverPlay, decorateAnchorVideo, applyAccessibilityEvents, decoratePausePlayWrapper, isVideoAccessible } from '../../utils/decorate.js'; import { createTag } from '../../utils/utils.js'; function buildCaption(pEl) { @@ -31,7 +31,7 @@ function decorateVideo(clone, figEl) { ); } applyHoverPlay(videoTag); - if (!videoTag.controls && isAccessible(anchorTag)) { + if (!videoTag.controls && isVideoAccessible(anchorTag)) { applyAccessibilityEvents(videoTag); decoratePausePlayWrapper(videoTag, 'autoplay'); } diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 1834cd7cff..8d7c01cf83 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -123,6 +123,10 @@ video { display: contents; } +.how-to .how-to-media .video-container { + height: fit-content; +} + @media (min-width: 600px) { .media:not(.media-reverse-mobile, .media-reversed) .video-container .pause-play-wrapper, :is(.marquee.row-reversed .asset, .marquee-anchors, .hero-marquee.asset-left) .video-container .pause-play-wrapper, diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index c928fd473a..15251d7613 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -408,7 +408,7 @@ export async function loadCDT(el, classList) { } } -export function isAccessible(anchorTag) { +export function isVideoAccessible(anchorTag) { return !anchorTag?.hash.includes(HIDE_CONTROLS); } @@ -429,7 +429,7 @@ function updateAriaLabel(videoEl, videoAttrs) { const playIcon = pausePlayWrapper.querySelector('.play-icon'); const indexOfVideo = pausePlayWrapper.getAttribute('video-index'); let ariaLabel = `${videoAttrs.includes('autoplay') ? videoLabels.pauseMotion : videoLabels.playMotion}`; - ariaLabel = ariaLabel.concat(`${indexOfVideo === '1' && videoCounter === 1 ? '' : indexOfVideo}`); + ariaLabel = ariaLabel.concat(` ${indexOfVideo === '1' && videoCounter === 1 ? '' : indexOfVideo}`); pausePlayWrapper.setAttribute('aria-label', ariaLabel); pauseIcon.setAttribute('alt', videoLabels.pauseMotion); playIcon.setAttribute('alt', videoLabels.playMotion); @@ -454,7 +454,7 @@ export function decoratePausePlayWrapper(videoEl, videoAttrs) { export function decorateAnchorVideo({ src = '', anchorTag }) { if (!src.length || !(anchorTag instanceof HTMLElement)) return; - const accessibilityEnabled = isAccessible(anchorTag); + const accessibilityEnabled = isVideoAccessible(anchorTag); anchorTag.hash = anchorTag.hash.replace(`#${HIDE_CONTROLS}`, ''); if (anchorTag.closest('.marquee, .aside, .hero-marquee, .quiz-marquee') && !anchorTag.hash) anchorTag.hash = '#autoplay'; const { dataset, parentElement } = anchorTag; From 0248ad98ef6b5cc79013abe96547a4cc7240eaf3 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Fri, 15 Nov 2024 17:08:17 +0530 Subject: [PATCH 32/36] playpause wrapper adjusted for window --- libs/blocks/video/video.css | 9 ++++++++- libs/utils/decorate.js | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 8d7c01cf83..d0bbe3abc6 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -37,7 +37,7 @@ video { } .video-container .pause-play-wrapper { - width: 45px; + width: 45.2px; height: 45px; position: absolute; bottom: 2%; @@ -147,3 +147,10 @@ video { right: auto; } } + +@media (min-width: 600px) and (max-width: 1199px) { + .hero-marquee.asset-left .video-container.video-holder .pause-play-wrapper { + left: auto; + right: 2%; + } +} diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 15251d7613..8bdab7ecde 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -462,7 +462,9 @@ export function decorateAnchorVideo({ src = '', anchorTag }) { const tabIndex = anchorTag.tabIndex || 0; const videoIndex = (tabIndex === -1) ? 'tabindex=-1' : ''; let video = ``; - videoCounter += 1; + if (!attrs.includes('controls') && !attrs.includes('hoverplay') && accessibilityEnabled) { + videoCounter += 1; + } const indexOfVideo = videoCounter; if (accessibilityEnabled) { video = addAccessibilityControl(video, attrs, indexOfVideo, tabIndex); From 0d964fa9113bd0e26f9f26eefb579120d3c279b6 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Mon, 18 Nov 2024 17:14:37 +0530 Subject: [PATCH 33/36] icon offset bug fix --- libs/blocks/video/video.css | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index d0bbe3abc6..c358a5939f 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -37,8 +37,6 @@ video { } .video-container .pause-play-wrapper { - width: 45.2px; - height: 45px; position: absolute; bottom: 2%; right: 2%; @@ -47,6 +45,7 @@ video { align-items: center; border-radius: 50%; z-index: 2; + padding: 3px; cursor: pointer; } @@ -60,25 +59,27 @@ video { background: var(--color-gray-800); } +:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper { + padding: 1px; +} + +:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper:focus { + background: #000; +} + :is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper .offset-filler { border: 2px solid #fff; } -:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper:focus-visible .offset-filler { - outline: 1px solid #000; +.video-container .pause-play-wrapper:focus { + background: #fff; } .video-container .pause-play-wrapper .offset-filler:hover { background: #000; } -.video-container .pause-play-wrapper:focus-visible .offset-filler { - border: 2px solid #fff; - outline: 1px solid #fff; -} - .video-container .pause-play-wrapper:focus-visible { - background: #000; outline: var(--color-accent-focus-ring) solid 2px; } From 958f9aae3e2304b0a77b174e36d17e6f0c330ae5 Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Wed, 27 Nov 2024 09:13:42 +0530 Subject: [PATCH 34/36] indentation of string literal --- libs/blocks/carousel/carousel.js | 6 ++++-- libs/utils/decorate.js | 16 +++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libs/blocks/carousel/carousel.js b/libs/blocks/carousel/carousel.js index ea375291bb..4bb57f7180 100644 --- a/libs/blocks/carousel/carousel.js +++ b/libs/blocks/carousel/carousel.js @@ -230,10 +230,12 @@ function moveSlides(event, carouselElements, jumpToIndex) { if (index < show) { tabIndex = 0; } - slide.querySelectorAll('a,:not(.video-container, .pause-play-wrapper) > video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', tabIndex); }); + slide.querySelectorAll('a,:not(.video-container, .pause-play-wrapper) > video') + .forEach((focusableElement) => { focusableElement.setAttribute('tabindex', tabIndex); }); }); } else { - activeSlide.querySelectorAll('a,:not(.video-container, .pause-play-wrapper) > video').forEach((focusableElement) => { focusableElement.setAttribute('tabindex', 0); }); + activeSlide.querySelectorAll('a,:not(.video-container, .pause-play-wrapper) > video') + .forEach((focusableElement) => { focusableElement.setAttribute('tabindex', 0); }); } activeSlideIndicator.classList.add('active'); activeSlideIndicator.setAttribute('tabindex', 0); diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 8bdab7ecde..580d2fcc1f 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -268,14 +268,16 @@ export function addAccessibilityControl(videoString, videoAttrs, indexOfVideo, t if (videoAttrs.includes('hoverplay')) { return `${videoString}`; } - return `
${videoString} - - `; + `; } export function handlePause(event) { From 518b4a09995062c0b3e18bf94cf2c15c9d6b63ef Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Mon, 2 Dec 2024 10:41:59 +0530 Subject: [PATCH 35/36] figma match --- libs/blocks/video/video.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index c358a5939f..7a568cc433 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -71,7 +71,7 @@ video { border: 2px solid #fff; } -.video-container .pause-play-wrapper:focus { +.video-container .pause-play-wrapper:focus-visible { background: #fff; } From 80a6f69b27b83283b62777c408c8422435eec99f Mon Sep 17 00:00:00 2001 From: sharath kannan Date: Mon, 2 Dec 2024 10:54:08 +0530 Subject: [PATCH 36/36] figma focus match --- libs/blocks/video/video.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/blocks/video/video.css b/libs/blocks/video/video.css index 7a568cc433..6538e0596a 100644 --- a/libs/blocks/video/video.css +++ b/libs/blocks/video/video.css @@ -63,7 +63,7 @@ video { padding: 1px; } -:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper:focus { +:is(.marquee:not(.light), .dark) .video-container .pause-play-wrapper:focus-visible { background: #000; }