Skip to content
This repository has been archived by the owner on Nov 26, 2021. It is now read-only.

Commit

Permalink
Show guidance when subtitles aren't available (#140)
Browse files Browse the repository at this point in the history
* add state to identify if video was started via autoplay

* render guidance component on placeholder

* style guidance banner

* rejig things to handle video reloading on homepage

* convert guidance to a class

* style close button

* WIP tests

* hover state on close button

* close banner after 5 seconds

* wrap guidance in mixin

* hide banner when it auto closes
this way screen readers can still access it

* add tests for guidance

* link stright to video transcripts section

* tidying

* linting

* use font smoothing to match the placeholder

* remove need for o-buttons

* clear timeout if banner removed early

- prevents banner disappearing early when a skipped to the next video
in a playlist before the previous banenr was hidden

* implement listen once

- `once` option not suppported by IE 11
  • Loading branch information
robsquires authored Jul 8, 2019
1 parent 7e117b4 commit cb64aa7
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 28 deletions.
4 changes: 3 additions & 1 deletion main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ $o-video-is-silent: true !default;
@import "src/scss/base";
@import "src/scss/info";
@import "src/scss/placeholder";

@import "src/scss/guidance";

/// @access public
/// @param {Map} $opts - ['attributes': ('ads', 'info', 'placeholder'), 'sizes': ('small', 'medium', 'large')]
Expand All @@ -35,6 +35,8 @@ $o-video-is-silent: true !default;
@if index($attributes, 'info') {
@include _oVideoInfo($sizes);
}

@include _oVideoGuidance();
}

