From 787fcb84ea138477471011d58474e6a52a0574e8 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Thu, 3 May 2018 13:55:54 -0400 Subject: [PATCH] Bug 1446944 - Provide onboarding tooltip for the 3 pane inspector feature. r=jdescottes, flod --- devtools/client/inspector/inspector.js | 35 +++--- devtools/client/inspector/shared/moz.build | 1 + .../shared/three-pane-onboarding-tooltip.js | 110 ++++++++++++++++++ devtools/client/jar.mn | 1 + .../client/locales/en-US/inspector.properties | 8 ++ .../client/preferences/devtools-client.js | 6 + devtools/client/shared/test/shared-head.js | 2 + devtools/client/themes/images/fox-smiling.svg | 37 ++++++ devtools/client/themes/tooltips.css | 54 +++++++++ devtools/client/themes/variables.css | 1 + 10 files changed, 240 insertions(+), 15 deletions(-) create mode 100644 devtools/client/inspector/shared/three-pane-onboarding-tooltip.js create mode 100644 devtools/client/themes/images/fox-smiling.svg diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js index c2871062f65f..b771eb821337 100644 --- a/devtools/client/inspector/inspector.js +++ b/devtools/client/inspector/inspector.js @@ -13,7 +13,6 @@ const promise = require("promise"); const EventEmitter = require("devtools/shared/event-emitter"); const {executeSoon} = require("devtools/shared/DevToolsUtils"); const {Toolbox} = require("devtools/client/framework/toolbox"); -const {PrefObserver} = require("devtools/client/shared/prefs"); const Telemetry = require("devtools/client/shared/telemetry"); const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay"); const ReflowTracker = require("devtools/client/inspector/shared/reflow-tracker"); @@ -26,6 +25,7 @@ const Promise = require("Promise"); loader.lazyRequireGetter(this, "initCssProperties", "devtools/shared/fronts/css-properties", true); loader.lazyRequireGetter(this, "HTMLBreadcrumbs", "devtools/client/inspector/breadcrumbs", true); +loader.lazyRequireGetter(this, "ThreePaneOnboardingTooltip", "devtools/client/inspector/shared/three-pane-onboarding-tooltip"); loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts"); loader.lazyRequireGetter(this, "InspectorSearch", "devtools/client/inspector/inspector-search", true); loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/inspector/toolsidebar", true); @@ -55,6 +55,7 @@ const PORTRAIT_MODE_WIDTH_THRESHOLD = 700; // mode. const SIDE_PORTAIT_MODE_WIDTH_THRESHOLD = 1000; +const SHOW_THREE_PANE_ONBOARDING_PREF = "devtools.inspector.show-three-pane-tooltip"; const SHOW_THREE_PANE_TOGGLE_PREF = "devtools.inspector.three-pane-toggle"; const THREE_PANE_ENABLED_PREF = "devtools.inspector.three-pane-enabled"; const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled"; @@ -111,7 +112,6 @@ function Inspector(toolbox) { this._panels = new Map(); this.highlighters = new HighlightersOverlay(this); - this.prefsObserver = new PrefObserver("devtools."); this.reflowTracker = new ReflowTracker(this._target); this.styleChangeTracker = new InspectorStyleChangeTracker(this); this.telemetry = new Telemetry(); @@ -120,8 +120,9 @@ function Inspector(toolbox) { // telemetry counts in the Grid Inspector are not double counted on reload. this.previousURL = this.target.url; - this.show3PaneToggle = Services.prefs.getBoolPref(SHOW_THREE_PANE_TOGGLE_PREF); this.is3PaneModeEnabled = Services.prefs.getBoolPref(THREE_PANE_ENABLED_PREF); + this.show3PaneToggle = Services.prefs.getBoolPref(SHOW_THREE_PANE_TOGGLE_PREF); + this.show3PaneTooltip = Services.prefs.getBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF); this.nodeMenuTriggerInfo = null; @@ -296,6 +297,10 @@ Inspector.prototype = { // Setup the toolbar only now because it may depend on the document. await this.setupToolbar(); + if (this.show3PaneTooltip) { + this.threePaneTooltip = new ThreePaneOnboardingTooltip(this.toolbox, this.panelDoc); + } + // Log the 3 pane inspector setting on inspector open. The question we want to answer // is: // "What proportion of users use the 3 pane vs 2 pane inspector on inspector open?" @@ -1343,6 +1348,9 @@ Inspector.prototype = { this.cancelUpdate(); + this.selection.off("new-node-front", this.onNewSelection); + this.selection.off("detached-front", this.onDetached); + this.sidebar.off("select", this.onSidebarSelect); this.target.off("will-navigate", this._onBeforeNavigate); this.target.off("thread-paused", this.updateDebuggerPausedWarning); this.target.off("thread-resumed", this.updateDebuggerPausedWarning); @@ -1365,25 +1373,21 @@ Inspector.prototype = { this.animationinspector.destroy(); } + if (this.threePaneTooltip) { + this.threePaneTooltip.destroy(); + } + let cssPropertiesDestroyer = this._cssProperties.front.destroy(); - - this.sidebar.off("select", this.onSidebarSelect); let sidebarDestroyer = this.sidebar.destroy(); - let ruleViewSideBarDestroyer = this.ruleViewSideBar ? this.ruleViewSideBar.destroy() : null; + let markupDestroyer = this._destroyMarkup(); + let highlighterDestroyer = this.highlighters.destroy(); this.teardownSplitter(); - this.teardownToolbar(); + this.breadcrumbs.destroy(); - this.selection.off("new-node-front", this.onNewSelection); - this.selection.off("detached-front", this.onDetached); - - let markupDestroyer = this._destroyMarkup(); - - let highlighterDestroyer = this.highlighters.destroy(); - this.prefsObserver.destroy(); this.reflowTracker.destroy(); this.styleChangeTracker.destroy(); this.search.destroy(); @@ -1395,14 +1399,15 @@ Inspector.prototype = { this.panelDoc = null; this.panelWin.inspector = null; this.panelWin = null; - this.prefsObserver = null; this.resultsLength = null; this.search = null; this.searchBox = null; this.show3PaneToggle = null; + this.show3PaneTooltip = null; this.sidebar = null; this.store = null; this.target = null; + this.threePaneTooltip = null; this._panelDestroyer = promise.all([ highlighterDestroyer, diff --git a/devtools/client/inspector/shared/moz.build b/devtools/client/inspector/shared/moz.build index 019842fb79e2..a388247e3671 100644 --- a/devtools/client/inspector/shared/moz.build +++ b/devtools/client/inspector/shared/moz.build @@ -11,6 +11,7 @@ DevToolsModules( 'reflow-tracker.js', 'style-change-tracker.js', 'style-inspector-menu.js', + 'three-pane-onboarding-tooltip.js', 'tooltips-overlay.js', 'utils.js' ) diff --git a/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js b/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js new file mode 100644 index 000000000000..76fe3c1313be --- /dev/null +++ b/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js @@ -0,0 +1,110 @@ +/* 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 { openWebLink } = require("devtools/client/shared/link"); +const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip"); + +const { LocalizationHelper } = require("devtools/shared/l10n"); +const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties"); + +const SHOW_THREE_PANE_ONBOARDING_PREF = "devtools.inspector.show-three-pane-tooltip"; + +const XHTML_NS = "http://www.w3.org/1999/xhtml"; +const CONTAINER_WIDTH = 300; +const LEARN_MORE_LINK = "https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/Use_the_3-pane_inspector?utm_source=devtools&utm_medium=3-pane-onboarding"; + +/** + * Three pane inspector onboarding tooltip that is shown on the 3 pane inspector toggle + * button when the pref is on. + */ +class ThreePaneOnboardingTooltip { + constructor(toolbox, doc) { + this.toolbox = toolbox; + this.doc = doc; + this.tooltip = new HTMLTooltip(this.toolbox.doc, { + 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"; + + const icon = doc.createElementNS(XHTML_NS, "span"); + icon.className = "three-pane-onboarding-icon"; + container.appendChild(icon); + + const content = doc.createElementNS(XHTML_NS, "div"); + content.className = "three-pane-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.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"; + 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", + }); + } + + destroy() { + this.closeButton.removeEventListener("click", this.onCloseButtonClick); + this.learnMoreLink.removeEventListener("click", this.onLearnMoreLinkClick); + + this.tooltip.destroy(); + + this.closeButton = null; + this.doc = null; + this.learnMoreLink = null; + this.toolbox = 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_THREE_PANE_ONBOARDING_PREF, false); + this.tooltip.hide(); + } + + /** + * Handler for the "click" event on the learn more button. Hides the onboarding tooltip + * and opens the link to the mdn page in a new tab. + */ + onLearnMoreLinkClick() { + Services.prefs.setBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF, false); + this.tooltip.hide(); + openWebLink(LEARN_MORE_LINK); + } +} + +module.exports = ThreePaneOnboardingTooltip; diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn index 635e578cb460..2b1dd8b247fe 100644 --- a/devtools/client/jar.mn +++ b/devtools/client/jar.mn @@ -117,6 +117,7 @@ devtools.jar: skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg) skin/images/filters.svg (themes/images/filters.svg) skin/images/filter-swatch.svg (themes/images/filter-swatch.svg) + skin/images/fox-smiling.svg (themes/images/fox-smiling.svg) skin/images/grid.svg (themes/images/grid.svg) skin/images/angle-swatch.svg (themes/images/angle-swatch.svg) skin/images/pseudo-class.svg (themes/images/pseudo-class.svg) diff --git a/devtools/client/locales/en-US/inspector.properties b/devtools/client/locales/en-US/inspector.properties index 0a37f04b0d45..0ca7831a7433 100644 --- a/devtools/client/locales/en-US/inspector.properties +++ b/devtools/client/locales/en-US/inspector.properties @@ -464,3 +464,11 @@ inspector.classPanel.noClasses=No classes on this element # properties to display e.g. due to search criteria this message is # displayed. inspector.noProperties=No CSS properties found. + +# LOCALIZATION NOTE (inspector.threePaneOnboarding.content, +# inspector.threePaneOnboarding.learnMoreLink): This is the content shown in the 3 pane +# inspector onboarding tooltip that is displayed on top of the 3 pane inspector toggle +# button. %S in the content will be replaced by a link at run time with the learnMoreLink +# string. +inspector.threePaneOnboarding.content=New: 3-pane mode lets you see both CSS rules and Layout tools. Click this button to toggle. %S +inspector.threePaneOnboarding.learnMoreLink=Learn more diff --git a/devtools/client/preferences/devtools-client.js b/devtools/client/preferences/devtools-client.js index eb884d401622..99d6d9d9b11a 100644 --- a/devtools/client/preferences/devtools-client.js +++ b/devtools/client/preferences/devtools-client.js @@ -57,6 +57,12 @@ pref("devtools.inspector.three-pane-toggle", false); #endif // Enable the 3 pane mode in the inspector pref("devtools.inspector.three-pane-enabled", false); +// Show the 3 pane onboarding tooltip in the inspector +#if defined(NIGHTLY_BUILD) +pref("devtools.inspector.show-three-pane-tooltip", true); +#else +pref("devtools.inspector.show-three-pane-tooltip", false); +#endif // Collapse pseudo-elements by default in the rule-view pref("devtools.inspector.show_pseudo_elements", false); // The default size for image preview tooltips in the rule-view/computed-view/markup-view diff --git a/devtools/client/shared/test/shared-head.js b/devtools/client/shared/test/shared-head.js index 2a9f200d653e..16694b31226b 100644 --- a/devtools/client/shared/test/shared-head.js +++ b/devtools/client/shared/test/shared-head.js @@ -108,8 +108,10 @@ function loadFrameScriptUtils(browser = gBrowser.selectedBrowser) { return mm; } +Services.prefs.setBoolPref("devtools.inspector.show-three-pane-tooltip", false); registerCleanupFunction(() => { Services.prefs.clearUserPref("devtools.dump.emit"); + Services.prefs.clearUserPref("devtools.inspector.show-three-pane-tooltip"); Services.prefs.clearUserPref("devtools.toolbox.host"); Services.prefs.clearUserPref("devtools.toolbox.previousHost"); Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled"); diff --git a/devtools/client/themes/images/fox-smiling.svg b/devtools/client/themes/images/fox-smiling.svg new file mode 100644 index 000000000000..f277dbc31edd --- /dev/null +++ b/devtools/client/themes/images/fox-smiling.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + diff --git a/devtools/client/themes/tooltips.css b/devtools/client/themes/tooltips.css index 6161c0bc0fd1..e9abb27d4a06 100644 --- a/devtools/client/themes/tooltips.css +++ b/devtools/client/themes/tooltips.css @@ -13,11 +13,15 @@ .theme-dark { --bezier-diagonal-color: #eee; --bezier-grid-color: rgba(0, 0, 0, 0.2); + --onboarding-link-color: var(--theme-highlight-blue); + --onboarding-link-active-color: var(--blue-40); } .theme-light { --bezier-diagonal-color: rgba(0, 0, 0, 0.2); --bezier-grid-color: rgba(0, 0, 0, 0.05); + --onboarding-link-color: var(--blue-60); + --onboarding-link-active-color: var(--blue-70); } /* Tooltip widget (see devtools/client/shared/widgets/tooltip/Tooltip.js) */ @@ -480,3 +484,53 @@ height: 100%; padding: 7px; } + +/* Tooltip: 3 Pane Inspecot Onboarding Tooltip */ + +.three-pane-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 { + 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 { + flex: 1; + padding-inline-start: 5px; +} + +.three-pane-onboarding-link { + color: var(--onboarding-link-color); + cursor: pointer; +} + +.three-pane-onboarding-link:hover { + text-decoration: underline; +} + +.three-pane-onboarding-link:active { + color: var(--onboarding-link-active-color); +} + +.three-pane-onboarding-close-button { + align-self: flex-start; +} + +.three-pane-onboarding-close-button::before { + background-image: url("chrome://devtools/skin/images/close.svg"); + margin: -6px 0 0 -6px; +} diff --git a/devtools/client/themes/variables.css b/devtools/client/themes/variables.css index fb0d0027194b..a5cad98716b3 100644 --- a/devtools/client/themes/variables.css +++ b/devtools/client/themes/variables.css @@ -214,6 +214,7 @@ --purple-60: #8000d7; + --blue-40: #45a1ff; --blue-50: #0a84ff; --blue-55: #0074e8; --blue-60: #0060df;