Bug 1393478 - Display when no results are available in about:telemetry r?chutten draft
authorflyingrub <flyinggrub@gmail.com>
Tue, 29 Aug 2017 11:32:26 +0200
changeset 655359 008d2a60ecc49148f38ce4bcebbbe582f3f34687
parent 655358 9166a4e5010dca96a7da90fdff5fc5fb865346e3
child 655360 cc327b369db87f1b63f47cc6b318e3a649dd2ea7
push id76850
push userbmo:flyinggrub@gmail.com
push dateTue, 29 Aug 2017 22:37:30 +0000
reviewerschutten
bugs1393478
milestone57.0a1
Bug 1393478 - Display when no results are available in about:telemetry r?chutten Also keep the search state when changing ping. MozReview-Commit-ID: 9M2hz7ttl8e
toolkit/content/aboutTelemetry.css
toolkit/content/aboutTelemetry.js
toolkit/content/aboutTelemetry.xhtml
toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
--- a/toolkit/content/aboutTelemetry.css
+++ b/toolkit/content/aboutTelemetry.css
@@ -148,16 +148,37 @@ section:not(.active) {
 }
 
 #ping-explanation > span {
   cursor: pointer;
   border-bottom-width: 2px;
   border-bottom-style: solid;
 }
 
+#no-search-results {
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+}
+
+#no-search-results-text {
+  font-size: 17px;
+  margin-bottom: 2em;
+}
+
+.no-search-results-image {
+  background-image: url("chrome://browser/skin/preferences/in-content-new/no-search-results.svg");
+  width: 380px;
+  height: 293px;
+}
+
 .hidden {
   display: none !important;
 }
 
 #ping-picker {
   min-width: 300px;
   position: fixed;
   z-index: 2;
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -1365,108 +1365,141 @@ var Search = {
         allElementHidden = false;
       }
     }
     return allElementHidden;
   },
 
   filterKeyedElements(keyedElements, filterText) {
     let [isPassFunc, filter] = this.chooseFilter(filterText);
+    let allElementsHidden = true;
 
     let needLowerCase = (isPassFunc === this.isPassText);
     keyedElements.forEach((keyedElement) => {
       let subject = needLowerCase ? keyedElement.key.id.toLowerCase() : keyedElement.key.id;
       if (!isPassFunc(subject, filter)) { // If the keyedHistogram's name is not matched
-        let allElementHidden = true;
+        let allKeyedElementsHidden = true;
         for (let element of keyedElement.datas) {
           let subject = needLowerCase ? element.id.toLowerCase() : element.id;
           let match = isPassFunc(subject, filter);
           element.hidden = !match;
           if (match) {
-            allElementHidden = false;
+            allKeyedElementsHidden = false;
           }
         }
-        keyedElement.key.hidden = allElementHidden;
+        if (allElementsHidden && !allKeyedElementsHidden) {
+          allElementsHidden = false;
+        }
+        keyedElement.key.hidden = allKeyedElementsHidden;
       } else { // If the keyedHistogram's name is matched
+        allElementsHidden = false;
         keyedElement.key.hidden = false;
         for (let element of keyedElement.datas) {
           element.hidden = false;
         }
       }
     });
+    return allElementsHidden;
   },
 
   searchHandler(e) {
     if (this.idleTimeout) {
       clearTimeout(this.idleTimeout);
     }
     this.idleTimeout = setTimeout(() => Search.search(e.target.value), FILTER_IDLE_TIMEOUT);
   },
 
