Merge pull request #11369 from edx/clrux/ac-223

AC-223 updating transcript skip links
This commit is contained in:
clrux
2016-04-07 13:23:02 -04:00
15 changed files with 132 additions and 90 deletions

View File

@@ -646,7 +646,8 @@ hr.divider {
}
// ui - skipnav
.nav-skip {
.nav-skip,
.transcript-skip {
@extend %t-action3;
display: inline-block;
position: absolute;

View File

@@ -23,7 +23,8 @@ nav {
// ====================
// skip navigation
.nav-skip {
.nav-skip,
.transcript-skip {
@include font-size(13);
display: inline-block;
position: absolute;

View File

@@ -196,7 +196,7 @@ html:not('.afontgarde') .icon-fallback-img {
background-color: black;
position: relative;
.video-player-pre,
.video-player-pre,
.video-player-post {
height: 50px;
background-color: rgb(17, 16, 16) // UXPL grayscale black;
@@ -275,6 +275,12 @@ html:not('.afontgarde') .icon-fallback-img {
}
}
.video-error {
padding: ($baseline / 5);
background: black;
color: white !important; // the pattern library headings shim is more scoped
}
object,
iframe,
video {
@@ -723,6 +729,10 @@ html:not('.afontgarde') .icon-fallback-img {
font-size: 14px;
visibility: visible;
a {
color: #0074b5;
}
.subtitles-menu {
height: 100%;
margin: 0;

View File

@@ -26,7 +26,6 @@
</section>
</article>
</div>
<div class="focus_grabber last"></div>
</div>
</div>

View File

@@ -417,7 +417,7 @@
});
it('show explanation message', function () {
expect($('.subtitles-menu li')).toHaveHtml(
expect($('.subtitles-menu li')).toHaveText(
'Transcript will be displayed when you start playing the video.'
);
});

View File

@@ -132,7 +132,6 @@ function (VideoPlayer, i18n, moment) {
onYTApiReady = function () {
console.log('[Video info]: YouTube API is available and is loaded.');
if (state.htmlPlayerLoaded) { return; }
console.log('[Video info]: Starting YouTube player.');
@@ -140,7 +139,6 @@ function (VideoPlayer, i18n, moment) {
state.modules.push(video);
state.__dfd__.resolve();
state.youtubeApiAvailable = true;
};
@@ -211,7 +209,6 @@ function (VideoPlayer, i18n, moment) {
function _waitForYoutubeApi(state) {
console.log('[Video info]: Starting to wait for YouTube API to load.');
window.setTimeout(function () {
// If YouTube API will load OK, it will run `onYouTubeIframeAPIReady`
// callback, which will set `state.youtubeApiAvailable` to `true`.

View File

@@ -1,10 +1,11 @@
(function (define) {
(function(define) {
// VideoCaption module.
'use strict';
define(
'video/09_video_caption.js',
['video/00_sjson.js', 'video/00_async_process.js'],
define('video/09_video_caption.js', [
'video/00_sjson.js',
'video/00_async_process.js'
],
function (Sjson, AsyncProcess) {
/**
@@ -43,49 +44,6 @@
};
VideoCaption.prototype = {
langTemplate: [
'<div class="grouped-controls">',
'<button class="control toggle-captions" aria-disabled="false">',
'<span class="icon-fallback-img">',
'<span class="icon fa fa-cc" aria-hidden="true"></span>',
'<span class="sr control-text">',
gettext('Turn on closed captioning'),
'</span>',
'</span>',
'</button>',
'<button class="control toggle-transcript" aria-disabled="false">',
'<span class="icon-fallback-img">',
'<span class="icon fa fa-quote-left" aria-hidden="true"></span>',
'<span class="sr control-text">',
gettext('Turn off transcript'),
'</span>',
'</span>',
'</button>',
'<div class="lang menu-container" role="application">',
'<button class="control language-menu" aria-label="',
/* jshint maxlen:250 */
gettext('Language: Press the UP arrow key to enter the language menu, then use UP and DOWN arrow keys to navigate language options. Press ENTER to change to the selected language.'),
'" aria-disabled="false">',
'<span class="icon-fallback-img">',
'<span class="icon fa fa-caret-left" aria-hidden="true"></span>',
'<span class="sr control-text">',
gettext('Open language menu'),
'</span>',
'</span>',
'</button>',
'</div>',
'</div>'
].join(''),
template: [
'<div class="subtitles" role="region" aria-label="',
/* jshint maxlen:200 */
gettext('Activating an item in this group will spool the video to the corresponding time point. To skip transcript, go to previous item.'),
'">',
'<ol id="transcript-captions" class="subtitles-menu">',
'</ol>',
'</div>'
].join(''),
destroy: function () {
this.state.el
@@ -117,10 +75,46 @@
renderElements: function () {
var languages = this.state.config.transcriptLanguages;
var langTemplate = [
'<div class="grouped-controls">',
'<button class="control toggle-captions" aria-disabled="false">',
'<span class="icon-fallback-img">',
'<span class="icon fa fa-cc" aria-hidden="true"></span>',
'<span class="sr control-text"></span>',
'</span>',
'</button>',
'<button class="control toggle-transcript" aria-disabled="false">',
'<span class="icon-fallback-img">',
'<span class="icon fa fa-quote-left" aria-hidden="true"></span>',
'<span class="sr control-text"></span>',
'</span>',
'</button>',
'<div class="lang menu-container" role="application">',
'<button class="control language-menu" aria-label="" aria-disabled="false">',
'<span class="icon-fallback-img">',
'<span class="icon fa fa-caret-left" aria-hidden="true"></span>',
'<span class="sr control-text"></span>',
'</span>',
'</button>',
'</div>',
'</div>'
].join('');
var template = [
'<div class="subtitles" role="region" id="transcript-' + this.state.id + '">',
'<a href="#transcript-end-' + this.state.id + '"',
'id="transcript-start-' + this.state.id + '" class="transcript-start"></a>',
'<h3 id="transcript-label-' + this.state.id + '" class="transcript-title sr"></h3>',
'<ol id="transcript-captions" class="subtitles-menu"></ol>',
'<a href="#transcript-start-' + this.state.id + '"',
'id="transcript-end-' + this.state.id + '" class="transcript-end">\</a>',
'</div>'
].join('');
this.loaded = false;
this.subtitlesEl = $(this.template);
this.subtitlesEl = $(template);
this.subtitlesMenuEl = this.subtitlesEl.find('.subtitles-menu');
this.container = $(this.langTemplate);
this.container = $(langTemplate);
this.captionControlEl = this.container.find('.toggle-captions');
this.captionDisplayEl = this.state.el.find('.closed-captions');
this.transcriptControlEl = this.container.find('.toggle-transcript');
@@ -535,12 +529,9 @@
}
} else {
if (state.isTouch) {
self.subtitlesEl.find('.subtitles-menu').html(
gettext(
'<li>Transcript will be displayed when ' +
'you start playing the video.</li>'
)
);
self.subtitlesEl.find('.subtitles-menu')
.text(gettext('Transcript will be displayed when you start playing the video.')) // jshint ignore: line
.wrapInner('<li></li>');
} else {
self.renderCaption(start, captions);
}
@@ -747,6 +738,24 @@
self.isMouseFocus = false;
self.rendered = true;
self.state.el.addClass('is-captions-rendered');
self.subtitlesEl
.attr('aria-label', gettext('Activating a link in this group will skip to the corresponding point in the video.')); // jshint ignore:line
self.subtitlesEl.find('.transcript-title')
.text(gettext('Video transcript'));
self.subtitlesEl.find('.transcript-start')
.text(gettext('Start of transcript. Skip to the end.'));
self.subtitlesEl.find('.transcript-end')
.text(gettext('End of transcript. Skip to the start.'));
self.container.find('.menu-container .control')
.attr('aria-label', gettext('Language: Press the UP arrow key to enter the language menu then use UP and DOWN arrow keys to navigate language options. Press ENTER to change to the selected language.')); // jshint ignore:line
self.container.find('.menu-container .control .control-text')
.text(gettext('Open language menu.'));
};
this.rendered = false;

View File

@@ -1,3 +1,7 @@
AFontGarde('FontAwesome', {
glyphs: '&#61515;'
});
(function() {
'use strict';
window.AFontGarde('FontAwesome', {
glyphs: '&#61515;'
});
});

View File

@@ -57,7 +57,8 @@ VIDEO_MENUS = {
'language': '.lang .menu',
'speed': '.speed .menu',
'download_transcript': '.video-tracks .a11y-menu-list',
'transcript-format': '.video-tracks .a11y-menu-button'
'transcript-format': '.video-tracks .a11y-menu-button',
'transcript-skip': '.sr-is-focusable.transcript-start',
}
@@ -906,6 +907,17 @@ class VideoPage(PageObject):
classes = self.q(css=selector).attrs('class')[0].split()
return 'active' in classes
@property
def is_transcript_skip_visible(self):
"""
Checks if the skip-to containers in transcripts are present and visible.
Returns:
bool
"""
selector = self.get_element_selector(VIDEO_MENUS['transcript-skip'])
return self.q(css=selector).visible
def wait_for_captions(self):
"""
Wait until captions rendered completely.

View File

@@ -345,13 +345,18 @@ class CMSVideoA11yTest(CMSVideoBaseTest):
super(CMSVideoA11yTest, self).setUp()
def test_video_player_a11y(self):
# Limit the scope of the audit to the video player only.
self.outline.a11y_audit.config.set_scope(include=["div.video"])
self.outline.a11y_audit.config.set_rules({
"ignore": [
'link-href', # TODO: AC-223
],
})
# we're loading a shorter transcript to ensure both skip links are available
self._create_course_unit(subtitles=True)
self.edit_component()
self.video.upload_transcript('english_single_transcript.srt')
self._create_course_unit()
self.save_unit_settings()
self.video.wait_for_captions()
self.assertTrue(self.video.is_captions_visible())
# limit the scope of the audit to the video player only.
self.outline.a11y_audit.config.set_scope(
include=["div.video"],
exclude=["a.ui-slider-handle"]
)
self.outline.a11y_audit.check_for_accessibility_errors()

View File

@@ -1210,13 +1210,18 @@ class LMSVideoModuleA11yTest(VideoBaseTest):
super(LMSVideoModuleA11yTest, self).setUp()
def test_video_player_a11y(self):
self.navigate_to_video()
# load transcripts so we can test skipping to
self.assets.extend(['english_single_transcript.srt', 'subs_3_yD_cEKoCk.srt.sjson'])
data = {'transcripts': {"en": "english_single_transcript.srt"}, 'sub': '3_yD_cEKoCk'}
self.metadata = self.metadata_for_mode('youtube', additional_data=data)
# Limit the scope of the audit to the video player only.
self.video.a11y_audit.config.set_scope(include=["div.video"])
self.video.a11y_audit.config.set_rules({
"ignore": [
'link-href', # TODO: AC-223
],
})
# go to video
self.navigate_to_video()
self.video.show_captions()
# limit the scope of the audit to the video player only.
self.video.a11y_audit.config.set_scope(
include=["div.video"],
exclude=["a.ui-slider-handle"]
)
self.video.a11y_audit.check_for_accessibility_errors()

View File

@@ -0,0 +1,3 @@
1
00:00:01,000 --> 00:00:24,400
Hellow, 'Orld!

View File

@@ -333,7 +333,8 @@ mark {
}
// UI - skipnav
.nav-skip {
.nav-skip,
.transcript-skip {
@extend %ui-print-excluded;
display: inline-block;

View File

@@ -20,7 +20,7 @@
@extend %text-sr;
}
.nav-contents, .nav-skip {
.nav-contents, .nav-skip, .transcript-skip {
@extend %nav-skip;
}

View File

@@ -19,8 +19,6 @@ from openedx.core.djangolib.js_utils import js_escaped_string
<div class="focus_grabber first"></div>
<div class="tc-wrapper">
<a href="#before-transcript_${id}" class="nav-skip sr">${_("Skip to a navigable version of this video's transcript.")}</a>
<article class="video-wrapper">
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
<span tabindex="-1" class="btn-play is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
@@ -37,12 +35,9 @@ from openedx.core.djangolib.js_utils import js_escaped_string
<div class="secondary-controls"></div>
</div>
</section>
<a class="nav-skip sr" id="before-transcript_${id}" href="#after-transcript_${id}">${_('Skip to end of transcript.')}</a>
</article>
</div>
<a class="nav-skip sr" id="after-transcript_${id}" href="#before-transcript_${id}">${_('Go back to start of transcript.')}</a>
<div class="focus_grabber last"></div>
<ul class="wrapper-downloads">
% if download_video_link: