Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fix accessibility of the captions setting dialog #4050

Merged
merged 16 commits into from
Feb 21, 2017
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 121 additions & 59 deletions docs/translations-needed.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@
"Script": "Script",
"Small Caps": "Small Caps",
"Defaults": "Defaults",
"restore all settings to the default values": "restore all settings to the default values",
"Done": "Done",
"Caption Settings Dialog": "Caption Settings Dialog",
"Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window."
"Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.",
"End of dialog window.": "End of dialog window."
}
32 changes: 27 additions & 5 deletions src/css/components/_captions-settings.scss
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
.vjs-caption-settings {
.vjs-modal-dialog.vjs-text-track-settings {
position: relative;
top: 1em;
background-color: $primary-background-color;
background-color: rgba($primary-background-color, 0.75);
color: $primary-foreground-color;
margin: 0 auto;
padding: 0.5em;
height: 16em;
font-size: 12px;
width: 40em;
height: 22em;
width: 48em;
}

.vjs-caption-settings .vjs-tracksettings {
top: 0;
bottom: 1em;
bottom: 0;
left: 0;
right: 0;
position: absolute;
Expand All @@ -36,6 +35,29 @@
right: 1em;
}

.vjs-tracksettings-controls button:focus,
.vjs-tracksettings-controls button:active {
outline-style: solid;
outline-width: medium;
background-image: linear-gradient(0deg, $primary-foreground-color 88%, $secondary-background-color 100%);
}

.vjs-tracksettings-controls button:hover {
color: rgba(#2B333F, 0.75);
}

.vjs-tracksettings-controls button {
background-color: $primary-foreground-color;
background-image: linear-gradient(-180deg, $primary-foreground-color 88%, $secondary-background-color 100%);
color: #2B333F;
cursor: pointer;
border-radius: 2px;
}

.vjs-tracksettings-controls .vjs-default-button {
margin-right: 1em;
}

.vjs-caption-settings .vjs-tracksetting {
margin: 5px;
padding: 3px;
Expand Down
2 changes: 1 addition & 1 deletion src/css/video-js.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@import "components/big-play";
@import "components/button";
@import "components/close-button";
@import "components/modal-dialog";

@import "components/menu/menu";
@import "components/menu/menu-popup";
Expand Down Expand Up @@ -38,6 +39,5 @@
@import "components/audio";
@import "components/adaptive";
@import "components/captions-settings";
@import "components/modal-dialog";

@import "print";
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ class CaptionSettingsMenuItem extends TextTrackMenuItem {
* @listens click
*/
handleClick(event) {
this.player().getChild('textTrackSettings').show();
this.player().getChild('textTrackSettings').el_.focus();
this.player().getChild('textTrackSettings').open();
}

}
Expand Down
2 changes: 1 addition & 1 deletion src/js/modal-dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class ModalDialog extends Component {
});

this.descEl_ = Dom.createEl('p', {
className: `${MODAL_CLASS_NAME}-description vjs-offscreen`,
className: `${MODAL_CLASS_NAME}-description vjs-control-text`,
id: this.el().getAttribute('aria-describedby')
});

Expand Down
115 changes: 75 additions & 40 deletions src/js/tracks/text-track-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
* @file text-track-settings.js
*/
import window from 'global/window';
import document from 'global/document';
import Component from '../component';
import ModalDialog from '../modal-dialog';
import {createEl} from '../utils/dom';
import * as Fn from '../utils/fn';
import * as Obj from '../utils/obj';
Expand Down Expand Up @@ -236,9 +238,9 @@ function setSelectedOption(el, value, parser) {
/**
* Manipulate Text Tracks settings.
*
* @extends Component
* @extends ModalDialog
*/
class TextTrackSettings extends Component {
class TextTrackSettings extends ModalDialog {

/**
* Creates an instance of this class.
Expand All @@ -250,20 +252,34 @@ class TextTrackSettings extends Component {
* The key/value store of player options.
*/
constructor(player, options) {
options.temporary = false;

super(player, options);
this.setDefaults();
this.hide();

this.contentEl().className += ' vjs-caption-settings';

this.updateDisplay = Fn.bind(this, this.updateDisplay);

// fill the modal and pretend we have opened it
this.fill();
this.hasBeenOpened_ = this.hasBeenFilled_ = true;

this.endDialog = createEl('p', {
className: 'vjs-control-text',
textContent: this.localize('End of dialog window.')
});
this.el().appendChild(this.endDialog);

this.setDefaults();

// Grab `persistTextTrackSettings` from the player options if not passed in child options
if (options.persistTextTrackSettings === undefined) {
this.options_.persistTextTrackSettings = this.options_.playerOptions.persistTextTrackSettings;
}

this.on(this.$('.vjs-done-button'), 'click', () => {
this.saveSettings();
this.hide();
this.close();
});

this.on(this.$('.vjs-default-button'), 'click', () => {
Expand All @@ -290,21 +306,28 @@ class TextTrackSettings extends Component {
* The DOM element that gets created.
* @private
*/
createElSelect_(key) {
createElSelect_(key, legendId = '') {
const config = selectConfigs[key];
const id = config.id.replace('%s', this.id_);

return [
createEl('label', {
id,
className: 'vjs-label',
textContent: this.localize(config.label)
}, {
for: id
}),
createEl('select', {id}, undefined, config.options.map(o => {
createEl('select', {}, {
'aria-labelledby': `${legendId} ${id}`
}, config.options.map(o => {
const optionId = id + '-' + o[1];

return createEl('option', {
id: optionId,
textContent: this.localize(o[1]),
value: o[0]
}, {
'aria-labelledby': `${legendId} ${id} ${optionId}`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OwenEdwards did we have an answer whether we should keep this because of VO and Chrome or remove it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little concerned about how well this will work - there's a circular reference where the label for this element includes the id of two other elements, and then the id of this element. The behavior in this situation is mentioned in the W3C Accessible Name Computation - Text Alternative Computation:

if computing a name, and the current node has a non-empty aria-labelledby attribute, and the current node is not already part of an aria-labelledby traversal, process its IDREFs in the order they occur:

but it may not work correctly in all browsers. I guess we'll just have to fix it if that happens?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from the little I understand about this, I think it should be fine and it falls within what the spec allows.
I'm 👌 with leaving this and then fixing in the future if we find an issue.

});
}))
];
Expand All @@ -320,14 +343,15 @@ class TextTrackSettings extends Component {
*/
createElFgColor_() {
const legend = createEl('legend', {
id: `captions-text-legend-${this.id_}`,
textContent: this.localize('Text')
});

const select = this.createElSelect_('color');
const select = this.createElSelect_('color', legend.id);

const opacity = createEl('span', {
className: 'vjs-text-opacity vjs-opacity'
}, undefined, this.createElSelect_('textOpacity'));
}, undefined, this.createElSelect_('textOpacity', legend.id));

return createEl('fieldset', {
className: 'vjs-fg-color vjs-tracksetting'
Expand All @@ -344,14 +368,15 @@ class TextTrackSettings extends Component {
*/
createElBgColor_() {
const legend = createEl('legend', {
id: `captions-background-${this.id_}`,
textContent: this.localize('Background')
});

const select = this.createElSelect_('backgroundColor');
const select = this.createElSelect_('backgroundColor', legend.id);

const opacity = createEl('span', {
className: 'vjs-bg-opacity vjs-opacity'
}, undefined, this.createElSelect_('backgroundOpacity'));
}, undefined, this.createElSelect_('backgroundOpacity', legend.id));

return createEl('fieldset', {
className: 'vjs-bg-color vjs-tracksetting'
Expand All @@ -368,14 +393,15 @@ class TextTrackSettings extends Component {
*/
createElWinColor_() {
const legend = createEl('legend', {
id: `captions-window-${this.id_}`,
textContent: this.localize('Window')
});

const select = this.createElSelect_('windowColor');
const select = this.createElSelect_('windowColor', legend.id);

const opacity = createEl('span', {
className: 'vjs-window-opacity vjs-opacity'
}, undefined, this.createElSelect_('windowOpacity'));
}, undefined, this.createElSelect_('windowOpacity', legend.id));

return createEl('fieldset', {
className: 'vjs-window-color vjs-tracksetting'
Expand Down Expand Up @@ -435,9 +461,11 @@ class TextTrackSettings extends Component {
* @private
*/
createElControls_() {
const defaultsDescription = this.localize('restore all settings to the default values');
const defaultsButton = createEl('button', {
className: 'vjs-default-button',
textContent: this.localize('Defaults')
title: defaultsDescription,
innerHTML: `${this.localize('Defaults')}<span class='vjs-control-text'> - ${defaultsDescription}</span>`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OwenEdwards @mmcc brought up that maybe 'Reset' would be a better name for this button, thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would work fine - if the button is marked 'Reset', then the hidden text can be ' all settings to the default values', and you don't need the ' - ' in the hidden text.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reset seems like a better name to me

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

});

const doneButton = createEl('button', {
Expand All @@ -457,6 +485,10 @@ class TextTrackSettings extends Component {
* The element that was created.
*/
createEl() {
return super.createEl();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we need this here, won't this be the default behavior?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Um... yeah, probably not needed.

}

content() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function and a few of the funcitons below this need jsdoc comments

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSDoc gets them from the parent.

const settings = createEl('div', {
className: 'vjs-tracksettings'
}, undefined, [
Expand All @@ -465,33 +497,19 @@ class TextTrackSettings extends Component {
this.createElControls_()
]);

const heading = createEl('div', {
className: 'vjs-control-text',
id: `TTsettingsDialogLabel-${this.id_}`,
textContent: this.localize('Caption Settings Dialog')
}, {
'aria-level': '1',
'role': 'heading'
});
return settings;
}

const description = createEl('div', {
className: 'vjs-control-text',
id: `TTsettingsDialogDescription-${this.id_}`,
textContent: this.localize('Beginning of dialog window. Escape will cancel and close the window.')
});
label() {
return this.localize('Caption Settings Dialog');
}

const doc = createEl('div', undefined, {
role: 'document'
}, [heading, description, settings]);
description() {
return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
}

return createEl('div', {
className: 'vjs-caption-settings vjs-modal-overlay',
tabIndex: -1
}, {
'role': 'dialog',
'aria-labelledby': heading.id,
'aria-describedby': description.id
}, doc);
buildCSSClass() {
return super.buildCSSClass() + ' vjs-text-track-settings';
}

/**
Expand Down Expand Up @@ -525,7 +543,7 @@ class TextTrackSettings extends Component {
}

/**
* Sets all <select> elements to their default values.
* Sets all `<select>` elements to their default values.
*/
setDefaults() {
Obj.each(selectConfigs, (config) => {
Expand Down Expand Up @@ -584,6 +602,23 @@ class TextTrackSettings extends Component {
}
}

/**
* conditionally blur the element and refocus the captions button
*
* @private
*/
conditionalBlur_() {
this.previouslyActiveEl_ = null;
this.off(document, 'keydown', this.handleKeyDown);

const cb = this.player_.controlBar;
const ccBtn = cb && cb.captionsButton;

if (ccBtn) {
ccBtn.focus();
}
}

}

Component.registerComponent('TextTrackSettings', TextTrackSettings);
Expand Down
2 changes: 1 addition & 1 deletion test/unit/modal-dialog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ QUnit.test('should create the expected description element', function(assert) {
innerHTML: this.modal.description(),
classes: [
'vjs-modal-dialog-description',
'vjs-offscreen'
'vjs-control-text'
],
attrs: {
id: this.el.getAttribute('aria-describedby')
Expand Down