mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Bug 1593649 - Part 1: Fix a11y issues with about:addons header/recommendations r=robwu,rpl,fluent-reviewers,flod
Add a proper title and popup attributes to page-options button. Make recommended card's add-on names headings. Give the HTML pane a title so it reads better in screen readers. Always include a label for the search box. Clarify the label on the extension enable checkbox. Differential Revision: https://phabricator.services.mozilla.com/D54874 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
e6655d9d96
commit
415e677b84
@ -48,14 +48,15 @@ function getAddonElement(managerWindow, addonId) {
|
||||
);
|
||||
}
|
||||
|
||||
function assertDisabledSideloadedAddonElement(managerWindow, addonElement) {
|
||||
const doc = addonElement.ownerDocument;
|
||||
function assertSideloadedAddonElementState(addonElement, checked) {
|
||||
const enableBtn = addonElement.querySelector('[action="toggle-disabled"]');
|
||||
is(
|
||||
doc.l10n.getAttributes(enableBtn).id,
|
||||
"enable-addon-button-label",
|
||||
"The button has the enable label"
|
||||
enableBtn.checked,
|
||||
checked,
|
||||
`The enable button is ${!checked ? " not " : ""} checked`
|
||||
);
|
||||
is(enableBtn.localName, "input", "The enable button is an input");
|
||||
is(enableBtn.type, "checkbox", "It's a checkbox");
|
||||
}
|
||||
|
||||
function clickEnableExtension(managerWindow, addonElement) {
|
||||
@ -220,7 +221,7 @@ add_task(async function test_sideloading() {
|
||||
// XUL or HTML about:addons addon entry element.
|
||||
const addonElement = await getAddonElement(win, ID2);
|
||||
|
||||
assertDisabledSideloadedAddonElement(win, addonElement);
|
||||
assertSideloadedAddonElementState(addonElement, false);
|
||||
|
||||
info("Test enabling sideloaded addon 2 from about:addons enable button");
|
||||
|
||||
@ -267,6 +268,7 @@ add_task(async function test_sideloading() {
|
||||
|
||||
addon2 = await AddonManager.getAddonByID(ID2);
|
||||
is(addon2.userDisabled, false, "Addon 2 should be enabled");
|
||||
assertSideloadedAddonElementState(addonElement, true);
|
||||
|
||||
// Test post install notification on addon 2.
|
||||
await testPostInstallIncognitoCheckbox(addon2);
|
||||
|
@ -0,0 +1,28 @@
|
||||
# coding=utf8
|
||||
|
||||
# Any copyright is dedicated to the Public Domain.
|
||||
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
from __future__ import absolute_import
|
||||
import fluent.syntax.ast as FTL
|
||||
from fluent.migrate.helpers import transforms_from
|
||||
from fluent.migrate import COPY_PATTERN
|
||||
|
||||
TARGET_FILE = "toolkit/toolkit/about/aboutAddons.ftl"
|
||||
SOURCE_FILE = TARGET_FILE
|
||||
|
||||
|
||||
def migrate(ctx):
|
||||
"""Bug 1593649 - about:addons tools button, part {index}"""
|
||||
|
||||
ctx.add_transforms(
|
||||
TARGET_FILE,
|
||||
SOURCE_FILE,
|
||||
transforms_from(
|
||||
"""
|
||||
addon-page-options-button =
|
||||
.title = {COPY_PATTERN(from_path, "tools-menu.tooltiptext")}
|
||||
addons-page-title = {COPY_PATTERN(from_path, "addons-window.title")}
|
||||
""",
|
||||
from_path=SOURCE_FILE),
|
||||
)
|
@ -4,6 +4,7 @@
|
||||
|
||||
addons-window =
|
||||
.title = Add-ons Manager
|
||||
addons-page-title = Add-ons Manager
|
||||
|
||||
search-header =
|
||||
.placeholder = Search addons.mozilla.org
|
||||
@ -38,9 +39,6 @@ preferences =
|
||||
*[other] { -brand-short-name } Preferences
|
||||
}
|
||||
|
||||
tools-menu =
|
||||
.tooltiptext = Tools for all add-ons
|
||||
|
||||
show-unsigned-extensions-button =
|
||||
.label = Some extensions could not be verified
|
||||
|
||||
@ -367,9 +365,9 @@ remove-addon-button = Remove
|
||||
remove-addon-disabled-button = Can’t Be Removed <a data-l10n-name="link">Why?</a>
|
||||
disable-addon-button = Disable
|
||||
enable-addon-button = Enable
|
||||
disable-addon-button-label =
|
||||
.aria-label = Disable
|
||||
enable-addon-button-label =
|
||||
# This is used for the toggle on the extension card, it's a checkbox and this
|
||||
# is always its label.
|
||||
extension-enable-addon-button-label =
|
||||
.aria-label = Enable
|
||||
preferences-addon-button =
|
||||
{ PLATFORM() ->
|
||||
@ -482,5 +480,9 @@ shortcuts-heading = Manage Extension Shortcuts
|
||||
|
||||
theme-heading-search-label = Find more themes
|
||||
extension-heading-search-label = Find more extensions
|
||||
default-heading-search-label = Find more add-ons
|
||||
addons-heading-search-input =
|
||||
.placeholder = Search addons.mozilla.org
|
||||
|
||||
addon-page-options-button =
|
||||
.title = Tools for all add-ons
|
||||
|
@ -292,10 +292,6 @@ addon-card:not([expanded]) .addon-description {
|
||||
|
||||
/* Discopane extensions to the add-on card */
|
||||
|
||||
recommended-addon-card .addon-name {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
recommended-addon-card .addon-description:not(:empty) {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
@ -306,6 +302,13 @@ recommended-addon-card .addon-description:not(:empty) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.disco-addon-name {
|
||||
font-size: inherit;
|
||||
font-weight: normal;
|
||||
line-height: normal;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.disco-addon-author {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
@ -5,6 +5,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-l10n-id="addons-page-title"></title>
|
||||
|
||||
<!-- Bug 1571346 Remove 'unsafe-inline' from style-src within about:addons -->
|
||||
<!-- @CSP the 'onclick' handler for the searchButtonIcon within the file search-textbox.js, using: sha512-kSDNX67wegjpcf8CSj/L6h46a0QUKm2CyijGxC5PhSWVvPU9gdd28QVBBFq9t8N5UGKUFdDcZsjYbGSlYG0y3g== -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; script-src chrome: 'sha512-kSDNX67wegjpcf8CSj/L6h46a0QUKm2CyijGxC5PhSWVvPU9gdd28QVBBFq9t8N5UGKUFdDcZsjYbGSlYG0y3g=='; style-src chrome: 'unsafe-inline'; img-src chrome: file: jar: https: http:; connect-src chrome: data: https: http:; object-src 'none'">
|
||||
@ -54,7 +56,7 @@
|
||||
<div class="spacer"></div>
|
||||
<addon-updates-message id="updates-message" hidden></addon-updates-message>
|
||||
<div class="page-options-menu">
|
||||
<button class="more-options-button" action="page-options"></button>
|
||||
<button class="more-options-button" action="page-options" aria-haspopup="menu" aria-expanded="false" data-l10n-id="addon-page-options-button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -121,7 +123,7 @@
|
||||
</a>
|
||||
<div class="spacer"></div>
|
||||
<button class="theme-enable-button" action="toggle-disabled" hidden></button>
|
||||
<input type="checkbox" class="toggle-button extension-enable-button" action="toggle-disabled" hidden>
|
||||
<input type="checkbox" class="toggle-button extension-enable-button" action="toggle-disabled" data-l10n-id="extension-enable-addon-button-label" hidden>
|
||||
<button
|
||||
class="more-options-button"
|
||||
action="more-options"
|
||||
@ -142,7 +144,7 @@
|
||||
|
||||
<template name="addon-name-container-in-disco-card">
|
||||
<div class="disco-card-head">
|
||||
<span class="disco-addon-name"></span>
|
||||
<h3 class="disco-addon-name"></h3>
|
||||
<span class="disco-addon-author"><a data-l10n-name="author" target="_blank"></a></span>
|
||||
</div>
|
||||
<button class="disco-cta-button primary" action="install-addon"></button>
|
||||
@ -342,7 +344,7 @@
|
||||
<div class="card shortcut">
|
||||
<div class="card-heading">
|
||||
<img class="card-heading-icon addon-icon">
|
||||
<span class="addon-name"></span>
|
||||
<h2 class="addon-name"></h2>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1283,6 +1283,9 @@ class AddonPageHeader extends HTMLElement {
|
||||
this.heading = this.querySelector(".header-name");
|
||||
this.searchLabel = this.querySelector(".search-label");
|
||||
this.backButton = this.querySelector(".back-button");
|
||||
this.pageOptionsMenuButton = this.querySelector(
|
||||
'[action="page-options"]'
|
||||
);
|
||||
// The addon-page-options element is outside of this element since this is
|
||||
// position: sticky and that would break the positioning of the menu.
|
||||
this.pageOptionsMenu = document.getElementById(
|
||||
@ -1290,10 +1293,16 @@ class AddonPageHeader extends HTMLElement {
|
||||
);
|
||||
}
|
||||
this.addEventListener("click", this);
|
||||
// Use capture since the event is actually triggered on the internal
|
||||
// panel-list and it doesn't bubble.
|
||||
this.pageOptionsMenu.addEventListener("shown", this, true);
|
||||
this.pageOptionsMenu.addEventListener("hidden", this, true);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener("click", this);
|
||||
this.pageOptionsMenu.removeEventListener("shown", this, true);
|
||||
this.pageOptionsMenu.removeEventListener("hidden", this, true);
|
||||
}
|
||||
|
||||
setViewInfo({ type, param }) {
|
||||
@ -1309,20 +1318,16 @@ class AddonPageHeader extends HTMLElement {
|
||||
document.l10n.setAttributes(this.heading, `${viewType}-heading`);
|
||||
}
|
||||
|
||||
if (
|
||||
viewType === "extension" ||
|
||||
viewType === "theme" ||
|
||||
viewType === "shortcuts"
|
||||
) {
|
||||
let labelType = viewType === "shortcuts" ? "extension" : viewType;
|
||||
let customSearchLabelTypes = {
|
||||
shortcuts: "extension",
|
||||
extension: "extension",
|
||||
theme: "theme",
|
||||
};
|
||||
let searchLabelType = customSearchLabelTypes[viewType] || "default";
|
||||
document.l10n.setAttributes(
|
||||
this.searchLabel,
|
||||
`${labelType}-heading-search-label`
|
||||
`${searchLabelType}-heading-search-label`
|
||||
);
|
||||
} else {
|
||||
this.searchLabel.removeAttribute("data-l10n-id");
|
||||
this.searchLabel.textContent = "";
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(e) {
|
||||
@ -1336,6 +1341,11 @@ class AddonPageHeader extends HTMLElement {
|
||||
this.pageOptionsMenu.toggle(e);
|
||||
break;
|
||||
}
|
||||
} else if (e.type == "shown" || e.type == "hidden") {
|
||||
this.pageOptionsMenuButton.setAttribute(
|
||||
"aria-expanded",
|
||||
this.pageOptionsMenu.open
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1412,6 +1422,10 @@ class AddonPageOptions extends HTMLElement {
|
||||
return this.panel.toggle(...args);
|
||||
}
|
||||
|
||||
get open() {
|
||||
return this.panel.open;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.appendChild(importTemplate("addon-page-options"));
|
||||
this.panel = this.querySelector("panel-list");
|
||||
@ -2730,11 +2744,10 @@ class AddonCard extends HTMLElement {
|
||||
}
|
||||
name.title = `${addon.name} ${addon.version}`;
|
||||
|
||||
let toggleDisabledAction = addon.userDisabled ? "enable" : "disable";
|
||||
let canToggleDisabled = hasPermission(addon, toggleDisabledAction);
|
||||
let toggleDisabledButton = card.querySelector('[action="toggle-disabled"]');
|
||||
if (toggleDisabledButton) {
|
||||
toggleDisabledButton.hidden = !canToggleDisabled;
|
||||
let toggleDisabledAction = addon.userDisabled ? "enable" : "disable";
|
||||
toggleDisabledButton.hidden = !hasPermission(addon, toggleDisabledAction);
|
||||
if (addon.type === "theme") {
|
||||
document.l10n.setAttributes(
|
||||
toggleDisabledButton,
|
||||
@ -2742,10 +2755,6 @@ class AddonCard extends HTMLElement {
|
||||
);
|
||||
} else if (addon.type === "extension") {
|
||||
toggleDisabledButton.checked = !addon.userDisabled;
|
||||
document.l10n.setAttributes(
|
||||
toggleDisabledButton,
|
||||
`${toggleDisabledAction}-addon-button-label`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,6 +94,8 @@ skip-if = verify
|
||||
[browser_interaction_telemetry.js]
|
||||
[browser_manage_shortcuts.js]
|
||||
[browser_manage_shortcuts_hidden.js]
|
||||
[browser_menu_button_accessibility.js]
|
||||
[browser_page_accessibility.js]
|
||||
[browser_page_options_install_addon.js]
|
||||
[browser_page_options_updates.js]
|
||||
[browser_panel_item_accesskey.js]
|
||||
|
@ -19,9 +19,10 @@ function assertDisabledSideloadedExtensionElement(managerWindow, addonElement) {
|
||||
);
|
||||
is(
|
||||
doc.l10n.getAttributes(toggleDisabled).id,
|
||||
"enable-addon-button-label",
|
||||
"extension-enable-addon-button-label",
|
||||
"Addon toggle-disabled action has the enable label"
|
||||
);
|
||||
ok(!toggleDisabled.checked, "toggle-disable isn't checked");
|
||||
}
|
||||
|
||||
function assertEnabledSideloadedExtensionElement(managerWindow, addonElement) {
|
||||
@ -31,9 +32,10 @@ function assertEnabledSideloadedExtensionElement(managerWindow, addonElement) {
|
||||
);
|
||||
is(
|
||||
doc.l10n.getAttributes(toggleDisabled).id,
|
||||
"enable-addon-button-label",
|
||||
"extension-enable-addon-button-label",
|
||||
"Addon toggle-disabled action has the enable label"
|
||||
);
|
||||
ok(!toggleDisabled.checked, "toggle-disable isn't checked");
|
||||
}
|
||||
|
||||
function clickEnableExtension(managerWindow, addonElement) {
|
||||
|
@ -110,8 +110,8 @@ add_task(async function testExtensionList() {
|
||||
ok(disableToggle.checked, "The disable toggle is checked");
|
||||
is(
|
||||
doc.l10n.getAttributes(disableToggle).id,
|
||||
"disable-addon-button-label",
|
||||
"The toggle has the disable label"
|
||||
"extension-enable-addon-button-label",
|
||||
"The toggle has the enable label"
|
||||
);
|
||||
ok(disableToggle.getAttribute("aria-label"), "There's an aria-label");
|
||||
ok(!disableToggle.hidden, "The toggle is visible");
|
||||
@ -129,8 +129,8 @@ add_task(async function testExtensionList() {
|
||||
ok(!disableToggle.checked, "The disable toggle is unchecked");
|
||||
is(
|
||||
doc.l10n.getAttributes(disableToggle).id,
|
||||
"enable-addon-button-label",
|
||||
"The button has the enable label"
|
||||
"extension-enable-addon-button-label",
|
||||
"The button has the same enable label"
|
||||
);
|
||||
ok(disableToggle.getAttribute("aria-label"), "There's an aria-label");
|
||||
|
||||
|
@ -0,0 +1,81 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function testOpenMenu(btn, method) {
|
||||
let shown = BrowserTestUtils.waitForEvent(btn.ownerGlobal, "shown", true);
|
||||
await method();
|
||||
await shown;
|
||||
is(btn.getAttribute("aria-expanded"), "true", "expanded when open");
|
||||
}
|
||||
|
||||
async function testCloseMenu(btn, method) {
|
||||
let hidden = BrowserTestUtils.waitForEvent(btn.ownerGlobal, "hidden", true);
|
||||
await method();
|
||||
await hidden;
|
||||
is(btn.getAttribute("aria-expanded"), "false", "not expanded when closed");
|
||||
}
|
||||
|
||||
async function testButton(btn) {
|
||||
let win = btn.ownerGlobal;
|
||||
|
||||
is(btn.getAttribute("aria-haspopup"), "menu", "it has a menu");
|
||||
is(btn.getAttribute("aria-expanded"), "false", "not expanded");
|
||||
|
||||
info("Test open/close with mouse");
|
||||
await testOpenMenu(btn, () => {
|
||||
EventUtils.synthesizeMouseAtCenter(btn, {}, win);
|
||||
});
|
||||
await testCloseMenu(btn, () => {
|
||||
let spacer = win.document.querySelector(".main-heading .spacer");
|
||||
EventUtils.synthesizeMouseAtCenter(spacer, {}, win);
|
||||
});
|
||||
|
||||
info("Test open/close with keyboard");
|
||||
await testOpenMenu(btn, async () => {
|
||||
btn.focus();
|
||||
EventUtils.synthesizeKey(" ", {}, win);
|
||||
});
|
||||
await testCloseMenu(btn, () => {
|
||||
EventUtils.synthesizeKey("Escape", {}, win);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function testPageOptionsMenuButton() {
|
||||
let win = await loadInitialView("extension");
|
||||
|
||||
await testButton(
|
||||
win.document.querySelector(".page-options-menu .more-options-button")
|
||||
);
|
||||
|
||||
await closeView(win);
|
||||
});
|
||||
|
||||
add_task(async function testCardMoreOptionsButton() {
|
||||
let id = "more-options-button@mochi.test";
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
applications: { gecko: { id } },
|
||||
},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
await extension.startup();
|
||||
|
||||
let win = await loadInitialView("extension");
|
||||
let card = getAddonCard(win, id);
|
||||
|
||||
info("Check list page");
|
||||
await testButton(card.querySelector(".more-options-button"));
|
||||
|
||||
let viewLoaded = waitForViewLoad(win);
|
||||
EventUtils.synthesizeMouseAtCenter(card, {}, win);
|
||||
await viewLoaded;
|
||||
|
||||
info("Check detail page");
|
||||
card = getAddonCard(win, id);
|
||||
await testButton(card.querySelector(".more-options-button"));
|
||||
|
||||
await closeView(win);
|
||||
await extension.unload();
|
||||
});
|
@ -0,0 +1,15 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function testPageTitle() {
|
||||
let win = await loadInitialView("extension");
|
||||
let title = win.document.querySelector("title");
|
||||
is(
|
||||
win.document.l10n.getAttributes(title).id,
|
||||
"addons-page-title",
|
||||
"The page title is set"
|
||||
);
|
||||
await closeView(win);
|
||||
});
|
Loading…
Reference in New Issue
Block a user