mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-13 15:46:46 +03:00
feat(UI): Improve shaka player UI accessibility (#10023)
### ARIA attributes - Add `aria-hidden="true"` to all decorative SVG icons (`icon.js`) - Implement missing `aria-label` for ad controls (`skip_ad_button.js`, `ad_info.js`) - Add `aria-pressed` to toggle buttons (mute, fullscreen, pip, loop, statistics, remote, stereoscopic) - Remove incorrect `aria-pressed` from one-shot action button (`recenter_vr.js`) - Add `role="menu"`, `aria-haspopup`, `aria-expanded` to menus (`overflow_menu.js`, `settings_menu.js`, `context_menu.js`) - Add `role="menuitemradio"` and `aria-checked` to selection menus (resolution, language, text, playback rate, etc.) - Add `role="toolbar"` to control panel (`controls.js`) - Add `role="heading"` to content title (`content_title.js`) ### Focus management - Restore focus to trigger button when menus close - Maintain focus on fullscreen button after toggle ### CSS - Add `forced-colors` media query for Windows high contrast mode ### Other - Add `alt=""` to seek bar thumbnail image - Add `aria-hidden="true"` to watermark canvas - Add accessibility unit tests for ARIA attributes - Remove redundant `aria-hidden` from checkmark icon (`ui_utils.js`) - Add ARIA terms to project spell-check dictionary Issue https://github.com/shaka-project/shaka-player/issues/3146
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# events / html
|
||||
abrstatuschanged
|
||||
haspopup
|
||||
menuitemradio
|
||||
adblocker
|
||||
audiofocuspaused
|
||||
audiofocusgranted
|
||||
|
||||
+74
-2
@@ -385,6 +385,17 @@ describe('UI', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('has correct ARIA roles', () => {
|
||||
expect(overflowMenu.getAttribute('role')).toBe('menu');
|
||||
});
|
||||
|
||||
it('has aria-haspopup and aria-expanded on menu button', () => {
|
||||
const menuButton = videoContainer.getElementsByClassName(
|
||||
'shaka-overflow-menu-button')[0];
|
||||
expect(menuButton.getAttribute('aria-haspopup')).toBe('true');
|
||||
expect(menuButton.getAttribute('aria-expanded')).toBe('false');
|
||||
});
|
||||
});
|
||||
|
||||
describe('controls-button-panel', () => {
|
||||
@@ -502,6 +513,39 @@ describe('UI', () => {
|
||||
confirmAriaLabel('shaka-fast-forward-button');
|
||||
confirmAriaLabel('shaka-rewind-button');
|
||||
});
|
||||
|
||||
it('has toolbar role', async () => {
|
||||
await UiUtils.createUIThroughAPI(videoContainer, video);
|
||||
const controlsButtonPanels = videoContainer.getElementsByClassName(
|
||||
'shaka-controls-button-panel');
|
||||
expect(controlsButtonPanels.length).toBe(1);
|
||||
expect(controlsButtonPanels[0].getAttribute('role')).toBe('toolbar');
|
||||
});
|
||||
|
||||
it('has aria-pressed on toggle buttons', async () => {
|
||||
const config = {
|
||||
controlPanelElements: [
|
||||
'mute',
|
||||
'fullscreen',
|
||||
],
|
||||
};
|
||||
|
||||
await UiUtils.createUIThroughAPI(videoContainer, video, config);
|
||||
const muteButton = videoContainer.getElementsByClassName(
|
||||
'shaka-mute-button')[0];
|
||||
const fullscreenButton = videoContainer.getElementsByClassName(
|
||||
'shaka-fullscreen-button')[0];
|
||||
expect(muteButton.hasAttribute('aria-pressed')).toBe(true);
|
||||
expect(fullscreenButton.hasAttribute('aria-pressed')).toBe(true);
|
||||
});
|
||||
|
||||
it('has aria-hidden on SVG icons', async () => {
|
||||
await UiUtils.createUIThroughAPI(videoContainer, video);
|
||||
const icons = videoContainer.getElementsByClassName('shaka-ui-icon');
|
||||
for (const icon of icons) {
|
||||
expect(icon.getAttribute('aria-hidden')).toBe('true');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('control panel buttons with submenus', () => {
|
||||
@@ -583,6 +627,33 @@ describe('UI', () => {
|
||||
expect(resolutionMenu.classList.contains('shaka-hidden')).toBe(true);
|
||||
expect(languageMenu.classList.contains('shaka-hidden')).toBe(false);
|
||||
});
|
||||
|
||||
it('settings menus have ARIA roles', () => {
|
||||
expect(resolutionMenu.getAttribute('role')).toBe('menu');
|
||||
expect(languageMenu.getAttribute('role')).toBe('menu');
|
||||
});
|
||||
|
||||
it('settings menu buttons have aria-haspopup and aria-expanded', () => {
|
||||
expect(resolutionMenuButton.getAttribute('aria-haspopup'))
|
||||
.toBe('true');
|
||||
expect(resolutionMenuButton.getAttribute('aria-expanded'))
|
||||
.toBe('false');
|
||||
|
||||
expect(languageMenuButton.getAttribute('aria-haspopup'))
|
||||
.toBe('true');
|
||||
expect(languageMenuButton.getAttribute('aria-expanded'))
|
||||
.toBe('false');
|
||||
});
|
||||
|
||||
it('aria-expanded updates when menu opens and closes', () => {
|
||||
resolutionMenuButton.click();
|
||||
expect(resolutionMenuButton.getAttribute('aria-expanded'))
|
||||
.toBe('true');
|
||||
|
||||
resolutionMenuButton.click();
|
||||
expect(resolutionMenuButton.getAttribute('aria-expanded'))
|
||||
.toBe('false');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolutions menu', () => {
|
||||
@@ -854,8 +925,9 @@ describe('UI', () => {
|
||||
it('builds internal elements', () => {
|
||||
expect(contextMenu.childNodes.length).toBe(1);
|
||||
|
||||
expect(contextMenu.childNodes[0]['className'])
|
||||
.toBe('shaka-statistics-button');
|
||||
const element = /** @type {!HTMLElement} */ (contextMenu.childNodes[0]);
|
||||
expect(element.classList.contains('shaka-statistics-button'))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+3
-1
@@ -77,7 +77,7 @@ shaka.ui.AdInfo = class extends shaka.ui.Element {
|
||||
* @private
|
||||
*/
|
||||
updateAriaLabel_() {
|
||||
// TODO
|
||||
// arai-label is set dynamically in onTimerTick_().
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +119,7 @@ shaka.ui.AdInfo = class extends shaka.ui.Element {
|
||||
if (secondsLeft == -1 || adDuration == -1 ||
|
||||
!isFinite(secondsLeft) || !isFinite(adDuration)) {
|
||||
this.adInfo_.textContent = text;
|
||||
this.adInfo_.ariaLabel = text;
|
||||
shaka.ui.Utils.setDisplay(this.adInfo_, text != '');
|
||||
return;
|
||||
}
|
||||
@@ -141,6 +142,7 @@ shaka.ui.AdInfo = class extends shaka.ui.Element {
|
||||
.replace('[AD_TIME]', timeString);
|
||||
}
|
||||
this.adInfo_.textContent = text;
|
||||
this.adInfo_.ariaLabel = text;
|
||||
shaka.ui.Utils.setDisplay(this.adInfo_, text != '');
|
||||
} else {
|
||||
this.reset_();
|
||||
|
||||
@@ -38,6 +38,9 @@ shaka.ui.AdStatisticsButton = class extends shaka.ui.Element {
|
||||
/** @private {!HTMLButtonElement} */
|
||||
this.button_ = shaka.util.Dom.createButton();
|
||||
this.button_.classList.add('shaka-ad-statistics-button');
|
||||
this.button_.classList.add('shaka-tooltip');
|
||||
this.button_.classList.add('shaka-no-propagation');
|
||||
this.button_.ariaPressed = 'false';
|
||||
|
||||
/** @private {!shaka.ui.Icon} */
|
||||
this.icon_ = new shaka.ui.Icon(this.button_,
|
||||
@@ -156,10 +159,12 @@ shaka.ui.AdStatisticsButton = class extends shaka.ui.Element {
|
||||
this.icon_.use(shaka.ui.Enums.MaterialDesignSVGIcons['STATISTICS_OFF']);
|
||||
this.timer_.tickEvery(0.1);
|
||||
shaka.ui.Utils.setDisplay(this.container_, true);
|
||||
this.button_.ariaPressed = 'true';
|
||||
} else {
|
||||
this.icon_.use(shaka.ui.Enums.MaterialDesignSVGIcons['STATISTICS_ON']);
|
||||
this.timer_.stop();
|
||||
shaka.ui.Utils.setDisplay(this.container_, false);
|
||||
this.button_.ariaPressed = 'false';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ shaka.ui.ChapterSelection = class extends shaka.ui.SettingsMenu {
|
||||
if (chapters.length) {
|
||||
for (const chapter of chapters) {
|
||||
const button = shaka.util.Dom.createButton();
|
||||
button.setAttribute('role', 'menuitem');
|
||||
button.classList.add('shaka-chapter-item');
|
||||
const span = shaka.util.Dom.createHTMLElement('span');
|
||||
span.classList.add('shaka-chapter');
|
||||
|
||||
@@ -34,6 +34,8 @@ shaka.ui.ContentTitle = class extends shaka.ui.Element {
|
||||
/** @type {!HTMLElement} */
|
||||
this.title_ = shaka.util.Dom.createHTMLElement('div');
|
||||
this.title_.classList.add('shaka-content-title');
|
||||
this.title_.setAttribute('role', 'heading');
|
||||
this.title_.setAttribute('aria-level', '2');
|
||||
this.parent.appendChild(this.title_);
|
||||
|
||||
this.eventManager.listen(this.player, 'unloading', () => {
|
||||
|
||||
@@ -44,6 +44,7 @@ shaka.ui.ContextMenu = class extends shaka.ui.Element {
|
||||
this.contextMenu_.classList.add('shaka-no-propagation');
|
||||
this.contextMenu_.classList.add('shaka-context-menu');
|
||||
this.contextMenu_.classList.add('shaka-hidden');
|
||||
this.contextMenu_.setAttribute('role', 'menu');
|
||||
|
||||
this.controlsContainer_.appendChild(this.contextMenu_);
|
||||
|
||||
|
||||
Vendored
+2
@@ -1528,6 +1528,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
this.controlsButtonPanel_.classList.add('shaka-controls-button-panel');
|
||||
this.controlsButtonPanel_.classList.add(
|
||||
'shaka-show-controls-on-mouse-over');
|
||||
this.controlsButtonPanel_.setAttribute('role', 'toolbar');
|
||||
|
||||
if (this.config_.enableTooltips) {
|
||||
this.controlsButtonPanel_.classList.add('shaka-tooltips-on');
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
|
||||
return;
|
||||
}
|
||||
await this.controls.toggleFullScreen();
|
||||
this.button_.focus();
|
||||
});
|
||||
|
||||
this.eventManager.listen(document, 'fullscreenchange', () => {
|
||||
@@ -110,6 +111,8 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
|
||||
LocIds.EXIT_FULL_SCREEN : LocIds.FULL_SCREEN;
|
||||
|
||||
this.button_.ariaLabel = this.localization.resolve(label);
|
||||
this.button_.ariaPressed =
|
||||
this.controls.isFullScreenEnabled() ? 'true' : 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,9 @@ shaka.ui.Icon = class {
|
||||
this.svg_ = shaka.util.Dom.createSVGElement('svg');
|
||||
|
||||
this.svg_.classList.add('shaka-ui-icon');
|
||||
// Screen reader should ignore icon text.
|
||||
// all icons should have this attribute
|
||||
this.svg_.ariaHidden = 'true';
|
||||
this.svg_.setAttribute('viewBox', '0 -960 960 960');
|
||||
|
||||
if (icon) {
|
||||
|
||||
@@ -181,6 +181,9 @@ shaka.ui.LanguageUtils = class {
|
||||
button.addEventListener('click', () => {
|
||||
onTrackSelected(track);
|
||||
});
|
||||
// ARIA: single-select menu item
|
||||
button.setAttribute('role', 'menuitemradio');
|
||||
button.setAttribute('aria-checked', 'false');
|
||||
|
||||
const span = shaka.util.Dom.createHTMLElement('span');
|
||||
button.appendChild(span);
|
||||
@@ -262,7 +265,7 @@ shaka.ui.LanguageUtils = class {
|
||||
if (updateChosen && (combinationName == selectedCombination)) {
|
||||
button.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
span.classList.add('shaka-chosen-item');
|
||||
button.ariaSelected = 'true';
|
||||
button.setAttribute('aria-checked', 'true');
|
||||
currentSelectionElement.textContent = span.textContent;
|
||||
}
|
||||
langMenu.appendChild(button);
|
||||
@@ -345,6 +348,9 @@ shaka.ui.LanguageUtils = class {
|
||||
button.addEventListener('click', () => {
|
||||
onTrackSelected(track);
|
||||
});
|
||||
// ARIA: single-select menu item
|
||||
button.setAttribute('role', 'menuitemradio');
|
||||
button.setAttribute('aria-checked', 'false');
|
||||
|
||||
const span = shaka.util.Dom.createHTMLElement('span');
|
||||
button.appendChild(span);
|
||||
@@ -433,7 +439,7 @@ shaka.ui.LanguageUtils = class {
|
||||
if (updateChosen && (combinationName == selectedCombination)) {
|
||||
button.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
span.classList.add('shaka-chosen-item');
|
||||
button.ariaSelected = 'true';
|
||||
button.setAttribute('aria-checked', 'true');
|
||||
currentSelectionElement.textContent = span.textContent;
|
||||
}
|
||||
langMenu.appendChild(button);
|
||||
|
||||
@@ -145,3 +145,11 @@ the control buttons panel. */
|
||||
|
||||
@quality-mark-color: #fff;
|
||||
@quality-mark-hightlight-color: #f00;
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.shaka-overflow-menu,
|
||||
.shaka-settings-menu,
|
||||
.shaka-context-menu {
|
||||
border: 1px solid ButtonText;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ shaka.ui.LoopButton = class extends shaka.ui.Element {
|
||||
this.button_.classList.add('shaka-loop-button');
|
||||
this.button_.classList.add('shaka-tooltip');
|
||||
this.button_.classList.add('shaka-no-propagation');
|
||||
this.button_.ariaPressed = 'false';
|
||||
|
||||
/** @private {!shaka.ui.Icon} */
|
||||
this.icon_ = new shaka.ui.Icon(this.button_,
|
||||
@@ -181,6 +182,7 @@ shaka.ui.LoopButton = class extends shaka.ui.Element {
|
||||
LocIds.EXIT_LOOP_MODE : LocIds.ENTER_LOOP_MODE;
|
||||
|
||||
this.button_.ariaLabel = this.localization.resolve(ariaText);
|
||||
this.button_.ariaPressed = this.video.loop ? 'true' : 'false';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ shaka.ui.MuteButton = class extends shaka.ui.Element {
|
||||
this.button_.classList.add('shaka-mute-button');
|
||||
this.button_.classList.add('shaka-tooltip');
|
||||
this.button_.classList.add('shaka-no-propagation');
|
||||
this.button_.ariaPressed = 'false';
|
||||
|
||||
/** @private {!shaka.ui.Icon} */
|
||||
this.icon_ = new shaka.ui.Icon(this.button_,
|
||||
@@ -154,6 +155,9 @@ shaka.ui.MuteButton = class extends shaka.ui.Element {
|
||||
}
|
||||
|
||||
this.button_.ariaLabel = this.localization.resolve(label);
|
||||
this.button_.ariaLabel = this.localization.resolve(label);
|
||||
this.button_.ariaPressed = label == LocIds.UNMUTE ? 'true' : 'false';
|
||||
this.nameSpan_.textContent = this.localization.resolve(label);
|
||||
this.nameSpan_.textContent = this.localization.resolve(label);
|
||||
}
|
||||
|
||||
|
||||
@@ -145,6 +145,7 @@ shaka.ui.OverflowMenu = class extends shaka.ui.Element {
|
||||
this.overflowMenu_.classList.add('shaka-no-propagation');
|
||||
this.overflowMenu_.classList.add('shaka-show-controls-on-mouse-over');
|
||||
this.overflowMenu_.classList.add('shaka-hidden');
|
||||
this.overflowMenu_.setAttribute('role', 'menu');
|
||||
this.controlsContainer_.appendChild(this.overflowMenu_);
|
||||
}
|
||||
|
||||
@@ -155,6 +156,8 @@ shaka.ui.OverflowMenu = class extends shaka.ui.Element {
|
||||
addOverflowMenuButton_() {
|
||||
/** @private {!HTMLButtonElement} */
|
||||
this.overflowMenuButton_ = shaka.util.Dom.createButton();
|
||||
this.overflowMenuButton_.setAttribute('aria-haspopup', 'true');
|
||||
this.overflowMenuButton_.setAttribute('aria-expanded', 'false');
|
||||
this.overflowMenuButton_.classList.add('shaka-overflow-menu-button');
|
||||
this.overflowMenuButton_.classList.add('shaka-no-propagation');
|
||||
this.overflowMenuButton_.classList.add('shaka-tooltip');
|
||||
@@ -191,11 +194,14 @@ shaka.ui.OverflowMenu = class extends shaka.ui.Element {
|
||||
this.controls.hideContextMenus();
|
||||
if (this.controls.anySettingsMenusAreOpen()) {
|
||||
this.controls.hideSettingsMenus();
|
||||
this.overflowMenuButton_.setAttribute('aria-expanded', 'false');
|
||||
this.overflowMenuButton_.focus();
|
||||
} else {
|
||||
// Force to close any submenu.
|
||||
this.controls.dispatchEvent(new shaka.util.FakeEvent('submenuclose'));
|
||||
|
||||
shaka.ui.Utils.setDisplay(this.overflowMenu_, true);
|
||||
this.overflowMenuButton_.setAttribute('aria-expanded', 'true');
|
||||
this.controls.computeOpacity();
|
||||
|
||||
// If overflow menu has currently visible buttons, focus on the
|
||||
|
||||
@@ -44,6 +44,7 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
|
||||
this.pipButton_.classList.add('shaka-pip-button');
|
||||
this.pipButton_.classList.add('shaka-tooltip');
|
||||
this.pipButton_.classList.add('shaka-no-propagation');
|
||||
this.pipButton_.ariaPressed = 'false';
|
||||
|
||||
/** @private {!shaka.ui.Icon} */
|
||||
this.pipIcon_ = new shaka.ui.Icon(this.pipButton_,
|
||||
@@ -143,6 +144,7 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
|
||||
this.localization.resolve(LocIds.EXIT_PICTURE_IN_PICTURE);
|
||||
this.currentPipState_.textContent =
|
||||
this.localization.resolve(LocIds.ON);
|
||||
this.pipButton_.ariaPressed = 'true';
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +156,7 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
|
||||
this.localization.resolve(LocIds.ENTER_PICTURE_IN_PICTURE);
|
||||
this.currentPipState_.textContent =
|
||||
this.localization.resolve(LocIds.OFF);
|
||||
this.pipButton_.ariaPressed = 'false';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ shaka.ui.PlaybackRateSelection = class extends shaka.ui.SettingsMenu {
|
||||
if (span) {
|
||||
const button = span.parentElement;
|
||||
button.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
button.ariaSelected = 'true';
|
||||
button.setAttribute('aria-checked', 'true');
|
||||
span.classList.add('shaka-chosen-item');
|
||||
}
|
||||
|
||||
@@ -132,6 +132,9 @@ shaka.ui.PlaybackRateSelection = class extends shaka.ui.SettingsMenu {
|
||||
addPlaybackRates_() {
|
||||
for (const rate of this.controls.getConfig().playbackRates) {
|
||||
const button = shaka.util.Dom.createButton();
|
||||
// ARIA: single-select menu item
|
||||
button.setAttribute('role', 'menuitemradio');
|
||||
button.setAttribute('aria-checked', 'false');
|
||||
const span = shaka.util.Dom.createHTMLElement('span');
|
||||
span.textContent = rate + 'x';
|
||||
button.appendChild(span);
|
||||
|
||||
@@ -36,7 +36,6 @@ shaka.ui.RecenterVRButton = class extends shaka.ui.Element {
|
||||
this.recenterVRButton_ = shaka.util.Dom.createButton();
|
||||
this.recenterVRButton_.classList.add('shaka-recenter-vr-button');
|
||||
this.recenterVRButton_.classList.add('shaka-tooltip');
|
||||
this.recenterVRButton_.ariaPressed = 'false';
|
||||
|
||||
/** @private {!shaka.ui.Icon} */
|
||||
this.recenterVRIcon_ = new shaka.ui.Icon(this.recenterVRButton_,
|
||||
|
||||
@@ -197,6 +197,8 @@ shaka.ui.RemoteButton = class extends shaka.ui.Element {
|
||||
this.callbackId_ = -1;
|
||||
}
|
||||
}
|
||||
this.remoteButton_.ariaPressed =
|
||||
this.remote_?.state == 'connected' ? 'true' : 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -230,6 +230,9 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
||||
|
||||
// Add the Auto button
|
||||
const autoButton = shaka.util.Dom.createButton();
|
||||
// ARIA: single-select menu item
|
||||
autoButton.setAttribute('role', 'menuitemradio');
|
||||
autoButton.setAttribute('aria-checked', 'false');
|
||||
autoButton.classList.add('shaka-enable-abr-button');
|
||||
this.eventManager.listen(autoButton, 'click', () => {
|
||||
const config = {abr: {enabled: true}};
|
||||
@@ -245,7 +248,7 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
||||
|
||||
// If abr is enabled reflect it by marking 'Auto' as selected.
|
||||
if (this.player.getConfiguration().abr.enabled) {
|
||||
autoButton.ariaSelected = 'true';
|
||||
autoButton.setAttribute('aria-checked', 'true');
|
||||
autoButton.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
|
||||
this.abrOnSpan_.classList.add('shaka-chosen-item');
|
||||
@@ -332,6 +335,9 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
||||
// Add new ones
|
||||
for (const track of tracks) {
|
||||
const button = shaka.util.Dom.createButton();
|
||||
// ARIA: single-select menu item
|
||||
button.setAttribute('role', 'menuitemradio');
|
||||
button.setAttribute('aria-checked', 'false');
|
||||
button.classList.add('explicit-resolution');
|
||||
this.eventManager.listen(button, 'click',
|
||||
() => this.onTrackSelected_(track));
|
||||
@@ -346,7 +352,7 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
||||
|
||||
if (!abrEnabled && track == selectedTrack) {
|
||||
// If abr is disabled, mark the selected track's resolution.
|
||||
button.ariaSelected = 'true';
|
||||
button.setAttribute('aria-checked', 'true');
|
||||
button.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
span.classList.add('shaka-chosen-item');
|
||||
this.currentSelection.textContent = span.textContent;
|
||||
@@ -405,6 +411,9 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
||||
// Add new ones
|
||||
for (const track of tracks) {
|
||||
const button = shaka.util.Dom.createButton();
|
||||
// ARIA: single-select menu item
|
||||
button.setAttribute('role', 'menuitemradio');
|
||||
button.setAttribute('aria-checked', 'false');
|
||||
button.classList.add('explicit-resolution');
|
||||
this.eventManager.listen(button, 'click',
|
||||
() => this.onVideoTrackSelected_(track));
|
||||
@@ -429,7 +438,7 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
||||
|
||||
if (!abrEnabled && track == selectedTrack) {
|
||||
// If abr is disabled, mark the selected track's resolution.
|
||||
button.ariaSelected = 'true';
|
||||
button.setAttribute('aria-checked', 'true');
|
||||
button.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
span.classList.add('shaka-chosen-item');
|
||||
this.currentSelection.textContent = span.textContent;
|
||||
|
||||
@@ -746,6 +746,7 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
|
||||
}
|
||||
this.thumbnailImage_ = /** @type {!HTMLImageElement} */ (
|
||||
shaka.util.Dom.createHTMLElement('img'));
|
||||
this.thumbnailImage_.alt = '';
|
||||
this.thumbnailImage_.classList.add('shaka-player-ui-thumbnail-image');
|
||||
this.thumbnailImage_.draggable = false;
|
||||
this.thumbnailImage_.src = uri;
|
||||
|
||||
@@ -88,6 +88,8 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
addButton_(iconText) {
|
||||
/** @protected {!HTMLButtonElement} */
|
||||
this.button = shaka.util.Dom.createButton();
|
||||
this.button.setAttribute('aria-haspopup', 'true');
|
||||
this.button.setAttribute('aria-expanded', 'false');
|
||||
this.button.classList.add('shaka-overflow-button');
|
||||
|
||||
/** @protected {!shaka.ui.Icon}*/
|
||||
@@ -118,6 +120,7 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
this.menu = shaka.util.Dom.createHTMLElement('div');
|
||||
this.menu.classList.add('shaka-no-propagation');
|
||||
this.menu.classList.add('shaka-show-controls-on-mouse-over');
|
||||
this.menu.setAttribute('role', 'menu');
|
||||
if (this.isSubMenu) {
|
||||
this.menu.classList.add('shaka-sub-menu');
|
||||
} else {
|
||||
@@ -131,6 +134,7 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
this.menu.appendChild(this.backButton);
|
||||
this.eventManager.listen(this.backButton, 'click', () => {
|
||||
this.controls.hideSettingsMenus();
|
||||
this.backButton.focus();
|
||||
});
|
||||
|
||||
/** @private {shaka.ui.Icon} */
|
||||
@@ -195,6 +199,7 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
onButtonClick_() {
|
||||
if (!this.parent.classList.contains('shaka-context-menu')) {
|
||||
this.controls.hideContextMenus();
|
||||
this.button.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
if (!this.isSubMenu && this.controls.anySettingsMenusAreOpen()) {
|
||||
this.controls.hideSettingsMenus();
|
||||
@@ -206,8 +211,11 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
shaka.ui.Utils.setDisplay(this.menu, true);
|
||||
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
|
||||
this.adjustCustomStyle_();
|
||||
this.button.setAttribute('aria-expanded', 'true');
|
||||
} else {
|
||||
shaka.ui.Utils.setDisplay(this.menu, false);
|
||||
this.button.setAttribute('aria-expanded', 'false');
|
||||
this.button.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,8 @@ shaka.ui.SkipAdButton = class extends shaka.ui.Element {
|
||||
* @private
|
||||
*/
|
||||
updateAriaLabel_() {
|
||||
// TODO
|
||||
const LocIds = shaka.ui.Locales.Ids;
|
||||
this.button_.ariaLabel = this.localization.resolve(LocIds.SKIP_AD);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,9 @@ shaka.ui.StatisticsButton = class extends shaka.ui.Element {
|
||||
/** @private {!HTMLButtonElement} */
|
||||
this.button_ = shaka.util.Dom.createButton();
|
||||
this.button_.classList.add('shaka-statistics-button');
|
||||
this.button_.classList.add('shaka-tooltip');
|
||||
this.button_.classList.add('shaka-no-propagation');
|
||||
this.button_.ariaPressed = 'false';
|
||||
|
||||
/** @private {!shaka.ui.Icon} */
|
||||
this.icon_ = new shaka.ui.Icon(this.button_,
|
||||
@@ -207,10 +210,12 @@ shaka.ui.StatisticsButton = class extends shaka.ui.Element {
|
||||
this.icon_.use(shaka.ui.Enums.MaterialDesignSVGIcons['STATISTICS_OFF']);
|
||||
this.timer_.tickEvery(0.1);
|
||||
shaka.ui.Utils.setDisplay(this.container_, true);
|
||||
this.button_.ariaPressed = 'true';
|
||||
} else {
|
||||
this.icon_.use(shaka.ui.Enums.MaterialDesignSVGIcons['STATISTICS_ON']);
|
||||
this.timer_.stop();
|
||||
shaka.ui.Utils.setDisplay(this.container_, false);
|
||||
this.button_.ariaPressed = 'false';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -123,6 +123,9 @@ shaka.ui.TextPosition = class extends shaka.ui.SettingsMenu {
|
||||
// 4. Add new items
|
||||
for (const position of Object.values(shaka.config.PositionArea)) {
|
||||
const button = shaka.util.Dom.createButton();
|
||||
// ARIA: single-select menu item
|
||||
button.setAttribute('role', 'menuitemradio');
|
||||
button.setAttribute('aria-checked', 'false');
|
||||
const span = shaka.util.Dom.createHTMLElement('span');
|
||||
span.textContent = this.getNameOfPosition_(position);
|
||||
button.appendChild(span);
|
||||
@@ -145,7 +148,7 @@ shaka.ui.TextPosition = class extends shaka.ui.SettingsMenu {
|
||||
this.menu, 'shaka-ui-icon shaka-chosen-item');
|
||||
if (checkmarkIcon) {
|
||||
const previouslySelectedButton = checkmarkIcon.parentElement;
|
||||
previouslySelectedButton.removeAttribute('aria-selected');
|
||||
previouslySelectedButton.setAttribute('aria-checked', 'false');
|
||||
const previouslySelectedSpan =
|
||||
previouslySelectedButton.getElementsByTagName('span')[0];
|
||||
if (previouslySelectedSpan) {
|
||||
@@ -164,7 +167,7 @@ shaka.ui.TextPosition = class extends shaka.ui.SettingsMenu {
|
||||
if (span) {
|
||||
const button = span.parentElement;
|
||||
button.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
button.ariaSelected = 'true';
|
||||
button.setAttribute('aria-checked', 'true');
|
||||
span.classList.add('shaka-chosen-item');
|
||||
}
|
||||
this.currentSelection.textContent = positionAreaName;
|
||||
|
||||
@@ -107,7 +107,9 @@ shaka.ui.TextSelection = class extends shaka.ui.SettingsMenu {
|
||||
*/
|
||||
addOffOption_() {
|
||||
const off = shaka.util.Dom.createButton();
|
||||
off.ariaSelected = 'true';
|
||||
// ARIA: single-select menu item
|
||||
off.setAttribute('role', 'menuitemradio');
|
||||
off.setAttribute('aria-checked', 'true');
|
||||
this.menu.appendChild(off);
|
||||
|
||||
off.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
@@ -164,7 +166,9 @@ shaka.ui.TextSelection = class extends shaka.ui.SettingsMenu {
|
||||
this.menu.appendChild(offButton);
|
||||
|
||||
if (!hasTrack) {
|
||||
offButton.ariaSelected = 'true';
|
||||
// ARIA: single-select menu item
|
||||
offButton.setAttribute('role', 'menuitemradio');
|
||||
offButton.setAttribute('aria-checked', 'true');
|
||||
offButton.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
this.captionsOffSpan_.classList.add('shaka-chosen-item');
|
||||
this.currentSelection.textContent =
|
||||
|
||||
+5
-2
@@ -121,6 +121,9 @@ shaka.ui.TextSize = class extends shaka.ui.SettingsMenu {
|
||||
for (const fontScaleFactor of
|
||||
this.controls.getConfig().captionsFontScaleFactors) {
|
||||
const button = shaka.util.Dom.createButton();
|
||||
// ARIA: single-select menu item
|
||||
button.setAttribute('role', 'menuitemradio');
|
||||
button.setAttribute('aria-checked', 'false');
|
||||
const span = shaka.util.Dom.createHTMLElement('span');
|
||||
span.textContent = fontScaleFactor * 100 + '%';
|
||||
button.appendChild(span);
|
||||
@@ -143,7 +146,7 @@ shaka.ui.TextSize = class extends shaka.ui.SettingsMenu {
|
||||
this.menu, 'shaka-ui-icon shaka-chosen-item');
|
||||
if (checkmarkIcon) {
|
||||
const previouslySelectedButton = checkmarkIcon.parentElement;
|
||||
previouslySelectedButton.removeAttribute('aria-selected');
|
||||
previouslySelectedButton.setAttribute('aria-checked', 'false');
|
||||
const previouslySelectedSpan =
|
||||
previouslySelectedButton.getElementsByTagName('span')[0];
|
||||
if (previouslySelectedSpan) {
|
||||
@@ -162,7 +165,7 @@ shaka.ui.TextSize = class extends shaka.ui.SettingsMenu {
|
||||
if (span) {
|
||||
const button = span.parentElement;
|
||||
button.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
button.ariaSelected = 'true';
|
||||
button.setAttribute('aria-checked', 'true');
|
||||
span.classList.add('shaka-chosen-item');
|
||||
}
|
||||
this.currentSelection.textContent = fontScaleFactorName;
|
||||
|
||||
@@ -77,6 +77,8 @@ shaka.ui.ToggleStereoscopicButton = class extends shaka.ui.Element {
|
||||
return;
|
||||
}
|
||||
vr.toggleStereoscopicMode();
|
||||
this.toggleStereoscopicButton_.ariaPressed =
|
||||
vr.isStereoscopicModeEnabled() ? 'true' : 'false';
|
||||
});
|
||||
|
||||
this.eventManager.listen(vr, 'vrstatuschanged', () => {
|
||||
|
||||
+1
-2
@@ -70,8 +70,7 @@ shaka.ui.Utils = class {
|
||||
shaka.ui.Enums.MaterialDesignSVGIcons['CHECKMARK']);
|
||||
const iconElement = icon.getSvgElement();
|
||||
iconElement.classList.add('shaka-chosen-item');
|
||||
// Screen reader should ignore icon text.
|
||||
iconElement.ariaHidden = 'true';
|
||||
|
||||
return iconElement;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,9 @@ shaka.ui.VideoTypeSelection = class extends shaka.ui.SettingsMenu {
|
||||
if (roles.size > 1) {
|
||||
for (const role of roles) {
|
||||
const button = shaka.util.Dom.createButton();
|
||||
// ARIA: single-select menu item
|
||||
button.setAttribute('role', 'menuitemradio');
|
||||
button.setAttribute('aria-checked', 'false');
|
||||
this.eventManager.listen(button, 'click',
|
||||
() => this.onVideoRoleSelected_(role));
|
||||
|
||||
@@ -128,7 +131,7 @@ shaka.ui.VideoTypeSelection = class extends shaka.ui.SettingsMenu {
|
||||
button.appendChild(span);
|
||||
|
||||
if (selectedTrack.roles.includes(role)) {
|
||||
button.ariaSelected = 'true';
|
||||
button.setAttribute('aria-checked', 'true');
|
||||
button.appendChild(shaka.ui.Utils.checkmarkIcon());
|
||||
span.classList.add('shaka-chosen-item');
|
||||
this.currentSelection.textContent = span.textContent;
|
||||
|
||||
@@ -38,6 +38,7 @@ shaka.ui.Watermark = class extends shaka.ui.Element {
|
||||
this.canvas_.style.height = '100%';
|
||||
this.canvas_.style.zIndex = '2';
|
||||
this.canvas_.style.pointerEvents = 'none';
|
||||
this.canvas_.setAttribute('aria-hidden', 'true');
|
||||
|
||||
this.parent.appendChild(this.canvas_);
|
||||
this.resizeCanvas_();
|
||||
|
||||
Reference in New Issue
Block a user