Commit 3cb0fd41 by Ari Rizzitano

adjust focus order and fix duplicate ids in video player (TNL-6361, AC-587)

move focus to slider on caption select (TNL-6361)

toggle video playback on spacebar press

sr text for spacebar explanation

focus on slider container instead of handle

linter issues

cleanup

quality

more quality

ugh wrong var name

hack to get around linter

more quality

pls work this time

fix transcript height test

hopefully last one

fix some ie/ff issues

fix dupe ids in video sr instructions. [AC-587]

fix test
parent e9c00174
...@@ -986,9 +986,11 @@ ...@@ -986,9 +986,11 @@
videoWrapperHeight = $('.video-wrapper').height(); videoWrapperHeight = $('.video-wrapper').height();
progressSliderHeight = state.el.find('.slider').height(); progressSliderHeight = state.el.find('.slider').height();
controlHeight = state.el.find('.video-controls').height(); controlHeight = state.el.find('.video-controls').height();
shouldBeHeight = videoWrapperHeight - shouldBeHeight = parseInt((
videoWrapperHeight -
0.5 * progressSliderHeight - 0.5 * progressSliderHeight -
controlHeight; controlHeight
), 10);
expect(realHeight).toBe(shouldBeHeight); expect(realHeight).toBe(shouldBeHeight);
}); });
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
expect(timeControl).toHaveAttrs({ expect(timeControl).toHaveAttrs({
'role': 'slider', 'role': 'slider',
'aria-label': 'Video position', 'aria-label': 'Video position. Press space to toggle playback',
'aria-disabled': 'false' 'aria-disabled': 'false'
}); });
......
...@@ -405,6 +405,8 @@ function(HTML5Video, Resizer) { ...@@ -405,6 +405,8 @@ function(HTML5Video, Resizer) {
this.videoPlayer.goToStartTime = false; this.videoPlayer.goToStartTime = false;
this.videoPlayer.seekTo(time); this.videoPlayer.seekTo(time);
this.trigger('videoProgressSlider.focusSlider');
this.el.trigger('seek', [time, oldTime, type]); this.el.trigger('seek', [time, oldTime, type]);
} }
......
...@@ -12,7 +12,9 @@ mind, or whether to act, and in acting, to live." ...@@ -12,7 +12,9 @@ mind, or whether to act, and in acting, to live."
[], [],
function() { function() {
var template = [ var template = [
'<div class="slider" title="', gettext('Video position'), '"></div>' '<div class="slider" role="application" title="',
gettext('Video position. Press space to toggle playback'),
'"></div>'
].join(''); ].join('');
// VideoProgressSlider() function - what this module "exports". // VideoProgressSlider() function - what this module "exports".
...@@ -35,6 +37,8 @@ function() { ...@@ -35,6 +37,8 @@ function() {
// //
// Functions which will be accessible via 'state' object. When called, // Functions which will be accessible via 'state' object. When called,
// these functions will get the 'state' object as a context. // these functions will get the 'state' object as a context.
/* eslint-disable no-use-before-define */
function _makeFunctionsPublic(state) { function _makeFunctionsPublic(state) {
var methodsDict = { var methodsDict = {
destroy: destroy, destroy: destroy,
...@@ -45,7 +49,8 @@ function() { ...@@ -45,7 +49,8 @@ function() {
updatePlayTime: updatePlayTime, updatePlayTime: updatePlayTime,
updateStartEndTimeRegion: updateStartEndTimeRegion, updateStartEndTimeRegion: updateStartEndTimeRegion,
notifyThroughHandleEnd: notifyThroughHandleEnd, notifyThroughHandleEnd: notifyThroughHandleEnd,
getTimeDescription: getTimeDescription getTimeDescription: getTimeDescription,
focusSlider: focusSlider
}; };
state.bindTo(methodsDict, state.videoProgressSlider, state); state.bindTo(methodsDict, state.videoProgressSlider, state);
...@@ -57,6 +62,12 @@ function() { ...@@ -57,6 +62,12 @@ function() {
delete this.videoProgressSlider; delete this.videoProgressSlider;
} }
function bindHandlers(state) {
state.videoProgressSlider.el.on('keypress', sliderToggle.bind(state));
state.el.on('destroy', state.videoProgressSlider.destroy);
}
/* eslint-enable no-use-before-define */
// function _renderElements(state) // function _renderElements(state)
// //
// Create any necessary DOM elements, attach them, and set their // Create any necessary DOM elements, attach them, and set their
...@@ -69,6 +80,7 @@ function() { ...@@ -69,6 +80,7 @@ function() {
state.el.find('.video-controls').prepend(state.videoProgressSlider.el); state.el.find('.video-controls').prepend(state.videoProgressSlider.el);
state.videoProgressSlider.buildSlider(); state.videoProgressSlider.buildSlider();
_buildHandle(state); _buildHandle(state);
bindHandlers(state);
} }
function _buildHandle(state) { function _buildHandle(state) {
...@@ -77,7 +89,10 @@ function() { ...@@ -77,7 +89,10 @@ function() {
// ARIA // ARIA
// We just want the knob to be selectable with keyboard // We just want the knob to be selectable with keyboard
state.videoProgressSlider.el.attr('tabindex', -1); state.videoProgressSlider.el.attr({
tabindex: -1
});
// Let screen readers know that this div, representing the slider // Let screen readers know that this div, representing the slider
// handle, behaves as a slider named 'video position'. // handle, behaves as a slider named 'video position'.
state.videoProgressSlider.handle.attr({ state.videoProgressSlider.handle.attr({
...@@ -89,10 +104,8 @@ function() { ...@@ -89,10 +104,8 @@ function() {
'aria-valuemin': '0', 'aria-valuemin': '0',
'aria-valuenow': state.videoPlayer.currentTime, 'aria-valuenow': state.videoPlayer.currentTime,
'tabindex': '0', 'tabindex': '0',
'aria-label': gettext('Video position') 'aria-label': gettext('Video position. Press space to toggle playback')
}); });
state.el.on('destroy', state.videoProgressSlider.destroy);
} }
// *************************************************************** // ***************************************************************
...@@ -103,8 +116,11 @@ function() { ...@@ -103,8 +116,11 @@ function() {
// *************************************************************** // ***************************************************************
function buildSlider() { function buildSlider() {
this.videoProgressSlider.el var sliderContents = edx.HtmlUtils.joinHtml(
.append('<div class="ui-slider-handle progress-handle"></div>'); edx.HtmlUtils.HTML('<div class="ui-slider-handle progress-handle"></div>')
);
this.videoProgressSlider.el.append(sliderContents.text);
this.videoProgressSlider.slider = this.videoProgressSlider.el this.videoProgressSlider.slider = this.videoProgressSlider.el
.slider({ .slider({
...@@ -328,5 +344,21 @@ function() { ...@@ -328,5 +344,21 @@ function() {
return i18n(seconds, 'second'); return i18n(seconds, 'second');
} }
// Shift focus to the progress slider container element.
function focusSlider() {
this.videoProgressSlider.handle.attr(
'aria-valuetext', getTimeDescription(this.videoPlayer.currentTime)
);
this.videoProgressSlider.el.trigger('focus');
}
// Toggle video playback when the spacebar is pushed.
function sliderToggle(e) {
if (e.which === 32) {
e.preventDefault();
this.videoCommands.execute('togglePlayback');
}
}
}); });
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); }(RequireJS.requirejs, RequireJS.require, RequireJS.define));
...@@ -40,10 +40,10 @@ function(HtmlUtils) { ...@@ -40,10 +40,10 @@ function(HtmlUtils) {
videoVolumeControlHtml: HtmlUtils.interpolateHtml( videoVolumeControlHtml: HtmlUtils.interpolateHtml(
HtmlUtils.HTML([ HtmlUtils.HTML([
'<div class="volume" role="application">', '<div class="volume" role="application">',
'<p class="sr instructions" id="volume-instructions">', '<p class="sr instructions">',
'{volumeInstructions}', '{volumeInstructions}',
'</p>', '</p>',
'<button class="control" aria-disabled="false" aria-describedby="volume-instructions"', '<button class="control" aria-disabled="false"',
'" aria-expanded="false" title="', '" aria-expanded="false" title="',
'{adjustVideoVolume}', '{adjustVideoVolume}',
'">', '">',
...@@ -129,7 +129,8 @@ function(HtmlUtils) { ...@@ -129,7 +129,8 @@ function(HtmlUtils) {
* initial configuration. * initial configuration.
*/ */
render: function() { render: function() {
var container = this.el.find('.volume-slider'); var container = this.el.find('.volume-slider'),
instructionsId = 'volume-instructions-' + this.state.id;
HtmlUtils.append(container, HtmlUtils.HTML('<div class="ui-slider-handle volume-handle"></div>')); HtmlUtils.append(container, HtmlUtils.HTML('<div class="ui-slider-handle volume-handle"></div>'));
...@@ -146,6 +147,10 @@ function(HtmlUtils) { ...@@ -146,6 +147,10 @@ function(HtmlUtils) {
// order. // order.
container.find('.volume-handle').attr('tabindex', -1); container.find('.volume-handle').attr('tabindex', -1);
this.state.el.find('.secondary-controls').append(this.el); this.state.el.find('.secondary-controls').append(this.el);
// set dynamic id for instruction element to avoid collisions
this.el.find('.instructions').attr('id', instructionsId);
this.button.attr('aria-describedby', instructionsId);
}, },
/** Bind any necessary function callbacks to DOM events. */ /** Bind any necessary function callbacks to DOM events. */
......
...@@ -31,13 +31,13 @@ ...@@ -31,13 +31,13 @@
SpeedControl.prototype = { SpeedControl.prototype = {
template: [ template: [
'<div class="speeds menu-container" role="application">', '<div class="speeds menu-container" role="application">',
'<p class="sr instructions" id="speed-instructions">', '<p class="sr instructions">',
gettext('Press UP to enter the speed menu then use the UP and DOWN arrow keys to navigate the different speeds, then press ENTER to change to the selected speed.'), // eslint-disable-line max-len, indent gettext('Press UP to enter the speed menu then use the UP and DOWN arrow keys to navigate the different speeds, then press ENTER to change to the selected speed.'), // eslint-disable-line max-len, indent
'</p>', '</p>',
'<button class="control speed-button" aria-disabled="false" aria-expanded="false"', '<button class="control speed-button" aria-disabled="false" aria-expanded="false"',
'title="', 'title="',
gettext('Adjust video speed'), gettext('Adjust video speed'),
'" aria-describedby="speed-instructions">', '">',
'<span>', '<span>',
'<span class="icon fa fa-caret-right" aria-hidden="true"></span>', '<span class="icon fa fa-caret-right" aria-hidden="true"></span>',
'</span>', '</span>',
...@@ -98,6 +98,7 @@ ...@@ -98,6 +98,7 @@
render: function(speeds, currentSpeed) { render: function(speeds, currentSpeed) {
var speedsContainer = this.speedsContainer, var speedsContainer = this.speedsContainer,
reversedSpeeds = speeds.concat().reverse(), reversedSpeeds = speeds.concat().reverse(),
instructionsId = 'speed-instructions-' + this.state.id,
speedsList = $.map(reversedSpeeds, function(speed) { speedsList = $.map(reversedSpeeds, function(speed) {
return HtmlUtils.interpolateHtml( return HtmlUtils.interpolateHtml(
HtmlUtils.HTML( HtmlUtils.HTML(
...@@ -125,6 +126,10 @@ ...@@ -125,6 +126,10 @@
HtmlUtils.HTML(this.el) HtmlUtils.HTML(this.el)
); );
this.setActiveSpeed(currentSpeed); this.setActiveSpeed(currentSpeed);
// set dynamic id for instruction element to avoid collisions
this.el.find('.instructions').attr('id', instructionsId);
this.speedButton.attr('aria-describedby', instructionsId);
}, },
/** /**
......
...@@ -88,20 +88,19 @@ ...@@ -88,20 +88,19 @@
'<span class="icon fa fa-quote-left" aria-hidden="true"></span>', '<span class="icon fa fa-quote-left" aria-hidden="true"></span>',
'</button>', '</button>',
'<div class="lang menu-container" role="application">', '<div class="lang menu-container" role="application">',
'<p class="sr instructions" id="lang-instructions"></p>', '<p class="sr instructions" id="lang-instructions-{courseId}"></p>',
'<button class="control language-menu" aria-disabled="false"', '<button class="control language-menu" aria-disabled="false"',
'aria-describedby="lang-instructions" ', 'aria-describedby="lang-instructions-{courseId}" ',
'title="{langTitle}">', 'title="{langTitle}">',
'<span class="icon fa fa-caret-left" aria-hidden="true"></span>', '<span class="icon fa fa-caret-left" aria-hidden="true"></span>',
'</button>', '</button>',
'</div>', '</div>',
'</div>' '</div>'
].join(''), ].join('')),
{ {
langTitle: gettext('Open language menu') langTitle: gettext('Open language menu'),
} courseId: this.state.id
) }
); );
var subtitlesHtml = HtmlUtils.interpolateHtml( var subtitlesHtml = HtmlUtils.interpolateHtml(
...@@ -109,7 +108,7 @@ ...@@ -109,7 +108,7 @@
[ [
'<div class="subtitles" role="region" id="transcript-{courseId}">', '<div class="subtitles" role="region" id="transcript-{courseId}">',
'<h3 id="transcript-label-{courseId}" class="transcript-title sr"></h3>', '<h3 id="transcript-label-{courseId}" class="transcript-title sr"></h3>',
'<ol id="transcript-captions" class="subtitles-menu" lang="{courseLang}"></ol>', '<ol id="transcript-captions-{courseId}" class="subtitles-menu" lang="{courseLang}"></ol>',
'</div>' '</div>'
].join('')), ].join('')),
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment