Bug 1571022 - Conditional rendering for CFR feature recommendations r=k88hudson

Differential Revision: https://phabricator.services.mozilla.com/D43630

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andrei Oprea 2019-08-31 00:02:44 +00:00
Родитель 8682c94a42
Коммит 95146c9c2f
5 изменённых файлов: 239 добавлений и 89 удалений

Просмотреть файл

@ -1346,7 +1346,7 @@ class _ASRouter {
} else {
CFRPageActions.addRecommendation(
target,
trigger.param.host,
trigger.param && trigger.param.host,
message,
this.dispatch
);

Просмотреть файл

@ -126,6 +126,7 @@ const CFR_MESSAGES = [
id: "FACEBOOK_CONTAINER_3",
template: "cfr_doorhanger",
content: {
layout: "addon_recommendation",
category: "cfrAddons",
bucket_id: "CFR_M1",
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
@ -194,6 +195,7 @@ const CFR_MESSAGES = [
id: "GOOGLE_TRANSLATE_3",
template: "cfr_doorhanger",
content: {
layout: "addon_recommendation",
category: "cfrAddons",
bucket_id: "CFR_M1",
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
@ -263,6 +265,7 @@ const CFR_MESSAGES = [
id: "YOUTUBE_ENHANCE_3",
template: "cfr_doorhanger",
content: {
layout: "addon_recommendation",
category: "cfrAddons",
bucket_id: "CFR_M1",
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
@ -333,6 +336,7 @@ const CFR_MESSAGES = [
template: "cfr_doorhanger",
exclude: true,
content: {
layout: "addon_recommendation",
category: "cfrAddons",
bucket_id: "CFR_M1",
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
@ -406,6 +410,7 @@ const CFR_MESSAGES = [
template: "cfr_doorhanger",
exclude: true,
content: {
layout: "addon_recommendation",
category: "cfrAddons",
bucket_id: "CFR_M1",
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
@ -475,6 +480,7 @@ const CFR_MESSAGES = [
id: "PIN_TAB",
template: "cfr_doorhanger",
content: {
layout: "message_and_animation",
category: "cfrFeatures",
bucket_id: "CFR_PIN_TAB",
notification_text: { string_id: "cfr-doorhanger-extension-notification" },
@ -526,6 +532,78 @@ const CFR_MESSAGES = [
frequency: { lifetime: 3 },
trigger: { id: "frequentVisits", params: PINNED_TABS_TARGET_SITES },
},
{
id: "SAVE_LOGIN",
frequency: {
lifetime: 3,
},
targeting: "usesFirefoxSync == false",
template: "cfr_doorhanger",
last_modified: 1565907636313,
content: {
layout: "icon_and_message",
text: {
string_id: "cfr-doorhanger-sync-logins-body",
},
icon: "chrome://browser/content/aboutlogins/icons/intro-illustration.svg",
buttons: {
secondary: [
{
label: {
string_id: "cfr-doorhanger-extension-cancel-button",
},
action: {
type: "CANCEL",
},
},
{
label: {
string_id: "cfr-doorhanger-extension-never-show-recommendation",
},
},
{
label: {
string_id: "cfr-doorhanger-extension-manage-settings-button",
},
action: {
type: "OPEN_PREFERENCES_PAGE",
data: {
category: "general-cfrfeatures",
},
},
},
],
primary: {
label: {
string_id: "cfr-doorhanger-sync-logins-ok-button",
},
action: {
type: "OPEN_PREFERENCES_PAGE",
data: {
category: "sync",
},
},
},
},
bucket_id: "CFR_SAVE_LOGIN",
heading_text: {
string_id: "cfr-doorhanger-sync-logins-header",
},
info_icon: {
label: {
string_id: "cfr-doorhanger-extension-sumo-link",
},
sumo_path: "extensionrecommendations",
},
notification_text: {
string_id: "cfr-doorhanger-extension-notification",
},
category: "cfrFeatures",
},
trigger: {
id: "newSavedLogin",
},
},
];
const CFRMessageProvider = {

Просмотреть файл

@ -428,6 +428,7 @@ class PageAction {
}
}
// eslint-disable-next-line max-statements
async _renderPopup(message, browser) {
const { id, content } = message;
@ -451,12 +452,6 @@ class PageAction {
let options = {};
let panelTitle;
// Use the message category as a CSS selector to hide different parts of the
// notification template markup
this.window.document
.getElementById("contextual-feature-recommendation-notification")
.setAttribute("data-notification-category", message.content.category);
headerLabel.value = await this.getStrings(content.heading_text);
headerLink.setAttribute(
"href",
@ -473,12 +468,80 @@ class PageAction {
bucket_id: content.bucket_id,
event: "RATIONALE",
});
// Use the message layout as a CSS selector to hide different parts of the
// notification template markup
this.window.document
.getElementById("contextual-feature-recommendation-notification")
.setAttribute("data-notification-category", content.layout);
switch (content.layout) {
case "icon_and_message":
const author = this.window.document.getElementById(
"cfr-notification-author"
);
author.textContent = await this.getStrings(content.text);
primaryActionCallback = () => {
this._blockMessage(id);
this.dispatchUserAction(primary.action);
this.hideAddressBarNotifier();
this._sendTelemetry({
message_id: id,
bucket_id: content.bucket_id,
event: "ENABLE",
});
RecommendationMap.delete(browser);
};
panelTitle = await this.getStrings(content.heading_text);
options = {
popupIconURL: content.icon,
popupIconClass: "cfr-doorhanger-large-icon",
};
break;
case "message_and_animation":
footerText.textContent = await this.getStrings(content.text);
const stepsContainerId = "cfr-notification-feature-steps";
let stepsContainer = this.window.document.getElementById(
stepsContainerId
);
primaryActionCallback = () => {
this._blockMessage(id);
this.dispatchUserAction(primary.action);
this.hideAddressBarNotifier();
this._sendTelemetry({
message_id: id,
bucket_id: content.bucket_id,
event: "PIN",
});
RecommendationMap.delete(browser);
};
panelTitle = await this.getStrings(content.heading_text);
if (content.addon) {
await this._setAddonAuthorAndRating(this.window.document, content);
if (content.descriptionDetails) {
if (stepsContainer) {
// If it exists we need to empty it
stepsContainer.remove();
stepsContainer = stepsContainer.cloneNode(false);
} else {
stepsContainer = this.window.document.createXULElement("vbox");
stepsContainer.setAttribute("id", stepsContainerId);
}
footerText.parentNode.appendChild(stepsContainer);
for (let step of content.descriptionDetails.steps) {
// This li is a generic xul element with custom styling
const li = this.window.document.createXULElement("li");
this._l10n.setAttributes(li, step.string_id);
stepsContainer.appendChild(li);
}
await this._l10n.translateElements([...stepsContainer.children]);
}
await this._renderPinTabAnimation();
break;
default:
panelTitle = await this.getStrings(content.addon.title);
await this._setAddonAuthorAndRating(this.window.document, content);
// Main body content of the dropdown
footerText.textContent = await this.getStrings(content.text);
options = { popupIconURL: content.addon.icon };
footerLink.value = await this.getStrings({
@ -507,42 +570,6 @@ class PageAction {
});
RecommendationMap.delete(browser);
};
} else {
const stepsContainerId = "cfr-notification-feature-steps";
let stepsContainer = this.window.document.getElementById(
stepsContainerId
);
primaryActionCallback = () => {
this._blockMessage(id);
this.dispatchUserAction(primary.action);
this.hideAddressBarNotifier();
this._sendTelemetry({
message_id: id,
bucket_id: content.bucket_id,
event: "PIN",
});
RecommendationMap.delete(browser);
};
panelTitle = await this.getStrings(content.heading_text);
if (stepsContainer) {
// If it exists we need to empty it
stepsContainer.remove();
stepsContainer = stepsContainer.cloneNode(false);
} else {
stepsContainer = this.window.document.createXULElement("vbox");
stepsContainer.setAttribute("id", stepsContainerId);
}
footerText.parentNode.appendChild(stepsContainer);
for (let step of content.descriptionDetails.steps) {
// This li is a generic xul element with custom styling
const li = this.window.document.createXULElement("li");
this._l10n.setAttributes(li, step.string_id);
stepsContainer.appendChild(li);
}
await this._l10n.translateElements([...stepsContainer.children]);
await this._renderPinTabAnimation();
}
const primaryBtnStrings = await this.getStrings(primary.label);
@ -749,7 +776,8 @@ const CFRPageActions = {
}
if (
browser !== win.gBrowser.selectedBrowser ||
!isHostMatch(browser, host)
// We can have recommendations without URL restrictions
(host && !isHostMatch(browser, host))
) {
return false;
}

Просмотреть файл

@ -8,8 +8,14 @@ const { ASRouter } = ChromeUtils.import(
"resource://activity-stream/lib/ASRouter.jsm"
);
const createDummyRecommendation = ({ action, category, heading_text }) => ({
const createDummyRecommendation = ({
action,
category,
heading_text,
layout,
}) => ({
content: {
layout: layout || "addon_recommendation",
category,
notification_text: "Mochitest",
heading_text: heading_text || "Mochitest",
@ -63,9 +69,10 @@ const createDummyRecommendation = ({ action, category, heading_text }) => ({
function checkCFRFeaturesElements(notification) {
Assert.ok(notification.hidden === false, "Panel should be visible");
Assert.ok(
notification.getAttribute("data-notification-category") === "cfrFeatures",
"Panel have corret data attribute"
Assert.equal(
notification.getAttribute("data-notification-category"),
"message_and_animation",
"Panel have correct data attribute"
);
Assert.ok(
notification.querySelector(
@ -81,9 +88,10 @@ function checkCFRFeaturesElements(notification) {
function checkCFRAddonsElements(notification) {
Assert.ok(notification.hidden === false, "Panel should be visible");
Assert.ok(
notification.getAttribute("data-notification-category") === "cfrAddons",
"Panel have corret data attribute"
Assert.equal(
notification.getAttribute("data-notification-category"),
"addon_recommendation",
"Panel have correct data attribute"
);
Assert.ok(
notification.querySelector("#cfr-notification-footer-text-and-addon-info"),
@ -115,13 +123,19 @@ function clearNotifications() {
function trigger_cfr_panel(
browser,
trigger,
{ action = { type: "FOO" }, heading_text, category = "cfrAddons" } = {}
{
action = { type: "FOO" },
heading_text,
category = "cfrAddons",
layout,
} = {}
) {
// a fake action type will result in the action being ignored
const recommendation = createDummyRecommendation({
action,
category,
heading_text,
layout,
});
if (category !== "cfrAddons") {
delete recommendation.content.addon;
@ -340,6 +354,7 @@ add_task(async function test_cfr_pin_tab_notification_show() {
const response = await trigger_cfr_panel(browser, "example.com", {
action: { type: "PIN_CURRENT_TAB" },
category: "cfrFeatures",
layout: "message_and_animation",
});
Assert.ok(
response,
@ -395,6 +410,7 @@ add_task(async function test_cfr_features_and_addon_show() {
let response = await trigger_cfr_panel(browser, "example.com", {
action: { type: "PIN_CURRENT_TAB" },
category: "cfrFeatures",
layout: "message_and_animation",
});
Assert.ok(
response,
@ -522,7 +538,7 @@ add_task(async function test_cfr_addon_and_features_show() {
// Trigger Addon CFR
response = await trigger_cfr_panel(browser, "example.com", {
action: { type: "PIN_CURRENT_TAB" },
category: "cfrFeatures",
category: "cfrAddons",
});
Assert.ok(
response,
@ -542,7 +558,7 @@ add_task(async function test_cfr_addon_and_features_show() {
.hidden === false,
"Panel should be visible"
);
checkCFRFeaturesElements(
checkCFRAddonsElements(
document.getElementById("contextual-feature-recommendation-notification")
);

Просмотреть файл

@ -238,6 +238,11 @@
margin-inline-end: 4px;
}
#contextual-feature-recommendation-notification .cfr-doorhanger-large-icon {
width: 64px;
height: 64px;
}
#contextual-feature-recommendation-notification .popup-notification-body-container {
padding-bottom: 0;
}
@ -257,12 +262,35 @@
font-size: 13px;
}
#contextual-feature-recommendation-notification[data-notification-category="cfrFeatures"] .popup-notification-body-container,
#contextual-feature-recommendation-notification[data-notification-category="cfrFeatures"] #cfr-notification-footer-addon-info,
#contextual-feature-recommendation-notification[data-notification-category="cfrAddons"] #cfr-notification-feature-steps {
#contextual-feature-recommendation-notification[data-notification-category="message_and_animation"] .popup-notification-body-container,
#contextual-feature-recommendation-notification[data-notification-category="message_and_animation"] #cfr-notification-footer-addon-info,
#contextual-feature-recommendation-notification[data-notification-category="addon_recommendation"] #cfr-notification-feature-steps,
#contextual-feature-recommendation-notification[data-notification-category="icon_and_message"] .popup-notification-footer-container {
display: none;
}
/*
* `icon_and_message` CFR doorhanger with: icon, title and subtitle.
* No panel header is shown
*/
#contextual-feature-recommendation-notification[data-notification-category="icon_and_message"] #cfr-notification-header {
display: none;
}
#contextual-feature-recommendation-notification[data-notification-category="icon_and_message"] .popup-notification-description {
font-size: 16px;
font-weight: 600;
margin-bottom: 4px;
}
#contextual-feature-recommendation-notification[data-notification-category="icon_and_message"] popupnotificationcontent {
display: block; /* This forces the subtitle content to wrap */
}
#contextual-feature-recommendation-notification[data-notification-category="icon_and_message"] .popup-notification-body-container {
padding-bottom: 20px;
}
#cfr-notification-feature-steps {
display: flex;
flex-direction: column;
@ -284,7 +312,7 @@
inset-inline-start: 0;
}
#contextual-feature-recommendation-notification[data-notification-category="cfrFeatures"] #cfr-notification-footer-text {
#contextual-feature-recommendation-notification[data-notification-category="message_and_animation"] #cfr-notification-footer-text {
font-size: 14px;
font-weight: 600;
}
@ -370,7 +398,7 @@
}
}
#contextual-feature-recommendation-notification[data-notification-category="cfrAddons"] #cfr-notification-footer-pintab-animation-container {
#contextual-feature-recommendation-notification[data-notification-category="addon_recommendation"] #cfr-notification-footer-pintab-animation-container {
display: none;
}