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:
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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'
|
||||
});
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -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('')),
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user