Bug 1350233 - Add [learn more] MDN link for statistics panel draft
authorFred Lin <gasolin@mozilla.com>
Tue, 11 Apr 2017 10:59:38 +0800
changeset 561072 f5dceb91c91608cbe9e9f1653034e15c300c1016
parent 560035 b5b5dbed1c409d96aa6b97f2036cd66312fc45ad
child 623872 7e4cd49efd709b36331d1c28672577fd809fab6e
push id53619
push userbmo:gasolin@mozilla.com
push dateWed, 12 Apr 2017 06:29:40 +0000
bugs1350233
milestone55.0a1
Bug 1350233 - Add [learn more] MDN link for statistics panel MozReview-Commit-ID: BeRrGFD7c1D
devtools/client/netmonitor/src/assets/styles/netmonitor.css
devtools/client/netmonitor/src/components/request-list-empty-notice.js
devtools/client/netmonitor/src/components/statistics-panel.js
devtools/client/netmonitor/src/utils/mdn-utils.js
--- a/devtools/client/netmonitor/src/assets/styles/netmonitor.css
+++ b/devtools/client/netmonitor/src/assets/styles/netmonitor.css
@@ -93,16 +93,27 @@ body,
   overflow: auto;
 }
 
 .cropped-textbox .textbox-input {
   /* workaround for textbox not supporting the @crop attribute */
   text-overflow: ellipsis;
 }
 
+.learn-more-link {
+  color: var(--theme-highlight-blue);
+  cursor: pointer;
+  margin: 0 5px;
+  white-space: nowrap;
+}
+
+.learn-more-link:hover {
+  text-decoration: underline;
+}
+
 /* Status bar */
 
 .status-bar-label {
   display: inline-flex;
   align-content: stretch;
   margin-inline-end: 10px;
 
   /* Status bar has just one line so, don't wrap labels */
@@ -142,16 +153,18 @@ body,
   padding: 12px;
   font-size: 120%;
   flex: 1;
   overflow: auto;
 }
 
 .notice-perf-message {
   margin-top: 2px;
+  display: flex;
+  align-items: center;
 }
 
 .requests-list-perf-notice-button {
   min-width: 30px;
   min-height: 26px;
   margin: 0 5px;
   vertical-align: middle;
 }
@@ -855,28 +868,16 @@ body,
   color: inherit;
   padding-inline-start: 3px;
 }
 
 .theme-dark .tabpanel-summary-value {
   color: var(--theme-selection-color);
 }
 
-.learn-more-link {
-  color: var(--theme-highlight-blue);
-  cursor: pointer;
-  margin: 0 5px;
-  white-space: nowrap;
-  flex-grow: 1;
-}
-
-.learn-more-link:hover {
-  text-decoration: underline;
-}
-
 /* Headers tabpanel */
 
 .headers-overview {
   background: var(--theme-toolbar-background);
 }
 
 .headers-summary,
 .response-summary {
@@ -917,16 +918,20 @@ body,
 .headers-summary .textbox-input {
   margin-inline-end: 2px;
 }
 
 .headers-summary .status-text {
     width: auto!important;
 }
 
+.headers-summary .learn-more-link {
+  flex-grow: 1;
+}
+
 /* Response tabpanel */
 
 .response-error-header {
   margin: 0;
   padding: 3px 8px;
   background-color: var(--theme-highlight-red);
   color: var(--theme-selection-color);
 }
@@ -1150,16 +1155,24 @@ body,
 }
 
 .statistics-panel .charts,
 .statistics-panel .pie-table-chart-container {
   width: 100%;
   height: 100%;
 }
 
+.statistics-panel .learn-more-link {
+  font-weight: 400;
+}
+
+.statistics-panel .table-chart-title {
+  display: flex;
+}
+
 .pie-table-chart-container {
   display: flex;
   justify-content: center;
   align-items: center;
 }
 
 .statistics-panel .pie-chart-container {
   margin-inline-start: 3vw;
--- a/devtools/client/netmonitor/src/components/request-list-empty-notice.js
+++ b/devtools/client/netmonitor/src/components/request-list-empty-notice.js
@@ -1,24 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
   createClass,
+  createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const Actions = require("../actions/index");
 const { ACTIVITY_TYPE } = require("../constants");
 const { NetMonitorController } = require("../netmonitor-controller");
 const { L10N } = require("../utils/l10n");
+const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
+
+// Components
+const MDNLink = createFactory(require("./mdn-link"));
 
 const { button, div, span } = DOM;
 
 /**
  * UI displayed when the request list is empty. Contains instructions on reloading
  * the page and on triggering performance analysis of the page.
  */
 const RequestListEmptyNotice = createClass({
@@ -49,17 +54,18 @@ const RequestListEmptyNotice = createCla
       div({ className: "notice-perf-message" },
         span(null, L10N.getStr("netmonitor.perfNotice1")),
         button({
           title: L10N.getStr("netmonitor.perfNotice3"),
           className: "devtools-button requests-list-perf-notice-button",
           "data-standalone": true,
           onClick: this.props.onPerfClick,
         }),
-        span(null, L10N.getStr("netmonitor.perfNotice2"))
+        span(null, L10N.getStr("netmonitor.perfNotice2")),
+        MDNLink({ url: getPerformanceAnalysisURL() })
       )
     );
   }
 });
 
 module.exports = connect(
   undefined,
   dispatch => ({
--- a/devtools/client/netmonitor/src/components/statistics-panel.js
+++ b/devtools/client/netmonitor/src/components/statistics-panel.js
@@ -1,29 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const {
   createClass,
+  createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { Chart } = require("devtools/client/shared/widgets/Chart");
 const { PluralForm } = require("devtools/shared/plural-form");
 const Actions = require("../actions/index");
 const { Filters } = require("../utils/filter-predicates");
 const {
   getSizeWithDecimals,
   getTimeWithDecimals
 } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
+const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
+
+// Components
+const MDNLink = createFactory(require("./mdn-link"));
 
 const { button, div } = DOM;
 const MediaQueryList = window.matchMedia("(min-width: 700px)");
 
 const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
 const BACK_BUTTON = L10N.getStr("netmonitor.backButton");
 const CHARTS_CACHE_ENABLED = L10N.getStr("charts.cacheEnabled");
 const CHARTS_CACHE_DISABLED = L10N.getStr("charts.cacheDisabled");
@@ -43,16 +49,20 @@ const StatisticsPanel = createClass({
   },
 
   getInitialState() {
     return {
       isVerticalSpliter: MediaQueryList.matches,
     };
   },
 
+  componentWillMount() {
+    this.mdnLinkContainerNodes = new Map();
+  },
+
   componentDidUpdate(prevProps) {
     MediaQueryList.addListener(this.onLayoutChange);
 
     const { requests } = this.props;
     let ready = requests && !requests.isEmpty() && requests.every((req) =>
       req.contentSize !== undefined && req.mimeType && req.responseHeaders &&
       req.status !== undefined && req.totalTime !== undefined
     );
@@ -63,20 +73,45 @@ const StatisticsPanel = createClass({
       data: ready ? this.sanitizeChartDataSource(requests, false) : null,
     });
 
     this.createChart({
       id: "emptyCacheChart",
       title: CHARTS_CACHE_DISABLED,
       data: ready ? this.sanitizeChartDataSource(requests, true) : null,
     });
+
+    this.createMDNLink("primedCacheChart", getPerformanceAnalysisURL());
+    this.createMDNLink("emptyCacheChart", getPerformanceAnalysisURL());
   },
 
   componentWillUnmount() {
     MediaQueryList.removeListener(this.onLayoutChange);
+    this.unmountMDNLinkContainers();
+  },
+
+  createMDNLink(chartId, url) {
+    if (this.mdnLinkContainerNodes.has(chartId)) {
+      ReactDOM.unmountComponentAtNode(this.mdnLinkContainerNodes.get(chartId));
+    }
+
+    // MDNLink is a React component but Chart isn't.  To get the link
+    // into the chart we mount a new ReactDOM at the appropriate
+    // location after the chart has been created.
+    let title = this.refs[chartId].querySelector(".table-chart-title");
+    let containerNode = document.createElement("span");
+    title.appendChild(containerNode);
+    ReactDOM.render(MDNLink({ url }), containerNode);
+    this.mdnLinkContainerNodes.set(chartId, containerNode);
+  },
+
+  unmountMDNLinkContainers() {
+    for (let [, node] of this.mdnLinkContainerNodes) {
+      ReactDOM.unmountComponentAtNode(node);
+    }
   },
 
   createChart({ id, title, data }) {
     // Create a new chart.
     let chart = Chart.PieTable(document, {
       diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
       title,
       header: {
--- a/devtools/client/netmonitor/src/utils/mdn-utils.js
+++ b/devtools/client/netmonitor/src/utils/mdn-utils.js
@@ -135,55 +135,64 @@ const SUPPORTED_HTTP_CODES = [
     "501",
     "502",
     "503",
     "504",
     "505",
     "511"
 ];
 
+const MDN_URL = "https://developer.mozilla.org/docs/";
 const GA_PARAMS =
   "?utm_source=mozilla&utm_medium=devtools-netmonitor&utm_campaign=default";
 
-const NETWORK_MONITOR_TIMINGS_MDN_URL =
-  "https://developer.mozilla.org/docs/Tools/Network_Monitor#Timings";
-
 /**
  * Get the MDN URL for the specified header.
  *
  * @param {string} header Name of the header for the baseURL to use.
  *
  * @return {string} The MDN URL for the header, or null if not available.
  */
 function getHeadersURL(header) {
   const lowerCaseHeader = header.toLowerCase();
   let idx = SUPPORTED_HEADERS.findIndex(item =>
     item.toLowerCase() === lowerCaseHeader);
   return idx > -1 ?
-    `https://developer.mozilla.org/docs/Web/HTTP/Headers/${SUPPORTED_HEADERS[idx] + GA_PARAMS}` : null;
+    `${MDN_URL}Web/HTTP/Headers/${SUPPORTED_HEADERS[idx] + GA_PARAMS}` : null;
 }
 
 /**
  * Get the MDN URL for the specified HTTP status code.
  *
  * @param {string} HTTP status code for the baseURL to use.
  *
  * @return {string} The MDN URL for the HTTP status code, or null if not available.
  */
 function getHTTPStatusCodeURL(statusCode) {
   let idx = SUPPORTED_HTTP_CODES.indexOf(statusCode);
-  return idx > -1 ? `https://developer.mozilla.org/docs/Web/HTTP/Status/${SUPPORTED_HTTP_CODES[idx] + GA_PARAMS}` : null;
+  return idx > -1 ?
+    `${MDN_URL}Web/HTTP/Status/${SUPPORTED_HTTP_CODES[idx] + GA_PARAMS}` : null;
 }
 
 /**
  * Get the MDN URL of the Timings tag for Network Monitor.
  *
  * @return {string} the MDN URL of the Timings tag for Network Monitor.
  */
 function getNetMonitorTimingsURL() {
-  return NETWORK_MONITOR_TIMINGS_MDN_URL;
+  return `${MDN_URL}Tools/Network_Monitor${GA_PARAMS}#Timings`;
+}
+
+/**
+ * Get the MDN URL for Performance Analysis
+ *
+ * @return {string} The MDN URL for the documentation of Performance Analysis.
+ */
+function getPerformanceAnalysisURL() {
+  return `${MDN_URL}Tools/Network_Monitor${GA_PARAMS}#Performance_analysis`;
 }
 
 module.exports = {
   getHeadersURL,
   getHTTPStatusCodeURL,
   getNetMonitorTimingsURL,
+  getPerformanceAnalysisURL,
 };