Bug 1546980 - Add ratings and user counts to discopane r=mstriemer,flod
authorRob Wu <rob@robwu.nl>
Wed, 08 May 2019 23:36:32 +0000
changeset 532040 043d273f14ca4ff406c4d6674701ecfa51ecbd9d
parent 532039 af2c36273f0c06c24d6dcc5625805f92f390eec0
child 532041 134e12b67bba8cb1921aff84ddab311c4305f8a1
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstriemer, flod
bugs1546980
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1546980 - Add ratings and user counts to discopane r=mstriemer,flod Differential Revision: https://phabricator.services.mozilla.com/D29481
toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
toolkit/mozapps/extensions/content/aboutaddons.css
toolkit/mozapps/extensions/content/aboutaddons.html
toolkit/mozapps/extensions/content/aboutaddons.js
toolkit/mozapps/extensions/content/rating-star.css
toolkit/mozapps/extensions/test/browser/browser_html_discover_view.js
--- a/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
@@ -362,16 +362,20 @@ discopane-notice-recommendations =
 discopane-notice-learn-more = Learn more
 
 privacy-policy = Privacy Policy
 
 # Refers to the author of an add-on, shown below the name of the add-on.
 # Variables:
 #   $author (string) - The name of the add-on developer.
 created-by-author = by <a data-l10n-name="author">{ $author }</a>
+# Shows the number of daily users of the add-on.
+# Variables:
+#   $dailyUsers (number) - The number of daily users.
+user-count = Users: { $dailyUsers }
 install-extension-button = Add to { -brand-product-name }
 install-theme-button = Install Theme
 # The label of the button that appears after installing an add-on. Upon click,
 # the detailed add-on view is opened, from where the add-on can be managed.
 manage-addon-button = Manage
 find-more-addons = Find more add-ons
 
 ## Add-on actions
--- a/toolkit/mozapps/extensions/content/aboutaddons.css
+++ b/toolkit/mozapps/extensions/content/aboutaddons.css
@@ -152,16 +152,24 @@ recommended-addon-card .addon-descriptio
   flex-direction: column;
 }
 
 .disco-addon-author {
   font-size: 12px;
   font-weight: normal;
 }
 
+.disco-description-statistics {
+  margin-top: 1em;
+  display: grid;
+  grid-template-columns: repeat(2, max-content);
+  grid-column-gap: 2em;
+  align-items: center;
+}
+
 .disco-cta-button {
   font-size: 14px;
   flex-shrink: 0;
   flex-grow: 0;
   align-self: baseline;
 }
 
 .disco-cta-button[action="install-addon"]::before {
--- a/toolkit/mozapps/extensions/content/aboutaddons.html
+++ b/toolkit/mozapps/extensions/content/aboutaddons.html
@@ -71,16 +71,20 @@
       <button class="disco-cta-button" data-l10n-id="manage-addon-button" action="manage-addon"></button>
     </template>
 
     <template name="addon-description-in-disco-card">
       <div>
         <strong class="disco-description-intro"></strong>
         <span class="disco-description-main"></span>
       </div>
+      <div class="disco-description-statistics">
+        <five-star-rating></five-star-rating>
+        <span class="disco-user-count"></span>
+      </div>
     </template>
 
     <template name="addon-details">
       <div class="addon-detail-description"></div>
       <div class="addon-detail-contribute">
         <label data-l10n-id="detail-contributions-description"></label>
         <button
           class="addon-detail-contribute-button"
--- a/toolkit/mozapps/extensions/content/aboutaddons.js
+++ b/toolkit/mozapps/extensions/content/aboutaddons.js
@@ -203,16 +203,19 @@ class DiscoAddonWrapper {
     // The property names and values should have the same semantics as
     // AddonWrapper, to ease the reuse of helper functions in this file.
     this.id = repositoryAddon.id;
     this.type = repositoryAddon.type;
     this.name = repositoryAddon.name;
     this.screenshots = repositoryAddon.screenshots;
     this.sourceURI = repositoryAddon.sourceURI;
     this.creator = repositoryAddon.creator;
+    this.averageRating = repositoryAddon.averageRating;
+
+    this.dailyUsers = details.addon.average_daily_users;
 
     this.editorialHeading = details.heading_text;
     this.editorialDescription = details.description_text;
     this.iconURL = details.addon.icon_url;
     this.amoListingUrl = details.addon.url;
   }
 }
 
