Bug 1467572 - Part 18: Show an onboarding tooltip for the setting menu button in RDM. r=jdescottes draft
authorGabriel Luong <gabriel.luong@gmail.com>
Tue, 14 Aug 2018 17:54:55 -0400 (2018-08-14)
changeset 829223 51fedae0567e4fe24765681ab881bb76fa3315fc
parent 829222 1821bdf87275d6f4ada7dcf5f56d5e02b9baef59
child 829224 0ab72fa3cf05ef9719b1fd61f678ed8ac0bee616
push id118750
push userbmo:gl@mozilla.com
push dateTue, 14 Aug 2018 21:55:43 +0000 (2018-08-14)
reviewersjdescottes
bugs1467572, 12408235
milestone63.0a1
Bug 1467572 - Part 18: Show an onboarding tooltip for the setting menu button in RDM. r=jdescottes Design https://mozilla.invisionapp.com/d/main#/console/12408235/300479943/preview MozReview-Commit-ID: K1vIjMc685u
devtools/client/inspector/shared/three-pane-onboarding-tooltip.js
devtools/client/locales/en-US/responsive.properties
devtools/client/preferences/devtools-client.js
devtools/client/responsive.html/manager.js
devtools/client/responsive.html/moz.build
devtools/client/responsive.html/setting-onboarding-tooltip.js
devtools/client/responsive.html/test/browser/head.js
devtools/client/themes/tooltips.css
--- a/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js
+++ b/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js
@@ -29,45 +29,45 @@ class ThreePaneOnboardingTooltip {
       type: "arrow",
       useXulWrapper: true,
     });
 
     this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
     this.onLearnMoreLinkClick = this.onLearnMoreLinkClick.bind(this);
 
     const container = doc.createElementNS(XHTML_NS, "div");
-    container.className = "three-pane-onboarding-container";
+    container.className = "onboarding-container";
 
     const icon = doc.createElementNS(XHTML_NS, "span");
-    icon.className = "three-pane-onboarding-icon";
+    icon.className = "onboarding-icon";
     container.appendChild(icon);
 
     const content = doc.createElementNS(XHTML_NS, "div");
-    content.className = "three-pane-onboarding-content";
+    content.className = "onboarding-content";
     container.appendChild(content);
 
     const message = doc.createElementNS(XHTML_NS, "div");
     const learnMoreString = L10N.getStr("inspector.threePaneOnboarding.learnMoreLink");
     const messageString = L10N.getFormatStr("inspector.threePaneOnboarding.content",
       learnMoreString);
     const learnMoreStartIndex = messageString.indexOf(learnMoreString);
 
     message.append(messageString.substring(0, learnMoreStartIndex));
 
     this.learnMoreLink = doc.createElementNS(XHTML_NS, "a");
-    this.learnMoreLink.className = "three-pane-onboarding-link";
+    this.learnMoreLink.className = "onboarding-link";
     this.learnMoreLink.href = "#";
     this.learnMoreLink.textContent = learnMoreString;
 
     message.append(this.learnMoreLink);
     message.append(messageString.substring(learnMoreStartIndex + learnMoreString.length));
     content.append(message);
 
     this.closeButton = doc.createElementNS(XHTML_NS, "button");
-    this.closeButton.className = "three-pane-onboarding-close-button devtools-button";
+    this.closeButton.className = "onboarding-close-button devtools-button";
     container.appendChild(this.closeButton);
 
     this.closeButton.addEventListener("click", this.onCloseButtonClick);
     this.learnMoreLink.addEventListener("click", this.onLearnMoreLinkClick);
 
     this.tooltip.setContent(container, { width: CONTAINER_WIDTH });
     this.tooltip.show(this.doc.querySelector("#inspector-sidebar .sidebar-toggle"), {
       position: "top",
--- a/devtools/client/locales/en-US/responsive.properties
+++ b/devtools/client/locales/en-US/responsive.properties
@@ -127,8 +127,13 @@ responsive.reloadConditions.userAgent=Re
 # LOCALIZATION NOTE (responsive.reloadNotification.description): Text in notification bar
 # shown on first open to clarify that some features need a reload to apply.  %1$S is the
 # label on the reload conditions menu (responsive.reloadConditions.label).
 responsive.reloadNotification.description=Device simulation changes require a reload to fully apply.  Automatic reloads are disabled by default to avoid losing any changes in DevTools.  You can enable reloading via the ā€œ%1$Sā€ menu.
 
 # LOCALIZATION NOTE (responsive.leftAlignViewport): Label on checkbox used in the settings
 # menu.
 responsive.leftAlignViewport = Left-align Viewport
+
+# LOCALIZATION NOTE (responsive.settingOnboarding.content): This is the content shown in
+# the setting onboarding tooltip that is displayed below the settings menu button in
+# Responsive Design Mode.
+responsive.settingOnboarding.content=New: Change to left-alignment or edit reload behavior here.
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -313,16 +313,22 @@ pref("devtools.editor.autocomplete", tru
 // Whether or not the viewports are left aligned.
 pref("devtools.responsive.leftAlignViewport.enabled", false);
 // Whether to reload when touch simulation is toggled
 pref("devtools.responsive.reloadConditions.touchSimulation", false);
 // Whether to reload when user agent is changed
 pref("devtools.responsive.reloadConditions.userAgent", false);
 // Whether to show the notification about reloading to apply emulation
 pref("devtools.responsive.reloadNotification.enabled", true);
+// Whether to show the settings onboarding tooltip only in release or beta builds.
+#if defined(RELEASE_OR_BETA)
+pref("devtools.responsive.show-setting-tooltip", true);
+#else
+pref("devtools.responsive.show-setting-tooltip", false);
+#endif
 
 // Enable new about:debugging.
 pref("devtools.aboutdebugging.new-enabled", false);
 pref("devtools.aboutdebugging.network-locations", "[]");
 
 // about:debugging: only show system add-ons in local builds by default.
 #ifdef MOZILLA_OFFICIAL
   pref("devtools.aboutdebugging.showSystemAddons", false);
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -9,29 +9,31 @@ const promise = require("promise");
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
 
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "throttlingProfiles", "devtools/client/shared/components/throttling/profiles");
+loader.lazyRequireGetter(this, "SettingOnboardingTooltip", "devtools/client/responsive.html/setting-onboarding-tooltip");
 loader.lazyRequireGetter(this, "swapToInnerBrowser", "devtools/client/responsive.html/browser/swap", true);
 loader.lazyRequireGetter(this, "startup", "devtools/client/responsive.html/utils/window", true);
 loader.lazyRequireGetter(this, "message", "devtools/client/responsive.html/utils/message");
 loader.lazyRequireGetter(this, "showNotification", "devtools/client/responsive.html/utils/notification", true);
 loader.lazyRequireGetter(this, "l10n", "devtools/client/responsive.html/utils/l10n");
 loader.lazyRequireGetter(this, "EmulationFront", "devtools/shared/fronts/emulation", true);
 loader.lazyRequireGetter(this, "PriorityLevels", "devtools/client/shared/components/NotificationBox", true);
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
 
 const RELOAD_CONDITION_PREF_PREFIX = "devtools.responsive.reloadConditions.";
 const RELOAD_NOTIFICATION_PREF = "devtools.responsive.reloadNotification.enabled";
+const SHOW_SETTING_TOOLTIP_PREF = "devtools.responsive.show-setting-tooltip";
 
 function debug(msg) {
   // console.log(`RDM manager: ${msg}`);
 }
 
 /**
  * ResponsiveUIManager is the external API for the browser UI, etc. to use when
  * opening and closing the responsive UI.
@@ -372,16 +374,22 @@ ResponsiveUI.prototype = {
     // Notify the inner browser to start the frame script
     debug("Wait until start frame script");
     await message.request(this.toolWindow, "start-frame-script");
 
     // Get the protocol ready to speak with emulation actor
     debug("Wait until RDP server connect");
     await this.connectToServer();
 
+    // Show the settings onboarding tooltip
+    if (Services.prefs.getBoolPref(SHOW_SETTING_TOOLTIP_PREF)) {
+      this.settingOnboardingTooltip =
+        new SettingOnboardingTooltip(ui.toolWindow.document);
+    }
+
     // Non-blocking message to tool UI to start any delayed init activities
     message.post(this.toolWindow, "post-init");
 
     debug("Init done");
   },
 
   /**
    * Close RDM and restore page content back into a regular tab.
@@ -431,16 +439,21 @@ ResponsiveUI.prototype = {
                       this.reloadOnChange("userAgent");
       reloadNeeded |= await this.updateTouchSimulation() &&
                       this.reloadOnChange("touchSimulation");
       if (reloadNeeded) {
         this.getViewportBrowser().reload();
       }
     }
 
+    if (this.settingOnboardingTooltip) {
+      this.settingOnboardingTooltip.destroy();
+      this.settingOnboardingTooltip = null;
+    }
+
     // Destroy local state
     const swap = this.swap;
     this.browserWindow = null;
     this.tab = null;
     this.inited = null;
     this.toolWindow = null;
     this.swap = null;
 
--- a/devtools/client/responsive.html/moz.build
+++ b/devtools/client/responsive.html/moz.build
@@ -16,16 +16,17 @@ DIRS += [
 DevToolsModules(
     'commands.js',
     'constants.js',
     'index.css',
     'index.js',
     'manager.js',
     'reducers.js',
     'responsive-ua.css',
+    'setting-onboarding-tooltip.js',
     'store.js',
     'types.js',
 )
 
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
 
 with Files('**'):
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/setting-onboarding-tooltip.js
@@ -0,0 +1,71 @@
+/* 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 Services = require("Services");
+const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+
+const { getStr } = require("./utils/l10n");
+
+const SHOW_SETTING_TOOLTIP_PREF = "devtools.responsive.show-setting-tooltip";
+
+const CONTAINER_WIDTH = 270;
+
+/**
+ * Setting onboarding tooltip that is shown on the setting menu button in the RDM toolbar
+ * when the pref is on.
+ */
+class SettingOnboardingTooltip {
+  constructor(doc) {
+    this.doc = doc;
+    this.tooltip = new HTMLTooltip(this.doc, { type: "arrow" });
+
+    this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
+
+    const container = doc.createElement("div");
+    container.className = "onboarding-container";
+
+    const icon = doc.createElement("span");
+    icon.className = "onboarding-icon";
+    container.appendChild(icon);
+
+    const content = doc.createElement("div");
+    content.className = "onboarding-content";
+    content.textContent = getStr("responsive.settingOnboarding.content");
+    container.appendChild(content);
+
+    this.closeButton = doc.createElement("button");
+    this.closeButton.className = "onboarding-close-button devtools-button";
+    container.appendChild(this.closeButton);
+
+    this.closeButton.addEventListener("click", this.onCloseButtonClick);
+
+    this.tooltip.setContent(container, { width: CONTAINER_WIDTH });
+    this.tooltip.show(this.doc.getElementById("settings-button"), {
+      position: "bottom",
+    });
+  }
+
+  destroy() {
+    this.closeButton.removeEventListener("click", this.onCloseButtonClick);
+
+    this.tooltip.destroy();
+
+    this.closeButton = null;
+    this.doc = null;
+    this.tooltip = null;
+  }
+
+  /**
+   * Handler for the "click" event on the close button. Hides the onboarding tooltip
+   * and sets the show three pane onboarding tooltip pref to false.
+   */
+  onCloseButtonClick() {
+    Services.prefs.setBoolPref(SHOW_SETTING_TOOLTIP_PREF, false);
+    this.tooltip.hide();
+  }
+}
+
+module.exports = SettingOnboardingTooltip;
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -50,23 +50,26 @@ SimpleTest.waitForExplicitFinish();
 // on debug builds. Usually we are just barely over the limit, so a blanket factor of 2
 // should be enough.
 requestLongerTimeout(2);
 
 Services.prefs.setCharPref("devtools.devices.url", TEST_URI_ROOT + "devices.json");
 // The appearance of this notification causes intermittent behavior in some tests that
 // send mouse events, since it causes the content to shift when it appears.
 Services.prefs.setBoolPref("devtools.responsive.reloadNotification.enabled", false);
+// Don't show the setting onboarding tooltip in the test suites.
+Services.prefs.setBoolPref("devtools.responsive.show-setting-tooltip", false);
 
 registerCleanupFunction(async () => {
   Services.prefs.clearUserPref("devtools.devices.url");
   Services.prefs.clearUserPref("devtools.responsive.reloadNotification.enabled");
   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.touchSimulation");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.userAgent");
+  Services.prefs.clearUserPref("devtools.responsive.show-setting-tooltip");
   await asyncStorage.removeItem("devtools.devices.url_cache");
   await removeLocalDevices();
 });
 
 /**
  * Open responsive design mode for the given tab.
  */
 var openRDM = async function(tab) {
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -558,57 +558,57 @@
 /* Tooltip: Image tooltip */
 
 .devtools-tooltip-image-broken {
   box-sizing: border-box;
   height: 100%;
   padding: 7px;
 }
 
-/* Tooltip: 3 Pane Inspecot Onboarding Tooltip */
+/* Tooltip: Onboarding Tooltip */
 
-.three-pane-onboarding-container {
+.onboarding-container {
   align-items: center;
   background-color: var(--theme-toolbar-background);
   box-sizing: border-box;
   color: var(--theme-body-color);
   display: flex;
   font-size: 12px;
   padding: 7px;
   width: 100%;
   -moz-user-select: none;
 }
 
-.three-pane-onboarding-icon {
+.onboarding-icon {
   display: inline-block;
   background-size: 21px;
   width: 21px;
   height: 21px;
   margin: 8px;
   background-image: url("chrome://devtools/skin/images/fox-smiling.svg");
 }
 
-.three-pane-onboarding-content {
+.onboarding-content {
   flex: 1;
   padding-inline-start: 5px;
 }
 
-.three-pane-onboarding-link {
+.onboarding-link {
   color: var(--onboarding-link-color);
   cursor: pointer;
 }
 
-.three-pane-onboarding-link:hover {
+.onboarding-link:hover {
   text-decoration: underline;
 }
 
-.three-pane-onboarding-link:active {
+.onboarding-link:active {
   color: var(--onboarding-link-active-color);
 }
 
-.three-pane-onboarding-close-button {
+.onboarding-close-button {
   align-self: flex-start;
 }
 
-.three-pane-onboarding-close-button::before {
+.onboarding-close-button::before {
   background-image: url("chrome://devtools/skin/images/close.svg");
   margin: -6px 0 0 -6px;
 }