зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1196102 - Heartbeat: Support prompts with a button instead of rating stars. r=bgrins
--HG-- extra : commitid : 7j2LYTqd47n extra : rebase_source : d423ae516a7ea47393629674550aa278dce994f4
This commit is contained in:
Родитель
7be0da2ef5
Коммит
72963b0294
|
@ -99,15 +99,26 @@ if (typeof Mozilla == 'undefined') {
|
|||
};
|
||||
|
||||
Mozilla.UITour.showHeartbeat = function(message, thankyouMessage, flowId, engagementURL,
|
||||
learnMoreLabel, learnMoreURL) {
|
||||
_sendEvent('showHeartbeat', {
|
||||
learnMoreLabel, learnMoreURL, options) {
|
||||
var args = {
|
||||
message: message,
|
||||
thankyouMessage: thankyouMessage,
|
||||
flowId: flowId,
|
||||
engagementURL: engagementURL,
|
||||
learnMoreLabel: learnMoreLabel,
|
||||
learnMoreURL: learnMoreURL,
|
||||
});
|
||||
};
|
||||
|
||||
if (options) {
|
||||
for (var option in options) {
|
||||
if (!options.hasOwnProperty(option)) {
|
||||
continue;
|
||||
}
|
||||
args[option] = options[option];
|
||||
}
|
||||
}
|
||||
|
||||
_sendEvent('showHeartbeat', args);
|
||||
};
|
||||
|
||||
Mozilla.UITour.showHighlight = function(target, effect) {
|
||||
|
|
|
@ -461,17 +461,22 @@ this.UITour = {
|
|||
// Validate the input parameters.
|
||||
if (typeof data.message !== "string" || data.message === "") {
|
||||
log.error("showHeartbeat: Invalid message specified.");
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data.thankyouMessage !== "string" || data.thankyouMessage === "") {
|
||||
log.error("showHeartbeat: Invalid thank you message specified.");
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data.flowId !== "string" || data.flowId === "") {
|
||||
log.error("showHeartbeat: Invalid flowId specified.");
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.engagementButtonLabel && typeof data.engagementButtonLabel != "string") {
|
||||
log.error("showHeartbeat: Invalid engagementButtonLabel specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally show the Heartbeat UI.
|
||||
|
@ -1105,7 +1110,8 @@ this.UITour = {
|
|||
* Show the Heartbeat UI to request user feedback. This function reports back to the
|
||||
* caller using |notify|. The notification event name reflects the current status the UI
|
||||
* is in (either "Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed",
|
||||
* "Heartbeat:LearnMore" or "Heartbeat:Voted"). When a "Heartbeat:Voted" event is notified
|
||||
* "Heartbeat:LearnMore", "Heartbeat:Engaged" or "Heartbeat:Voted").
|
||||
* When a "Heartbeat:Voted" event is notified
|
||||
* the data payload contains a |score| field which holds the rating picked by the user.
|
||||
* Please note that input parameters are already validated by the caller.
|
||||
*
|
||||
|
@ -1119,8 +1125,11 @@ this.UITour = {
|
|||
* @param {String} aOptions.flowId
|
||||
* An identifier for this rating flow. Please note that this is only used to
|
||||
* identify the notification box.
|
||||
* @param {String} [aOptions.engagementButtonLabel=null]
|
||||
* The text of the engagement button to use instad of stars. If this is null
|
||||
* or invalid, rating stars are used.
|
||||
* @param {String} [aOptions.engagementURL=null]
|
||||
* The engagement URL to open in a new tab once user has voted. If this is null
|
||||
* The engagement URL to open in a new tab once user has engaged. If this is null
|
||||
* or invalid, no new tab is opened.
|
||||
* @param {String} [aOptions.learnMoreLabel=null]
|
||||
* The label of the learn more link. No link will be shown if this is null.
|
||||
|
@ -1130,10 +1139,25 @@ this.UITour = {
|
|||
*/
|
||||
showHeartbeat(aChromeWindow, aOptions) {
|
||||
let nb = aChromeWindow.document.getElementById("high-priority-global-notificationbox");
|
||||
let buttons = null;
|
||||
|
||||
if (aOptions.engagementButtonLabel) {
|
||||
buttons = [{
|
||||
label: aOptions.engagementButtonLabel,
|
||||
callback: () => {
|
||||
// Let the consumer know user engaged.
|
||||
this.notify("Heartbeat:Engaged", { flowId: aOptions.flowId, timestamp: Date.now() });
|
||||
|
||||
userEngaged(new Map([
|
||||
["type", "button"],
|
||||
["flowid", aOptions.flowId]
|
||||
]));
|
||||
},
|
||||
}];
|
||||
}
|
||||
// Create the notification. Prefix its ID to decrease the chances of collisions.
|
||||
let notice = nb.appendNotification(aOptions.message, "heartbeat-" + aOptions.flowId,
|
||||
"chrome://browser/skin/heartbeat-icon.svg", nb.PRIORITY_INFO_HIGH, null, function() {
|
||||
"chrome://browser/skin/heartbeat-icon.svg", nb.PRIORITY_INFO_HIGH, buttons, function() {
|
||||
// Let the consumer know the notification bar was closed. This also happens
|
||||
// after voting.
|
||||
this.notify("Heartbeat:NotificationClosed", { flowId: aOptions.flowId, timestamp: Date.now() });
|
||||
|
@ -1145,11 +1169,51 @@ this.UITour = {
|
|||
let messageText =
|
||||
aChromeWindow.document.getAnonymousElementByAttribute(notice, "anonid", "messageText");
|
||||
|
||||
function userEngaged(aEngagementParams) {
|
||||
// Make the heartbeat icon pulse twice.
|
||||
notice.label = aOptions.thankyouMessage;
|
||||
messageImage.classList.remove("pulse-onshow");
|
||||
messageImage.classList.add("pulse-twice");
|
||||
|
||||
// Remove all the children of the notice (rating container
|
||||
// and the flex).
|
||||
while (notice.firstChild) {
|
||||
notice.removeChild(notice.firstChild);
|
||||
}
|
||||
|
||||
// Make sure that we have a valid URL. If we haven't, do not open the engagement page.
|
||||
let engagementURL = null;
|
||||
try {
|
||||
engagementURL = new URL(aOptions.engagementURL);
|
||||
} catch (error) {
|
||||
log.error("showHeartbeat: Invalid URL specified.");
|
||||
}
|
||||
|
||||
// Just open the engagement tab if we have a valid engagement URL.
|
||||
if (engagementURL) {
|
||||
for (let [param, value] of aEngagementParams) {
|
||||
engagementURL.searchParams.append(param, value);
|
||||
}
|
||||
|
||||
// Open the engagement URL in a new tab.
|
||||
aChromeWindow.gBrowser.selectedTab =
|
||||
aChromeWindow.gBrowser.addTab(engagementURL.toString(), {
|
||||
owner: aChromeWindow.gBrowser.selectedTab,
|
||||
relatedToCurrent: true
|
||||
});
|
||||
}
|
||||
|
||||
// Remove the notification bar after 3 seconds.
|
||||
aChromeWindow.setTimeout(() => {
|
||||
nb.removeNotification(notice);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Create the fragment holding the rating UI.
|
||||
let frag = aChromeWindow.document.createDocumentFragment();
|
||||
|
||||
// Build the Heartbeat star rating.
|
||||
const numStars = 5;
|
||||
const numStars = aOptions.engagementButtonLabel ? 0 : 5;
|
||||
let ratingContainer = aChromeWindow.document.createElement("hbox");
|
||||
ratingContainer.id = "star-rating-container";
|
||||
|
||||
|
@ -1170,44 +1234,12 @@ this.UITour = {
|
|||
// Let the consumer know user voted.
|
||||
this.notify("Heartbeat:Voted", { flowId: aOptions.flowId, score: rating, timestamp: Date.now() });
|
||||
|
||||
// Make the heartbeat icon pulse twice.
|
||||
notice.label = aOptions.thankyouMessage;
|
||||
messageImage.classList.remove("pulse-onshow");
|
||||
messageImage.classList.add("pulse-twice");
|
||||
|
||||
// Remove all the children of the notice (rating container
|
||||
// and the flex).
|
||||
while (notice.firstChild) {
|
||||
notice.removeChild(notice.firstChild);
|
||||
}
|
||||
|
||||
// Make sure that we have a valid URL. If we haven't, do not open the engagement page.
|
||||
let engagementURL = null;
|
||||
try {
|
||||
engagementURL = new URL(aOptions.engagementURL);
|
||||
} catch (error) {
|
||||
log.error("showHeartbeat: Invalid URL specified.");
|
||||
}
|
||||
|
||||
// Just open the engagement tab if we have a valid engagement URL.
|
||||
if (engagementURL) {
|
||||
// Append the score data to the engagement URL.
|
||||
engagementURL.searchParams.append("type", "stars");
|
||||
engagementURL.searchParams.append("score", rating);
|
||||
engagementURL.searchParams.append("flowid", aOptions.flowId);
|
||||
|
||||
// Open the engagement URL in a new tab.
|
||||
aChromeWindow.gBrowser.selectedTab =
|
||||
aChromeWindow.gBrowser.addTab(engagementURL.toString(), {
|
||||
owner: aChromeWindow.gBrowser.selectedTab,
|
||||
relatedToCurrent: true
|
||||
});
|
||||
}
|
||||
|
||||
// Remove the notification bar after 3 seconds.
|
||||
aChromeWindow.setTimeout(() => {
|
||||
nb.removeNotification(notice);
|
||||
}, 3000);
|
||||
// Append the score data to the engagement URL.
|
||||
userEngaged(new Map([
|
||||
["type", "stars"],
|
||||
["score", rating],
|
||||
["flowid", aOptions.flowId]
|
||||
]));
|
||||
}.bind(this));
|
||||
|
||||
// Add it to the container.
|
||||
|
|
|
@ -249,6 +249,56 @@ let tests = [
|
|||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Test that the engagement button opens the engagement URL.
|
||||
*/
|
||||
function test_heartbeat_engagement_button(done) {
|
||||
let engagementURL = "http://example.com";
|
||||
let flowId = "ui-engagewithfirefox-" + Math.random();
|
||||
let originalTabCount = gBrowser.tabs.length;
|
||||
const expectedTabCount = originalTabCount + 1;
|
||||
let heartbeatEngagedSeen = false;
|
||||
|
||||
gContentAPI.observe(function (aEventName, aData) {
|
||||
switch (aEventName) {
|
||||
case "Heartbeat:NotificationOffered": {
|
||||
info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
|
||||
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
|
||||
let notification = getHeartbeatNotification(flowId);
|
||||
is(notification.querySelectorAll(".star-x").length, 0, "No stars should be present");
|
||||
// The UI was just shown. We can simulate a click on the engagement button.
|
||||
let engagementButton = notification.querySelector(".notification-button");
|
||||
is(engagementButton.label, "Engage Me", "Check engagement button text");
|
||||
engagementButton.doCommand();
|
||||
break;
|
||||
}
|
||||
case "Heartbeat:Engaged": {
|
||||
info("'Heartbeat:Engaged' notification received (timestamp " + aData.timestamp.toString() + ").");
|
||||
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
|
||||
heartbeatEngagedSeen = true;
|
||||
break;
|
||||
}
|
||||
case "Heartbeat:NotificationClosed": {
|
||||
info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
|
||||
ok(heartbeatEngagedSeen, "Heartbeat:Engaged should have been received");
|
||||
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
|
||||
is(gBrowser.tabs.length, expectedTabCount, "Engagement URL should open in a new tab.");
|
||||
gBrowser.removeCurrentTab();
|
||||
done();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// We are not expecting other states for this test.
|
||||
ok(false, "Unexpected notification received: " + aEventName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL, null, null, {
|
||||
engagementButtonLabel: "Engage Me",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Test that the learn more link is displayed and that the page is correctly opened when
|
||||
* clicking on it.
|
||||
|
@ -290,5 +340,23 @@ let tests = [
|
|||
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, dummyURL,
|
||||
"What is this?", dummyURL);
|
||||
}
|
||||
},
|
||||
|
||||
function test_invalidEngagementButtonLabel(done) {
|
||||
let engagementURL = "http://example.com";
|
||||
let flowId = "ui-engagewithfirefox-" + Math.random();
|
||||
|
||||
Services.mm.addMessageListener("UITour:onPageEvent", function onPageEvent(aMessage) {
|
||||
Services.mm.removeMessageListener("UITour:onPageEvent", onPageEvent);
|
||||
SimpleTest.executeSoon(() => {
|
||||
ok(!(UITour.tourBrowsersByWindow.get(window) &&
|
||||
UITour.tourBrowsersByWindow.get(window).has(gBrowser.selectedBrowser)),
|
||||
"Invalid engagementButtonLabel should prevent init");
|
||||
});
|
||||
done();
|
||||
});
|
||||
gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL, null, null, {
|
||||
engagementButtonLabel: 42,
|
||||
});
|
||||
},
|
||||
];
|
||||
|
|
Загрузка…
Ссылка в новой задаче