@@ -1272,17 +1275,33 @@ class RecommendedAddonCard extends HTMLE
     // the add-on's original description that would normally appear on a card.
     card.querySelector(".disco-description-main")
       .textContent = addon.editorialDescription;
     if (addon.editorialHeading) {
       card.querySelector(".disco-description-intro").textContent =
         addon.editorialHeading;
     }
 
-    // TODO: Append ratings and user count to description.
+    let hasStats = false;
+    if (addon.averageRating) {
+      hasStats = true;
+      card.querySelector("five-star-rating").rating = addon.averageRating;
+    } else {
+      card.querySelector("five-star-rating").hidden = true;
+    }
+
+    if (addon.dailyUsers) {
+      hasStats = true;
+      let userCountElem = card.querySelector(".disco-user-count");
+      document.l10n.setAttributes(userCountElem, "user-count", {
+        dailyUsers: addon.dailyUsers,
+      });
+    }
+
+    card.querySelector(".disco-description-statistics").hidden = !hasStats;
   }
 
   registerButtons(card, addon) {
     let installButton = card.querySelector("[action='install-addon']");
     if (addon.type == "theme") {
       document.l10n.setAttributes(installButton, "install-theme-button");
     } else {
       document.l10n.setAttributes(installButton, "install-extension-button");
--- a/toolkit/mozapps/extensions/content/rating-star.css
+++ b/toolkit/mozapps/extensions/content/rating-star.css
@@ -3,16 +3,20 @@
   --rating-star-spacing: 0.3ch;
 
   display: inline-grid;
   grid-template-columns: repeat(5, var(--rating-star-size));
   grid-column-gap: var(--rating-star-spacing);
   align-content: center;
 }
 
+:host([hidden]) {
+  display: none;
+}
+
 .rating-star {
   display: inline-block;
   width: var(--rating-star-size);
   height: var(--rating-star-size);
   background-image: url("chrome://mozapps/skin/extensions/rating-star.svg#empty");
   background-position: center;
   background-repeat: no-repeat;
   background-size: 100%;
--- a/toolkit/mozapps/extensions/test/browser/browser_html_discover_view.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_discover_view.js
@@ -39,16 +39,18 @@ amoServer.registerFile("/png",
 // stored in API_RESPONSE_FILE.
 function getTestExpectationFromApiResult(result) {
   return {
     typeIsTheme: result.addon.type === "statictheme",
     addonName: result.addon.name,
     authorName: result.addon.authors[0].name,
     editorialHead: result.heading_text,
     editorialBody: result.description_text,
+    dailyUsers: result.addon.average_daily_users,
+    rating: result.addon.ratings.average,
   };
 }
 
 // Read the content of API_RESPONSE_FILE, and replaces any embedded URLs with
 // URLs that point to the `amoServer` test server.
 async function readAPIResponseFixture() {
   let apiText = await OS.File.read(API_RESPONSE_FILE, {encoding: "utf-8"});
   apiText = apiText.replace(/\bhttps?:\/\/[^"]+(?=")/g, (url) => {
@@ -334,16 +336,34 @@ add_task(async function discopane_with_r
       ok(card.querySelector(".card-heading-image").offsetWidth,
          "Preview image must be visible");
     } else {
       // Extension button + extended description.
       ok(installButton.matches("[data-l10n-id='install-extension-button'"),
          "Has extension install button");
       checkContent(".disco-description-intro", expectations.editorialHead);
       checkContent(".disco-description-main", expectations.editorialBody);
+
+      let ratingElem = card.querySelector("five-star-rating");
+      if (expectations.rating) {
+        is(ratingElem.rating, expectations.rating, "Expected rating value");
+        ok(ratingElem.offsetWidth, "Rating element is visible");
+      } else {
+        is(ratingElem.offsetWidth, 0, "Rating element is not visible");
+      }
+
+      let userCountElem = card.querySelector(".disco-user-count");
+      if (expectations.dailyUsers) {
+        Assert.deepEqual(
+          win.document.l10n.getAttributes(userCountElem),
+          {id: "user-count", args: {dailyUsers: expectations.dailyUsers}},
+          "Card count should be rendered");
+      } else {
+        is(userCountElem.offsetWidth, 0, "User count element is not visible");
+      }
     }
   }
 
   is(apiHandler.requestCount, 1, "Discovery API should be fetched once");
 
   await closeView(win);
 });