@if $o-video-is-silent == false {
Expand Down
66 changes: 66 additions & 0 deletions src/js/guidance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* eslint class-methods-use-this: 0 */

const closeButton = (onClick) => {
const button = document.createElement('button');
button.className = 'o-video__guidance__close';
button.addEventListener('click', e => {
e.stopPropagation();
onClick();
});
return button;
};

const container = (bannerMode) => {
const containerEl = document.createElement('div');
containerEl.className = `o-video__guidance ${bannerMode ? 'o-video__guidance--banner' : ''}`;
return containerEl;
};

const link = () => {
const linkEl = document.createElement('a');
linkEl.setAttribute('href', 'https://www.ft.com/accessibility#video-transcriptions');
linkEl.className = 'o-video__guidance__link';
linkEl.innerText = 'Subtitles unavailable';
linkEl.target = '_blank';
linkEl.addEventListener('click', e => e.stopPropagation());
return linkEl;
};

class Guidance {

constructor () {
this.removeBanner = this.removeBanner.bind(this);
this.hideBanner = this.hideBanner.bind(this);
}

createPlaceholder () {
const containerEl = container();
containerEl.appendChild(link());
return containerEl;
}

createBanner () {
this.banner = container(true);
this.banner.appendChild(closeButton(this.removeBanner));
this.banner.appendChild(link());

this.timeout = setTimeout(this.hideBanner, 5000);

return this.banner;
}

removeBanner () {
if (this.banner) {
this.banner.remove();
clearTimeout(this.timeout);
}
}

hideBanner () {
if (this.banner) {
this.banner.classList.add('o-video__guidance--hidden');
}
}
}

export default Guidance;
41 changes: 39 additions & 2 deletions src/js/video.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import getRendition from './helpers/get-rendition';
import VideoAds from './ads';
import VideoInfo from './info';
import Playlist from './playlist';
import Guidance from './guidance';

function listenOnce(el, eventName, fn) {
const wrappedFn = function(...args) {
el.removeEventListener(eventName, wrappedFn);
fn(...args);
};
el.addEventListener(eventName, wrappedFn);
}

function eventListener(video, ev) {

Expand Down Expand Up @@ -159,6 +168,7 @@ class Video {
this.amountWatched = 0;
this.fireWatchedEvent = unloadListener.bind(this);
this.visibilityListener = visibilityListener.bind(this);
this.didUserPressPlay = false;

this.opts = Object.assign({}, defaultOpts, opts, getOptionsFromDataAttributes(this.containerEl.attributes));

Expand Down Expand Up @@ -186,6 +196,8 @@ class Video {
if (this.opts.autorender === true) {
this.init();
}

this.guidance = new Guidance();
}

getData() {
Expand Down Expand Up @@ -308,6 +320,8 @@ class Video {
}

this.videoEl.src = this.rendition && this.rendition.url;
this.guidance.removeBanner();
listenOnce(this.videoEl, 'playing', this.showGuidanceBanner.bind(this));

this.addCaptions();
}
Expand All @@ -329,17 +343,31 @@ class Video {
}

// play button
const playCTA = document.createElement('div');
playCTA.className = `o-video__play-cta ${this.opts.placeholderHint ? 'o-video__play-cta--with-hint' : 'o-video__play-cta--without-hint'}`;

const playButtonEl = document.createElement('button');
playButtonEl.className = 'o-video__play-button';

this.playButtonIconEl = document.createElement('span');
this.playButtonIconEl.className = 'o-video__play-button-icon';
this.playButtonIconEl.textContent = this.opts.placeholderHint;
playButtonEl.appendChild(this.playButtonIconEl);


playCTA.appendChild(this.playButtonIconEl);

const { captionsUrl } = this.videoData || {};
if (!captionsUrl) {
playCTA.appendChild(this.guidance.createPlaceholder());
}
playButtonEl.appendChild(playCTA);

this.placeholderEl.appendChild(playButtonEl);

this.placeholderEl.addEventListener('click', this.play.bind(this));
this.placeholderEl.addEventListener('click', () => {
this.didUserPressPlay = true;
this.play();
});

this.updatePlaceholder();

Expand Down Expand Up @@ -385,6 +413,8 @@ class Video {
}
this.clearCurrentlyPlaying();

this.didUserPressPlay = false;

this.opts = Object.assign(this.opts, { data: null }, newOpts);

if (!this.videoEl && !this.placeholderEl) {
Expand Down Expand Up @@ -451,6 +481,13 @@ class Video {
this.amountWatched = 0;
}

showGuidanceBanner () {
const { captionsUrl } = this.videoData || {};
if (!this.didUserPressPlay && !captionsUrl) {
this.containerEl.appendChild(this.guidance.createBanner());
}
}

destroy () {
// remove listeners
window.removeEventListener(unloadEventName, this.fireWatchedEvent);
Expand Down
80 changes: 80 additions & 0 deletions src/scss/_guidance.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/// @access private
/// Outputs styling for accessibility components

@mixin _oVideoGuidance {
$white: oColorsGetPaletteColor('white');

.o-video__guidance {
@include oTypographySans(-1);
background-color: oColorsMix($color: 'black', $background: 'transparent', $percentage: 75);
padding: 10px 16px;
// sass-lint:disable no-vendor-prefixes
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
// sass-lint:enable no-vendor-prefixes

&--banner {
padding: 6px;
position: absolute;
top: 0;
left: 0;
z-index: 2;
}

a {
color: $white;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

a:visited {
text-decoration: none;
}
}

.o-video__guidance--banner {
.o-video__guidance__link {
padding-left: 6px;
top: -4px;
position: relative;
}
}

.o-video__guidance--hidden {
visibility: hidden;
}

.o-video__guidance__link {
@include oTypographyLinkCustom($white, $white);
@include oTypographyLinkExternalIcon($white);
color: $white;
border-bottom: 0;
}

.o-video__guidance__close {
@include oIconsGetIcon('cross', $color: $white, $container-width: 20px);
border: 0;
cursor: pointer;

&:hover {
background-color: oColorsMix($color: 'black', $background: 'white', $percentage: 75);
}
}

.o-video--large {
.o-video__play-cta--without-hint {
.o-video__guidance {
padding: 21px 16px;
}
}
}

.o-video__play-cta--with-hint {
.o-video__guidance {
padding: 10px 16px;
}
}
}
7 changes: 1 addition & 6 deletions src/scss/_info.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,7 @@
line-height: 20px;
display: block;
}

// do not enlarge icons with hints!
.o-video__play-button-icon:empty {
width: 60px;
height: 60px;
}

}

/// @access private
Expand Down
49 changes: 30 additions & 19 deletions src/scss/_placeholder.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@
}

.o-video__play-button {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
bottom: 0;
padding: 0;
border: 0;
background-color: transparent;
Expand All @@ -45,28 +41,43 @@
.o-video__play-button-icon {
@include oIconsBaseStyles;
@include oIconsGetIcon('play', oColorsGetPaletteColor('white'), $apply-base-styles: false, $apply-width-height: false, $iconset-version: 1);
position: absolute;
color: oColorsGetPaletteColor('white');
background-color: oColorsGetPaletteColor('black');
:hover > &,
:focus > & {
background-color: oColorsGetPaletteColor('claret');
}
}

&:empty {
bottom: 0;
left: 0;
width: 40px;
height: 40px;
.o-video__play-cta {
position: absolute;
display: flex;
bottom: 0;
left: 0;

&--without-hint {
.o-video__play-button-icon {
width: 40px;
height: 40px;
}
}

&:not(:empty) {
@include oTypographySansBold(-1);
&--with-hint {
bottom: 10px;
left: 10px;
padding: 10px 16px 10px 36px;
background-position: left;
.o-video__play-button-icon {
@include oTypographySansBold(-1);
padding: 10px 16px 10px 36px;
background-position: left;
}
}

:hover > &,
:focus > & {
background-color: oColorsGetPaletteColor('claret');
}
.o-video--large {
.o-video__play-cta--without-hint {
.o-video__play-button-icon {
width: 60px;
height: 60px;
}
}
}
}
Loading

0 comments on commit cb64aa7

Please sign in to comment.