-  search(text, section = null) {
+  search(text, sectionParam = null) {
+    let section = sectionParam;
     if (!section) {
       let sectionId = document.querySelector(".category.selected").getAttribute("value");
       section = document.getElementById(sectionId);
     }
+    let noSearchResults = true;
     if (section.id === "home-section") {
-      this.homeSearch(text);
+      return this.homeSearch(text);
     } else if (section.id === "histograms-section") {
       let histograms = section.getElementsByClassName("histogram");
-      this.filterElements(histograms, text);
+      noSearchResults = this.filterElements(histograms, text);
     } else if (section.id === "keyed-histograms-section") {
       let keyedElements = [];
       let keyedHistograms = section.getElementsByClassName("keyed-histogram");
       for (let key of keyedHistograms) {
         let datas = key.getElementsByClassName("histogram");
         keyedElements.push({key, datas});
       }
-      this.filterKeyedElements(keyedElements, text);
+      noSearchResults = this.filterKeyedElements(keyedElements, text);
     } else if (section.id === "keyed-scalars-section") {
       let keyedElements = [];
       let keyedScalars = section.getElementsByClassName("keyed-scalar");
       for (let key of keyedScalars) {
         let datas = key.querySelector("table").rows;
         keyedElements.push({key, datas});
       }
-      this.filterKeyedElements(keyedElements, text);
+      noSearchResults = this.filterKeyedElements(keyedElements, text);
     } else {
       let tables = section.querySelectorAll("table");
       for (let table of tables) {
-        let allElementsHidden = this.filterElements(table.rows, text);
+        noSearchResults = this.filterElements(table.rows, text);
         if (table.caption) {
-          table.caption.hidden = allElementsHidden;
+          table.caption.hidden = noSearchResults;
         }
       }
     }
+
+    if (!sectionParam) { // If we are not searching in all section.
+      this.updateNoResults(text, noSearchResults);
+    }
+    return noSearchResults;
+  },
+
+  updateNoResults(text, noSearchResults) {
+    document.getElementById("no-search-results").classList.toggle("hidden", !noSearchResults);
+    if (noSearchResults) {
+      let section = document.querySelector(".category.selected > span");
+      let selectedTitle = section.textContent.trim();
+      if (section.parentElement.id === "category-home") {
+        selectedTitle = bundle.GetStringFromName("allSections");
+      }
+      let format = [selectedTitle, text];
+      let searchStatus = bundle.formatStringFromName("noSearchResults", format, 2);
+      document.getElementById("no-search-results-text").textContent = searchStatus;
+    }
   },
 
   resetHome() {
     document.getElementById("main").classList.remove("search");
+    document.getElementById("no-search-results").classList.add("hidden");
     adjustHeaderState();
     Array.from(document.querySelectorAll("section")).forEach((section) => {
       section.classList.toggle("active", section.id == "home-section");
     });
   },
 
   homeSearch(text) {
     if (text === "") {
       this.resetHome();
       return;
     }
     document.getElementById("main").classList.add("search");
     let title = bundle.formatStringFromName("resultsForSearch", [text], 1);
     adjustHeaderState(title);
+    let noSearchResults = true;
     Array.from(document.querySelectorAll("section")).forEach((section) => {
       if (section.id == "home-section" || section.id == "raw-payload-section") {
         section.classList.remove("active");
         return;
       }
       section.classList.add("active");
-      this.search(text, section);
+      let sectionHidden = this.search(text, section);
+      if (noSearchResults && !sectionHidden) {
+        noSearchResults = false;
+      }
     });
+    this.updateNoResults(text, noSearchResults);
   }
 }
 
 /*
  * Helper function to render JS objects with white space between top level elements
  * so that they look better in the browser
  * @param   aObject JavaScript object or array to render
  * @return  String
@@ -1852,16 +1885,17 @@ function refreshSearch() {
     Search.search(search.value);
   }
 }
 
 function adjustSearchState() {
   let selectedSection = document.querySelector(".category.selected").getAttribute("value");
   let search = document.getElementById("search");
   search.hidden = Search.blacklist.includes(selectedSection);
+  document.getElementById("no-search-results").classList.add("hidden");
   Search.search(""); // reinitialize search state.
 }
 
 function adjustSection() {
   let selectedCategory = document.querySelector(".category.selected");
   if (!selectedCategory.classList.contains("has-data")) {
     PingPicker._showStructuredPingData();
   }
--- a/toolkit/content/aboutTelemetry.xhtml
+++ b/toolkit/content/aboutTelemetry.xhtml
@@ -133,16 +133,21 @@
       <div class="header">
           <div id="sectionTitle" class="header-name">
               &aboutTelemetry.pageTitle;
           </div>
           <input type="text" id="search" placeholder=""/>
           <select id="processes" hidden="true"></select>
       </div>
 
+      <div id="no-search-results" hidden="true">
+        <span id="no-search-results-text"></span>
+        <div class="no-search-results-image"></div>
+      </div>
+
       <section id="home-section" class="active">
         <h3 id="page-subtitle"></h3>
         <p id="home-explanation"></p>
         <p id="ping-explanation"></p>
       </section>
 
       <section id="raw-payload-section">
         <button id="payload-json-viewer">&aboutTelemetry.showInFirefoxJsonViewer;</button>
--- a/toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
@@ -41,16 +41,21 @@ currentPing = current
 
 # Used as a tooltip for the "current" ping title in the sidebar
 currentPingSidebar = current ping
 
 # Note to translators:
 # - %1$S will be replaced by the current text in the search input
 resultsForSearch = Results for ā€œ%1$Sā€
 
+# Note to translators:
+# - %1$S will be replaced by the section name from the structure of the ping. More info about it can be found here : http://gecko.readthedocs.io/en/latest/toolkit/components/telemetry/telemetry/data/main-ping.html
+# - %2$S will be replaced by the current text in the search input
+noSearchResults = Sorry! There are no results in %1$S for ā€œ%2$Sā€
+
 telemetryPingTypeAll = all
 
 telemetryLogTitle = Telemetry Log
 
 telemetryLogHeadingId = Id
 
 telemetryLogHeadingTimestamp = Timestamp