Commit fbe61604 by Chris Rodriguez

LMS: updating transcript skip links

parent b9b87f9b
......@@ -646,7 +646,8 @@ hr.divider {
}
// ui - skipnav
.nav-skip {
.nav-skip,
.transcript-skip {
@extend %t-action3;
display: inline-block;
position: absolute;
......
......@@ -23,7 +23,8 @@ nav {
// ====================
// skip navigation
.nav-skip {
.nav-skip,
.transcript-skip {
@include font-size(13);
display: inline-block;
position: absolute;
......
......@@ -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;
......
......@@ -26,7 +26,6 @@
</section>
</article>
</div>
<div class="focus_grabber last"></div>
</div>
</div>
......
......@@ -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.'
);
});
......
......@@ -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`.
......
(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;
......
AFontGarde('FontAwesome', {
glyphs: '&#61515;'
});
\ No newline at end of file
(function() {
'use strict';
window.AFontGarde('FontAwesome', {
glyphs: '&#61515;'
});
});
......@@ -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.
......
......@@ -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()
......@@ -1210,13 +1210,18 @@ class LMSVideoModuleA11yTest(VideoBaseTest):
super(LMSVideoModuleA11yTest, self).setUp()
def test_video_player_a11y(self):
# 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)
# 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"])
self.video.a11y_audit.config.set_rules({
"ignore": [
'link-href', # TODO: AC-223
],
})
# 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()
1
00:00:01,000 --> 00:00:24,400
Hellow, 'Orld!
......@@ -333,7 +333,8 @@ mark {
}
// UI - skipnav
.nav-skip {
.nav-skip,
.transcript-skip {
@extend %ui-print-excluded;
display: inline-block;
......
......@@ -20,7 +20,7 @@
@extend %text-sr;
}
.nav-contents, .nav-skip {
.nav-contents, .nav-skip, .transcript-skip {
@extend %nav-skip;
}
......
......@@ -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:
......
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