-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[CD-483] Video SDC
- Loading branch information
Showing
7 changed files
with
299 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Video | ||
|
||
Produces a YouTube embed from a URL stored in a Drupal Link field. It's basically a reimplementation of [lite-youtube-embed](https://github.com/paulirish/lite-youtube-embed) by Paul Irish, but in Twig. | ||
|
||
This component has been tested over the years on multiple World Humanitarian Day campaigns and has met the following requirements: | ||
|
||
- **Performant:** loads minimal code/data up front, waits for user interaction before downloading video. | ||
- **Accessible:** has been tested to ensure it meets our standards for screen-reader UX and keyboard nav. | ||
- **Translatable:** all necessary labels can be translated by Drupal. | ||
- **Trackable:** integrates with GTM out of the box by allowing the necessary permissions within the iframe that gets generated by JS. | ||
- **Less spammy:** once the video has finished playing, it will only recommend videos from the same channel. | ||
|
||
## Outline of Progressive Enhancement | ||
|
||
1. When the page loads, it displays a responsive preview image with a YouTube play button superimposed over the preview. The video transcript is totally optional, but can be used to supplement the video for screen-reader users and requires no additional JS to be read. It uses the `<details>`/`<summary>` HTML elements and their built-in interactivity. | ||
2. When the cursor hovers over the image, it will preconnect to YouTube. | ||
3. When the user finally clicks the image, it will load the actual video embed and attempt to autoplay. | ||
|
||
## Caveats | ||
|
||
The `video_slug`/`video_url` properties are rarely entered and stored directly in Drupal. Rather, we often have Editors enter a YouTube URL as displayed in the browser, and preprocess it into what we need. This example preprocess is intended for a Paragraph with machine name `video` which contains a Link field named `field_video_url`. | ||
|
||
```php | ||
/** | ||
* Implements hook_preprocess_paragraph__type(). | ||
* | ||
* Example preprocess hook that converts a Link field that contains a | ||
* fully-qualified URL pointing to YouTube into the video slug/url. | ||
* | ||
* Example: https://www.youtube.com/watch?v=ScMzIvxBSi4 | ||
*/ | ||
function THEME_preprocess_paragraph__video(&$variables) { | ||
/** @var \Drupal\paragraphs\Entity\Paragraph $paragraph */ | ||
$paragraph = $variables['paragraph']; | ||
|
||
if (!$paragraph->field_video_url->isEmpty()) { | ||
// Extract video slug from YouTube. | ||
$qs = parse_url($paragraph->get('field_video_url')->first()->uri, PHP_URL_QUERY); | ||
parse_str($qs, $params); | ||
$variables['video_slug'] = $params['v']; | ||
$variables['video_url'] = $paragraph->get('field_video_url')->first()->uri; | ||
} | ||
} | ||
``` | ||
|
||
## Future TODOs | ||
|
||
- Once `sizes="auto"` is supported in evergreen browsers, we should use it. For the time being, we used some simple defaults that match the CD layout container. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
$schema: https://git.drupalcode.org/project/drupal/-/raw/10.1.x/core/modules/sdc/src/metadata.schema.json | ||
name: Video | ||
status: experimental | ||
props: | ||
type: object | ||
required: | ||
- attributes | ||
- title | ||
- video_url | ||
- video_slug | ||
properties: | ||
attributes: | ||
title: Drupal attributes | ||
type: Drupal\Core\Template\Attribute | ||
classes: | ||
title: Extra classnames | ||
type: array | ||
items: | ||
type: string | ||
title: | ||
title: Title | ||
type: string | ||
description: The title of the Video. | ||
examples: | ||
- Humanitarians are \#NotATarget | ||
video_url: | ||
title: Video URL | ||
type: string | ||
description: The publicly-viewable canonical URL of the Video. | ||
examples: | ||
- https://www.youtube.com/watch?v=p5zzrlNtmLo | ||
video_slug: | ||
title: Video Slug | ||
type: string | ||
description: The unique identifier extracted from the Video URL. Look in the README to see how this is provided to the Drupal template which implements this component. Compare example slug value to the `video_url` example. | ||
examples: | ||
- p5zzrlNtmLo | ||
video_transcript: | ||
title: Video transcript | ||
type: string | ||
description: A plaintext transcript of the video, or including a written description of any critical content within the video. If you send HTML, the tags will be visible in the output. | ||
examples: | ||
- | | ||
A man is shown handing out supplies to a group of children. The camera zooms in towards his face. | ||
Narrator: While bringing food, relief supplies, and medicine, we are at risk of being looted, harassed and even killed. | ||
Humanitarian workers are never the enemy. | ||
Face world leaders to say civilians are \#NotATarget | ||
World Humanitarian Day | ||
19. August 2018 | ||
WorldHumanitarianDay.org | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/** | ||
* Video Embed | ||
*/ | ||
.video { | ||
--video-padding: 0; | ||
--video-margin: 1rem; | ||
|
||
position: relative; | ||
overflow: hidden; | ||
|
||
/* Legacy layout (IE) */ | ||
width: 100%; | ||
height: 0; | ||
padding-bottom: 56.25%; | ||
} | ||
|
||
/* Modern layout */ | ||
@supports (--css-vars: true) { | ||
.video { | ||
aspect-ratio: 16 / 9; | ||
width: 100%; | ||
height: unset; | ||
margin-block-start: 1rem; | ||
padding: var(--video-padding); | ||
border-radius: 5px; | ||
} | ||
} | ||
|
||
.video__link { | ||
display: block; | ||
} | ||
|
||
.video__img { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
|
||
.video__button { | ||
position: absolute; | ||
z-index: 10; | ||
display: block; | ||
width: 73.4px; | ||
height: 51.8px; | ||
cursor: pointer; | ||
transform: translate(-50%, -50%); | ||
border: none; | ||
border-radius: 13px; | ||
background: transparent url('button-youtube.png') no-repeat 50% 50%; | ||
background-size: contain; | ||
inset: 50%; | ||
} | ||
|
||
.video__button:focus-visible { | ||
outline: 3px solid var(--cd-white); | ||
outline-offset: -2px; | ||
} | ||
|
||
.video__iframe { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
|
||
.video__transcript { | ||
margin-block-start: 1rem; | ||
border-radius: 5px; | ||
background: var(--brand-grey); | ||
} | ||
|
||
/* normalize.css removes built-in browser affordance for <summary> so we are | ||
resetting its display */ | ||
.video__transcript-label { | ||
display: list-item; | ||
cursor: pointer; | ||
border-radius: 5px; | ||
padding-block: .25rem; | ||
padding-inline: 1rem; | ||
font-weight: 700; | ||
} | ||
|
||
.video__transcript-label:focus-visible { | ||
outline: 3px solid var(--cd-black); | ||
} | ||
|
||
.video__transcript-text { | ||
padding: 1rem; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
(function ($) { | ||
'use strict'; | ||
|
||
// The videos get delivered as links to YouTube with image tags inside. Each | ||
// one needs to be prepped by JS with the following steps: | ||
// - Add a JS button to dynamically load the video when clicked | ||
// - Setup the listener for the button click | ||
const videos = document.querySelectorAll('.video'); | ||
videos.forEach((thisVideo) => { | ||
// Create basic button with contents/styling | ||
const thisButton = document.createElement('button'); | ||
thisButton.classList.add('video__button'); | ||
thisButton.innerHTML = '<span class="visually-hidden">' + thisVideo.dataset.videoButtonLabel + '</span>'; | ||
|
||
// Listen for interaction and load video | ||
thisButton.addEventListener('click', (ev) => { | ||
prepVideo(thisVideo); | ||
}); | ||
|
||
// Insert button into DOM. | ||
thisVideo.appendChild(thisButton); | ||
|
||
// Prevent link from leading away now, and override with video | ||
const originalLink = thisVideo.querySelector('a'); | ||
originalLink.addEventListener('click', (ev) => { | ||
prepVideo(thisVideo); | ||
ev.preventDefault(); | ||
}); | ||
|
||
// Wait for cursor to hover and warm the connection when we find it. | ||
originalLink.addEventListener('pointerover', (ev) => { | ||
if (thisVideo.dataset.videoConnected === 'true') { | ||
return; | ||
} | ||
|
||
addPrefetch('preconnect', 'https://www.youtube.com'); | ||
addPrefetch('preconnect', 'https://www.google.com'); | ||
thisVideo.dataset.videoConnected = 'true'; | ||
}); | ||
|
||
}); | ||
|
||
// Accepts a DOM container and replaces its contents with a YouTube iframe. | ||
function prepVideo(video) { | ||
video.innerHTML = '<iframe class="video__iframe" src="https://www.youtube.com/embed/' + video.dataset.videoSlug + '?autoplay=1&playsinline=1&rel=0&enablejsapi=1&origin=' + encodeURIComponent(video.dataset.videoOrigin) + '" frameborder="0" allow="autoplay; fullscreen" sandbox="allow-same-origin allow-scripts"></iframe>'; | ||
} | ||
|
||
// Warm TCP connections by adding <link>s to the <head>. | ||
function addPrefetch(kind, url, as) { | ||
const linkEl = document.createElement('link'); | ||
linkEl.rel = kind; | ||
linkEl.href = url; | ||
if (as) { | ||
linkEl.as = as; | ||
} | ||
document.head.append(linkEl); | ||
} | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<article{{ attributes.addClass(classes) }} aria-label="{{ 'Video'|t }}"> | ||
<h3 class="visually-hidden">{{ title }}</h3> | ||
<div class="video" | ||
data-video-slug="{{ video_slug }}" | ||
data-video-button-label="{{ 'Load video'|t }}" | ||
data-video-origin="{{ url('<front>')|render|trim('/') }}"> | ||
<a href="{{ video_url }}" | ||
target="_blank" | ||
rel="noopener" | ||
class="video__link"> | ||
<img | ||
class="video__img" | ||
loading="lazy" | ||
src="https://img.youtube.com/vi/{{ video_slug }}/maxresdefault.jpg" | ||
srcset="https://img.youtube.com/vi/{{ video_slug }}/mqdefault.jpg 320w, | ||
https://img.youtube.com/vi/{{ video_slug }}/hqdefault.jpg 480w, | ||
https://img.youtube.com/vi/{{ video_slug }}/sddefault.jpg 640w, | ||
https://img.youtube.com/vi/{{ video_slug }}/maxresdefault.jpg 1280w" | ||
sizes="(min-width: 1220px) 1200px, | ||
calc(100% - 1.5rem)" | ||
alt="{{ 'Preview of the video from YouTube'|t }}"> | ||
</a> | ||
</div> | ||
|
||
{% if video_transcript is not empty %} | ||
<details class="video__transcript"> | ||
<summary class="video__transcript-label">{{ 'Video transcript'|t }}</summary> | ||
<div class="video__transcript-text"> | ||
{{ video_transcript }} | ||
</div> | ||
</details> | ||
{% endif %} | ||
</article> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters