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
This commit is contained in:
Ari Rizzitano
2017-03-21 10:30:41 -04:00
parent e9c0017450
commit 3cb0fd4156
7 changed files with 70 additions and 25 deletions

View File

@@ -986,9 +986,11 @@
videoWrapperHeight = $('.video-wrapper').height();
progressSliderHeight = state.el.find('.slider').height();
controlHeight = state.el.find('.video-controls').height();
shouldBeHeight = videoWrapperHeight -
shouldBeHeight = parseInt((
videoWrapperHeight -
0.5 * progressSliderHeight -
controlHeight;
controlHeight
), 10);
expect(realHeight).toBe(shouldBeHeight);
});

View File

@@ -44,7 +44,7 @@
expect(timeControl).toHaveAttrs({
'role': 'slider',
'aria-label': 'Video position',
'aria-label': 'Video position. Press space to toggle playback',
'aria-disabled': 'false'
});

View File

@@ -405,6 +405,8 @@ function(HTML5Video, Resizer) {
this.videoPlayer.goToStartTime = false;
this.videoPlayer.seekTo(time);
this.trigger('videoProgressSlider.focusSlider');
this.el.trigger('seek', [time, oldTime, type]);
}

View File

@@ -12,7 +12,9 @@ mind, or whether to act, and in acting, to live."
[],
function() {
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('');
// VideoProgressSlider() function - what this module "exports".
@@ -35,6 +37,8 @@ function() {
//
// Functions which will be accessible via 'state' object. When called,
// these functions will get the 'state' object as a context.
/* eslint-disable no-use-before-define */
function _makeFunctionsPublic(state) {
var methodsDict = {
destroy: destroy,
@@ -45,7 +49,8 @@ function() {
updatePlayTime: updatePlayTime,
updateStartEndTimeRegion: updateStartEndTimeRegion,
notifyThroughHandleEnd: notifyThroughHandleEnd,
getTimeDescription: getTimeDescription
getTimeDescription: getTimeDescription,
focusSlider: focusSlider
};
state.bindTo(methodsDict, state.videoProgressSlider, state);
@@ -57,6 +62,12 @@ function() {
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)
//
// Create any necessary DOM elements, attach them, and set their
@@ -69,6 +80,7 @@ function() {
state.el.find('.video-controls').prepend(state.videoProgressSlider.el);
state.videoProgressSlider.buildSlider();
_buildHandle(state);
bindHandlers(state);
}
function _buildHandle(state) {
@@ -77,7 +89,10 @@ function() {
// ARIA
// 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
// handle, behaves as a slider named 'video position'.
state.videoProgressSlider.handle.attr({
@@ -89,10 +104,8 @@ function() {
'aria-valuemin': '0',
'aria-valuenow': state.videoPlayer.currentTime,
'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() {
// ***************************************************************
function buildSlider() {
this.videoProgressSlider.el
.append('<div class="ui-slider-handle progress-handle"></div>');
var sliderContents = edx.HtmlUtils.joinHtml(
edx.HtmlUtils.HTML('<div class="ui-slider-handle progress-handle"></div>')
);
this.videoProgressSlider.el.append(sliderContents.text);
this.videoProgressSlider.slider = this.videoProgressSlider.el
.slider({
@@ -328,5 +344,21 @@ function() {
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));

View File

@@ -40,10 +40,10 @@ function(HtmlUtils) {
videoVolumeControlHtml: HtmlUtils.interpolateHtml(
HtmlUtils.HTML([
'<div class="volume" role="application">',
'<p class="sr instructions" id="volume-instructions">',
'<p class="sr instructions">',
'{volumeInstructions}',
'</p>',
'<button class="control" aria-disabled="false" aria-describedby="volume-instructions"',
'<button class="control" aria-disabled="false"',
'" aria-expanded="false" title="',
'{adjustVideoVolume}',
'">',
@@ -129,7 +129,8 @@ function(HtmlUtils) {
* initial configuration.
*/
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>'));
@@ -146,6 +147,10 @@ function(HtmlUtils) {
// order.
container.find('.volume-handle').attr('tabindex', -1);
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. */

View File

@@ -31,13 +31,13 @@
SpeedControl.prototype = {
template: [
'<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
'</p>',
'<button class="control speed-button" aria-disabled="false" aria-expanded="false"',
'title="',
gettext('Adjust video speed'),
'" aria-describedby="speed-instructions">',
'">',
'<span>',
'<span class="icon fa fa-caret-right" aria-hidden="true"></span>',
'</span>',
@@ -98,6 +98,7 @@
render: function(speeds, currentSpeed) {
var speedsContainer = this.speedsContainer,
reversedSpeeds = speeds.concat().reverse(),
instructionsId = 'speed-instructions-' + this.state.id,
speedsList = $.map(reversedSpeeds, function(speed) {
return HtmlUtils.interpolateHtml(
HtmlUtils.HTML(
@@ -125,6 +126,10 @@
HtmlUtils.HTML(this.el)
);
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);
},
/**

View File

@@ -88,20 +88,19 @@
'<span class="icon fa fa-quote-left" aria-hidden="true"></span>',
'</button>',
'<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"',
'aria-describedby="lang-instructions" ',
'aria-describedby="lang-instructions-{courseId}" ',
'title="{langTitle}">',
'<span class="icon fa fa-caret-left" aria-hidden="true"></span>',
'</button>',
'</div>',
'</div>'
].join(''),
{
langTitle: gettext('Open language menu')
}
)
].join('')),
{
langTitle: gettext('Open language menu'),
courseId: this.state.id
}
);
var subtitlesHtml = HtmlUtils.interpolateHtml(
@@ -109,7 +108,7 @@
[
'<div class="subtitles" role="region" id="transcript-{courseId}">',
'<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>'
].join('')),
{