зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1587809: Give the CFR address bar button a more descriptive tooltip/a11y label. Announce its appearance to screen reader users. r=andreio,fluent-reviewers,flod
1. Previously, the label and tooltip for all recommendations was just "Recommendation", even though the icon was different for extension and feature recommendations. Because users might not be able to see the icon and/or determine its meaning, it is important that this is communicated in the tooltip and a11y label. 2. Screen reader users won't know this has appeared, even though this attracts some attention visually. Therefore, provide a specific announcement for screen reader users when the recommendation appears. Differential Revision: https://phabricator.services.mozilla.com/D47718 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
bee57f89e3
Коммит
f0d5de33b0
|
@ -129,7 +129,9 @@ const CFR_MESSAGES = [
|
|||
layout: "addon_recommendation",
|
||||
category: "cfrAddons",
|
||||
bucket_id: "CFR_M1",
|
||||
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
|
||||
notification_text: {
|
||||
string_id: "cfr-doorhanger-extension-notification2",
|
||||
},
|
||||
heading_text: { string_id: "cfr-doorhanger-extension-heading" },
|
||||
info_icon: {
|
||||
label: { string_id: "cfr-doorhanger-extension-sumo-link" },
|
||||
|
@ -198,7 +200,9 @@ const CFR_MESSAGES = [
|
|||
layout: "addon_recommendation",
|
||||
category: "cfrAddons",
|
||||
bucket_id: "CFR_M1",
|
||||
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
|
||||
notification_text: {
|
||||
string_id: "cfr-doorhanger-extension-notification2",
|
||||
},
|
||||
heading_text: { string_id: "cfr-doorhanger-extension-heading" },
|
||||
info_icon: {
|
||||
label: { string_id: "cfr-doorhanger-extension-sumo-link" },
|
||||
|
@ -268,7 +272,9 @@ const CFR_MESSAGES = [
|
|||
layout: "addon_recommendation",
|
||||
category: "cfrAddons",
|
||||
bucket_id: "CFR_M1",
|
||||
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
|
||||
notification_text: {
|
||||
string_id: "cfr-doorhanger-extension-notification2",
|
||||
},
|
||||
heading_text: { string_id: "cfr-doorhanger-extension-heading" },
|
||||
info_icon: {
|
||||
label: { string_id: "cfr-doorhanger-extension-sumo-link" },
|
||||
|
@ -339,7 +345,9 @@ const CFR_MESSAGES = [
|
|||
layout: "addon_recommendation",
|
||||
category: "cfrAddons",
|
||||
bucket_id: "CFR_M1",
|
||||
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
|
||||
notification_text: {
|
||||
string_id: "cfr-doorhanger-extension-notification2",
|
||||
},
|
||||
heading_text: { string_id: "cfr-doorhanger-extension-heading" },
|
||||
info_icon: {
|
||||
label: { string_id: "cfr-doorhanger-extension-sumo-link" },
|
||||
|
@ -413,7 +421,9 @@ const CFR_MESSAGES = [
|
|||
layout: "addon_recommendation",
|
||||
category: "cfrAddons",
|
||||
bucket_id: "CFR_M1",
|
||||
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
|
||||
notification_text: {
|
||||
string_id: "cfr-doorhanger-extension-notification2",
|
||||
},
|
||||
heading_text: { string_id: "cfr-doorhanger-extension-heading" },
|
||||
info_icon: {
|
||||
label: { string_id: "cfr-doorhanger-extension-sumo-link" },
|
||||
|
@ -483,7 +493,7 @@ const CFR_MESSAGES = [
|
|||
layout: "message_and_animation",
|
||||
category: "cfrFeatures",
|
||||
bucket_id: "CFR_PIN_TAB",
|
||||
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
|
||||
notification_text: { string_id: "cfr-doorhanger-feature-notification" },
|
||||
heading_text: { string_id: "cfr-doorhanger-pintab-heading" },
|
||||
info_icon: {
|
||||
label: { string_id: "cfr-doorhanger-extension-sumo-link" },
|
||||
|
@ -597,7 +607,7 @@ const CFR_MESSAGES = [
|
|||
sumo_path: "extensionrecommendations",
|
||||
},
|
||||
notification_text: {
|
||||
string_id: "cfr-doorhanger-extension-notification",
|
||||
string_id: "cfr-doorhanger-feature-notification",
|
||||
},
|
||||
category: "cfrFeatures",
|
||||
},
|
||||
|
|
|
@ -182,13 +182,18 @@ class PageAction {
|
|||
async showAddressBarNotifier(recommendation, shouldExpand = false) {
|
||||
this.container.hidden = false;
|
||||
|
||||
this.label.value = await this.getStrings(
|
||||
let notificationText = await this.getStrings(
|
||||
recommendation.content.notification_text
|
||||
);
|
||||
|
||||
this.label.value = notificationText;
|
||||
this.button.setAttribute(
|
||||
"tooltiptext",
|
||||
await this.getStrings(recommendation.content.notification_text)
|
||||
notificationText.attributes.tooltiptext
|
||||
);
|
||||
// For a11y, we want the more descriptive text.
|
||||
this.container.setAttribute(
|
||||
"aria-label",
|
||||
notificationText.attributes.tooltiptext
|
||||
);
|
||||
this.button.setAttribute(
|
||||
"data-cfr-icon",
|
||||
|
@ -216,6 +221,11 @@ class PageAction {
|
|||
|
||||
this.addImpression(recommendation);
|
||||
}
|
||||
|
||||
this.window.A11yUtils.announce({
|
||||
raw: notificationText.attributes["a11y-announcement"],
|
||||
source: this.container,
|
||||
});
|
||||
}
|
||||
|
||||
hideAddressBarNotifier() {
|
||||
|
|
|
@ -143,7 +143,7 @@ const MESSAGES = () => [
|
|||
content: {
|
||||
layout: "icon_and_message",
|
||||
category: "cfrFeatures",
|
||||
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
|
||||
notification_text: { string_id: "cfr-doorhanger-feature-notification" },
|
||||
heading_text: { string_id: "cfr-doorhanger-sync-bookmarks-header" },
|
||||
info_icon: {
|
||||
label: { string_id: "cfr-doorhanger-extension-sumo-link" },
|
||||
|
|
|
@ -15,65 +15,72 @@ const createDummyRecommendation = ({
|
|||
layout,
|
||||
skip_address_bar_notifier,
|
||||
template,
|
||||
}) => ({
|
||||
template,
|
||||
content: {
|
||||
layout: layout || "addon_recommendation",
|
||||
category,
|
||||
anchor_id: "page-action-buttons",
|
||||
skip_address_bar_notifier,
|
||||
notification_text: "Mochitest",
|
||||
heading_text: heading_text || "Mochitest",
|
||||
info_icon: {
|
||||
label: { attributes: { tooltiptext: "Why am I seeing this" } },
|
||||
sumo_path: "extensionrecommendations",
|
||||
},
|
||||
icon: "foo",
|
||||
icon_dark_theme: "bar",
|
||||
learn_more: "extensionrecommendations",
|
||||
addon: {
|
||||
id: "addon-id",
|
||||
title: "Addon name",
|
||||
icon: "foo",
|
||||
author: "Author name",
|
||||
amo_url: "https://example.com",
|
||||
},
|
||||
descriptionDetails: { steps: [] },
|
||||
text: "Mochitest",
|
||||
buttons: {
|
||||
primary: {
|
||||
label: {
|
||||
value: "OK",
|
||||
attributes: { accesskey: "O" },
|
||||
},
|
||||
action: {
|
||||
type: action.type,
|
||||
data: {},
|
||||
},
|
||||
}) => {
|
||||
let recommendation = {
|
||||
template,
|
||||
content: {
|
||||
layout: layout || "addon_recommendation",
|
||||
category,
|
||||
anchor_id: "page-action-buttons",
|
||||
skip_address_bar_notifier,
|
||||
heading_text: heading_text || "Mochitest",
|
||||
info_icon: {
|
||||
label: { attributes: { tooltiptext: "Why am I seeing this" } },
|
||||
sumo_path: "extensionrecommendations",
|
||||
},
|
||||
secondary: [
|
||||
{
|
||||
icon: "foo",
|
||||
icon_dark_theme: "bar",
|
||||
learn_more: "extensionrecommendations",
|
||||
addon: {
|
||||
id: "addon-id",
|
||||
title: "Addon name",
|
||||
icon: "foo",
|
||||
author: "Author name",
|
||||
amo_url: "https://example.com",
|
||||
},
|
||||
descriptionDetails: { steps: [] },
|
||||
text: "Mochitest",
|
||||
buttons: {
|
||||
primary: {
|
||||
label: {
|
||||
value: "Cancel",
|
||||
attributes: { accesskey: "C" },
|
||||
value: "OK",
|
||||
attributes: { accesskey: "O" },
|
||||
},
|
||||
action: {
|
||||
type: action.type,
|
||||
data: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: {
|
||||
value: "Cancel 1",
|
||||
attributes: { accesskey: "A" },
|
||||
secondary: [
|
||||
{
|
||||
label: {
|
||||
value: "Cancel",
|
||||
attributes: { accesskey: "C" },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: {
|
||||
value: "Cancel 2",
|
||||
attributes: { accesskey: "B" },
|
||||
{
|
||||
label: {
|
||||
value: "Cancel 1",
|
||||
attributes: { accesskey: "A" },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
label: {
|
||||
value: "Cancel 2",
|
||||
attributes: { accesskey: "B" },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
recommendation.content.notification_text = new String("Mochitest"); // eslint-disable-line
|
||||
recommendation.content.notification_text.attributes = {
|
||||
tooltiptext: "Mochitest tooltip",
|
||||
"a11y-announcement": "Mochitest announcement",
|
||||
};
|
||||
return recommendation;
|
||||
};
|
||||
|
||||
function checkCFRFeaturesElements(notification) {
|
||||
Assert.ok(notification.hidden === false, "Panel should be visible");
|
||||
|
|
|
@ -2494,6 +2494,7 @@ describe("ASRouter", () => {
|
|||
document: { getElementById },
|
||||
promiseDocumentFlushed: sandbox.stub().resolves([{ width: 0 }]),
|
||||
setTimeout: sandbox.stub(),
|
||||
A11yUtils: { announce: sandbox.stub() },
|
||||
};
|
||||
const firstMessage = { ...FAKE_RECOMMENDATION, id: "first_message" };
|
||||
const secondMessage = { ...FAKE_RECOMMENDATION, id: "second_message" };
|
||||
|
|
|
@ -12,6 +12,7 @@ describe("CFRPageActions", () => {
|
|||
let globals;
|
||||
let containerElem;
|
||||
let elements;
|
||||
let announceStub;
|
||||
|
||||
const elementIDs = [
|
||||
"urlbar",
|
||||
|
@ -41,6 +42,8 @@ describe("CFRPageActions", () => {
|
|||
sandbox = sinon.createSandbox();
|
||||
clock = sandbox.useFakeTimers();
|
||||
|
||||
announceStub = sandbox.stub();
|
||||
const A11yUtils = { announce: announceStub };
|
||||
fakeRecommendation = { ...FAKE_RECOMMENDATION };
|
||||
fakeHost = "mozilla.org";
|
||||
fakeBrowser = {
|
||||
|
@ -64,6 +67,7 @@ describe("CFRPageActions", () => {
|
|||
},
|
||||
PrivateBrowsingUtils: { isWindowPrivate: sandbox.stub().returns(false) },
|
||||
gBrowser: { selectedBrowser: fakeBrowser },
|
||||
A11yUtils,
|
||||
});
|
||||
document.createXULElement = document.createElement;
|
||||
|
||||
|
@ -94,22 +98,19 @@ describe("CFRPageActions", () => {
|
|||
|
||||
describe("PageAction", () => {
|
||||
let pageAction;
|
||||
let getStringsStub;
|
||||
|
||||
beforeEach(() => {
|
||||
pageAction = new PageAction(window, dispatchStub);
|
||||
getStringsStub = sandbox.stub(pageAction, "getStrings").resolves("");
|
||||
});
|
||||
|
||||
describe("#showAddressBarNotifier", () => {
|
||||
it("should un-hideAddressBarNotifier the element and set the right label value", async () => {
|
||||
const FAKE_NOTIFICATION_TEXT = "FAKE_NOTIFICATION_TEXT";
|
||||
getStringsStub
|
||||
.withArgs(fakeRecommendation.content.notification_text)
|
||||
.resolves(FAKE_NOTIFICATION_TEXT);
|
||||
await pageAction.showAddressBarNotifier(fakeRecommendation);
|
||||
assert.isFalse(pageAction.container.hidden);
|
||||
assert.equal(pageAction.label.value, FAKE_NOTIFICATION_TEXT);
|
||||
assert.equal(
|
||||
pageAction.label.value,
|
||||
fakeRecommendation.content.notification_text
|
||||
);
|
||||
});
|
||||
it("should wait for the document layout to flush", async () => {
|
||||
sandbox.spy(pageAction.label, "getClientRects");
|
||||
|
@ -356,7 +357,6 @@ describe("CFRPageActions", () => {
|
|||
];
|
||||
|
||||
beforeEach(() => {
|
||||
getStringsStub.restore();
|
||||
formatMessagesStub = sandbox
|
||||
.stub()
|
||||
.withArgs({ id: "hello_world" })
|
||||
|
@ -433,6 +433,7 @@ describe("CFRPageActions", () => {
|
|||
describe("#_showPopupOnClick", () => {
|
||||
let translateElementsStub;
|
||||
let setAttributesStub;
|
||||
let getStringsStub;
|
||||
beforeEach(async () => {
|
||||
CFRPageActions.PageActionMap.set(fakeBrowser.ownerGlobal, pageAction);
|
||||
await CFRPageActions.addRecommendation(
|
||||
|
@ -441,6 +442,7 @@ describe("CFRPageActions", () => {
|
|||
fakeRecommendation,
|
||||
dispatchStub
|
||||
);
|
||||
getStringsStub = sandbox.stub(pageAction, "getStrings").resolves("");
|
||||
getStringsStub
|
||||
.callsFake(async a => a) // eslint-disable-line max-nested-callbacks
|
||||
.withArgs({ string_id: "primary_button_id" })
|
||||
|
|
|
@ -81,13 +81,16 @@ export const FAKE_REMOTE_SETTINGS_PROVIDER = {
|
|||
enabled: true,
|
||||
};
|
||||
|
||||
const notificationText = new String("Fake notification text"); // eslint-disable-line
|
||||
notificationText.attributes = { tooltiptext: "Fake tooltip text" };
|
||||
|
||||
export const FAKE_RECOMMENDATION = {
|
||||
id: "fake_id",
|
||||
template: "cfr_doorhanger",
|
||||
content: {
|
||||
category: "cfrDummy",
|
||||
bucket_id: "fake_bucket_id",
|
||||
notification_text: "Fake Notification Text",
|
||||
notification_text: notificationText,
|
||||
info_icon: {
|
||||
label: "Fake Info Icon Label",
|
||||
sumo_path: "a_help_path_fragment",
|
||||
|
|
|
@ -35,7 +35,15 @@ cfr-doorhanger-extension-author = by { $name }
|
|||
|
||||
# This is a notification displayed in the address bar.
|
||||
# When clicked it opens a panel with a message for the user.
|
||||
cfr-doorhanger-extension-notification = Recommendation
|
||||
cfr-doorhanger-extension-notification2 = Recommendation
|
||||
.tooltiptext = Extension recommendation
|
||||
.a11y-announcement = Extension recommendation available
|
||||
|
||||
# This is a notification displayed in the address bar.
|
||||
# When clicked it opens a panel with a message for the user.
|
||||
cfr-doorhanger-feature-notification = Recommendation
|
||||
.tooltiptext = Feature recommendation
|
||||
.a11y-announcement = Feature recommendation available
|
||||
|
||||
## Add-on statistics
|
||||
## These strings are used to display the total number of
|
||||
|
|
Загрузка…
Ссылка в новой задаче