Bug 1359763 - Persist the box model and grid panel's accordion states. r=pbro draft
authorGabriel Luong <gabriel.luong@gmail.com>
Fri, 02 Jun 2017 02:13:13 -0400
changeset 588105 62becaee75bcab8aa6aec6b3c429a3029e546cf0
parent 588104 9b2fc8bf9cad366dbded5b56f0f309d3f86d7f81
child 631463 68d566f0a480ed2ac33f67a5f22d23a03c3dbd96
push id61911
push userbmo:gl@mozilla.com
push dateFri, 02 Jun 2017 06:14:11 +0000
reviewerspbro
bugs1359763
milestone55.0a1
Bug 1359763 - Persist the box model and grid panel's accordion states. r=pbro MozReview-Commit-ID: 46AZ5M2ZoJF
devtools/client/inspector/boxmodel/components/BoxModelApp.js
devtools/client/inspector/boxmodel/test/browser.ini
devtools/client/inspector/boxmodel/test/browser_boxmodel_computed-accordion-state.js
devtools/client/inspector/boxmodel/test/browser_boxmodel_layout-accordion-state.js
devtools/client/inspector/grids/test/browser.ini
devtools/client/inspector/grids/test/browser_grids_accordion-state.js
devtools/client/inspector/layout/components/Accordion.js
devtools/client/inspector/layout/components/App.js
devtools/client/preferences/devtools.js
--- a/devtools/client/inspector/boxmodel/components/BoxModelApp.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelApp.js
@@ -1,29 +1,32 @@
 /* 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 { addons, createClass, createFactory, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const Accordion =
   createFactory(require("devtools/client/inspector/layout/components/Accordion"));
 const BoxModel = createFactory(require("./BoxModel"));
 
 const Types = require("../types");
 
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
+const BOXMODEL_OPENED_PREF = "devtools.computed.boxmodel.opened";
+
 const BoxModelApp = createClass({
 
   displayName: "BoxModelApp",
 
   propTypes: {
     boxModel: PropTypes.shape(Types.boxModel).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
     showBoxModelProperties: PropTypes.bool.isRequired,
@@ -38,17 +41,21 @@ const BoxModelApp = createClass({
 
   render() {
     return Accordion({
       items: [
         {
           header: BOXMODEL_L10N.getStr("boxmodel.title"),
           component: BoxModel,
           componentProps: this.props,
-          opened: true,
+          opened: Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+          onToggled: () => {
+            let opened = Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF);
+            Services.prefs.setBoolPref(BOXMODEL_OPENED_PREF, !opened);
+          }
         }
       ]
     });
   },
 
 });
 
 module.exports = connect(state => state)(BoxModelApp);
--- a/devtools/client/inspector/boxmodel/test/browser.ini
+++ b/devtools/client/inspector/boxmodel/test/browser.ini
@@ -8,24 +8,26 @@ support-files =
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_boxmodel.js]
+[browser_boxmodel_computed-accordion-state.js]
 [browser_boxmodel_editablemodel.js]
 # [browser_boxmodel_editablemodel_allproperties.js]
 # Disabled for too many intermittent failures (bug 1009322)
 [browser_boxmodel_editablemodel_bluronclick.js]
 [browser_boxmodel_editablemodel_border.js]
 [browser_boxmodel_editablemodel_pseudo.js]
 [browser_boxmodel_editablemodel_stylerules.js]
 [browser_boxmodel_guides.js]
+[browser_boxmodel_layout-accordion-state.js]
 [browser_boxmodel_navigation.js]
 [browser_boxmodel_offsetparent.js]
 [browser_boxmodel_positions.js]
 [browser_boxmodel_properties.js]
 [browser_boxmodel_pseudo-element.js]
 [browser_boxmodel_rotate-labels-on-sides.js]
 [browser_boxmodel_sync.js]
 [browser_boxmodel_tooltips.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_computed-accordion-state.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the box model's accordion state is persistent through hide/show in the
+// computed view.
+
+const TEST_URI = `
+  <style>
+    #div1 {
+      margin: 10px;
+      padding: 3px;
+    }
+  </style>
+  <div id="div1"></div>
+`;
+
+const BOXMODEL_OPENED_PREF = "devtools.computed.boxmodel.opened";
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, view, toolbox } = yield openBoxModelView();
+  let { document: doc } = view;
+
+  yield testAccordionStateAfterClickingHeader(doc);
+  yield testAccordionStateAfterSwitchingSidebars(inspector, doc);
+  yield testAccordionStateAfterReopeningComputedView(toolbox);
+
+  Services.prefs.clearUserPref(BOXMODEL_OPENED_PREF);
+});
+
+function* testAccordionStateAfterClickingHeader(doc) {
+  let header = doc.querySelector("#computedview-container .box-model-pane ._header");
+  let content = doc.querySelector("#computedview-container .box-model-pane ._content");
+
+  info("Checking initial state of the box model panel.");
+  is(content.style.display, "block", "The box model panel content is 'display: block'.");
+  ok(Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+    `${BOXMODEL_OPENED_PREF} is pref on by default.`);
+
+  info("Clicking the box model header to hide the box model panel.");
+  header.click();
+
+  info("Checking the new state of the box model panel.");
+  is(content.style.display, "none", "The box model panel content is 'display: none'.");
+  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+    `${BOXMODEL_OPENED_PREF} is pref off.`);
+}
+
+function* testAccordionStateAfterSwitchingSidebars(inspector, doc) {
+  info("Checking the box model accordion state is persistent after switching sidebars.");
+
+  let content = doc.querySelector("#computedview-container .box-model-pane ._content");
+
+  info("Selecting the layout view.");
+  inspector.sidebar.select("layoutview");
+
+  info("Selecting the computed view.");
+  inspector.sidebar.select("computedview");
+
+  info("Checking the state of the box model panel.");
+  is(content.style.display, "none", "The box model panel content is 'display: none'.");
+  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+    `${BOXMODEL_OPENED_PREF} is pref off.`);
+}
+
+function* testAccordionStateAfterReopeningComputedView(toolbox) {
+  info("Checking the box model accordion state is persistent after closing and "
+  + "re-opening the layout view.");
+
+  info("Closing the toolbox.");
+  yield toolbox.destroy();
+
+  info("Re-opening the layout view.");
+  let { view } = yield openBoxModelView();
+  let { document: doc } = view;
+  let content = doc.querySelector("#computedview-container .box-model-pane ._content");
+
+  info("Checking the state of the box model panel.");
+  ok(!content, "The box model panel content is not rendered.");
+  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+    `${BOXMODEL_OPENED_PREF} is pref off.`);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_layout-accordion-state.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the box model's accordion state is persistent through hide/show in the
+// layout view.
+
+const TEST_URI = `
+  <style>
+    #div1 {
+      margin: 10px;
+      padding: 3px;
+    }
+  </style>
+  <div id="div1"></div>
+`;
+
+const BOXMODEL_OPENED_PREF = "devtools.layout.boxmodel.opened";
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, boxmodel, toolbox } = yield openLayoutView();
+  let { document: doc } = boxmodel;
+
+  yield testAccordionStateAfterClickingHeader(doc);
+  yield testAccordionStateAfterSwitchingSidebars(inspector, doc);
+  yield testAccordionStateAfterReopeningLayoutView(toolbox);
+
+  Services.prefs.clearUserPref(BOXMODEL_OPENED_PREF);
+});
+
+function* testAccordionStateAfterClickingHeader(doc) {
+  let header = doc.querySelector("#layout-container .box-model-pane ._header");
+  let content = doc.querySelector("#layout-container .box-model-pane ._content");
+
+  info("Checking initial state of the box model panel.");
+  is(content.style.display, "block", "The box model panel content is 'display: block'.");
+  ok(Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+    `${BOXMODEL_OPENED_PREF} is pref on by default.`);
+
+  info("Clicking the box model header to hide the box model panel.");
+  header.click();
+
+  info("Checking the new state of the box model panel.");
+  is(content.style.display, "none", "The box model panel content is 'display: none'.");
+  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+    `${BOXMODEL_OPENED_PREF} is pref off.`);
+}
+
+function* testAccordionStateAfterSwitchingSidebars(inspector, doc) {
+  info("Checking the box model accordion state is persistent after switching sidebars.");
+
+  let content = doc.querySelector("#layout-container .box-model-pane ._content");
+
+  info("Selecting the computed view.");
+  inspector.sidebar.select("computedview");
+
+  info("Selecting the layout view.");
+  inspector.sidebar.select("layoutview");
+
+  info("Checking the state of the box model panel.");
+  is(content.style.display, "none", "The box model panel content is 'display: none'.");
+  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+    `${BOXMODEL_OPENED_PREF} is pref off.`);
+}
+
+function* testAccordionStateAfterReopeningLayoutView(toolbox) {
+  info("Checking the box model accordion state is persistent after closing and "
+  + "re-opening the layout view.");
+
+  info("Closing the toolbox.");
+  yield toolbox.destroy();
+
+  info("Re-opening the layout view.");
+  let { boxmodel } = yield openLayoutView();
+  let { document: doc } = boxmodel;
+  let content = doc.querySelector("#layout-container .box-model-pane ._content");
+
+  info("Checking the state of the box model panel.");
+  ok(!content, "The box model panel content is not rendered.");
+  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+    `${BOXMODEL_OPENED_PREF} is pref off.`);
+}
--- a/devtools/client/inspector/grids/test/browser.ini
+++ b/devtools/client/inspector/grids/test/browser.ini
@@ -6,16 +6,17 @@ support-files =
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
   !/devtools/client/framework/test/shared-redux-head.js
 
+[browser_grids_accordion-state.js]
 [browser_grids_display-setting-extend-grid-lines.js]
 [browser_grids_display-setting-show-grid-line-numbers.js]
 [browser_grids_display-setting-show-grid-areas.js]
 [browser_grids_grid-list-color-picker-on-ESC.js]
 [browser_grids_grid-list-color-picker-on-RETURN.js]
 [browser_grids_grid-list-element-rep.js]
 [browser_grids_grid-list-no-grids.js]
 [browser_grids_grid-list-on-mutation-element-added.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_accordion-state.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid's accordion state is persistent through hide/show in the layout
+// view.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+const GRID_OPENED_PREF = "devtools.layout.grid.opened";
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector, toolbox } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+
+  yield testAccordionStateAfterClickingHeader(doc);
+  yield testAccordionStateAfterSwitchingSidebars(inspector, doc);
+  yield testAccordionStateAfterReopeningLayoutView(toolbox);
+
+  Services.prefs.clearUserPref(GRID_OPENED_PREF);
+});
+
+function* testAccordionStateAfterClickingHeader(doc) {
+  let header = doc.querySelector(".grid-pane ._header");
+  let content = doc.querySelector(".grid-pane ._content");
+
+  info("Checking initial state of the grid panel.");
+  is(content.style.display, "block", "The grid panel content is 'display: block'.");
+  ok(Services.prefs.getBoolPref(GRID_OPENED_PREF),
+    `${GRID_OPENED_PREF} is pref on by default.`);
+
+  info("Clicking the grid header to hide the grid panel.");
+  header.click();
+
+  info("Checking the new state of the grid panel.");
+  is(content.style.display, "none", "The grid panel content is 'display: none'.");
+  ok(!Services.prefs.getBoolPref(GRID_OPENED_PREF), `${GRID_OPENED_PREF} is pref off.`);
+}
+
+function* testAccordionStateAfterSwitchingSidebars(inspector, doc) {
+  info("Checking the grid accordion state is persistent after switching sidebars.");
+
+  let content = doc.querySelector(".grid-pane ._content");
+
+  info("Selecting the computed view.");
+  inspector.sidebar.select("computedview");
+
+  info("Selecting the layout view.");
+  inspector.sidebar.select("layoutview");
+
+  info("Checking the state of the grid panel.");
+  is(content.style.display, "none", "The grid panel content is 'display: none'.");
+  ok(!Services.prefs.getBoolPref(GRID_OPENED_PREF), `${GRID_OPENED_PREF} is pref off.`);
+}
+
+function* testAccordionStateAfterReopeningLayoutView(toolbox) {
+  info("Checking the grid accordion state is persistent after closing and re-opening the "
+  + "layout view.");
+
+  info("Closing the toolbox.");
+  yield toolbox.destroy();
+
+  info("Re-opening the layout view.");
+  let { gridInspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let content = doc.querySelector(".grid-pane ._content");
+
+  info("Checking the state of the grid panel.");
+  ok(!content, "The grid panel content is not rendered.");
+  ok(!Services.prefs.getBoolPref(GRID_OPENED_PREF),
+    `${GRID_OPENED_PREF} is pref off.`);
+}
--- a/devtools/client/inspector/layout/components/Accordion.js
+++ b/devtools/client/inspector/layout/components/Accordion.js
@@ -4,16 +4,17 @@
 
 /**
  * This file should not be modified and is a duplicate from the debugger.html project.
  * Any changes to this file should be imported from the upstream debugger.html project.
  */
 
 "use strict";
 
+const Services = require("Services");
 const React = require("devtools/client/shared/vendor/react");
 const { DOM: dom, PropTypes } = React;
 
 const { div, span } = dom;
 
 const Accordion = React.createClass({
   displayName: "Accordion",
 
@@ -35,16 +36,20 @@ const Accordion = React.createClass({
 
     opened[i] = !opened[i];
     created[i] = true;
 
     if (opened[i] && item.onOpened) {
       item.onOpened();
     }
 
+    if (item.onToggled) {
+      item.onToggled();
+    }
+
     this.setState({ opened, created });
   },
 
   renderContainer: function (item, i) {
     const { opened, created } = this.state;
     const containerClassName =
           item.header.toLowerCase().replace(/\s/g, "-") + "-pane";
     let arrowClassName = "arrow theme-twisty";
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -19,16 +19,19 @@ const GridTypes = require("devtools/clie
 const Accordion = createFactory(require("./Accordion"));
 
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
 const LAYOUT_STRINGS_URI = "devtools/client/locales/layout.properties";
 const LAYOUT_L10N = new LocalizationHelper(LAYOUT_STRINGS_URI);
 
+const BOXMODEL_OPENED_PREF = "devtools.layout.boxmodel.opened";
+const GRID_OPENED_PREF = "devtools.layout.grid.opened";
+
 const App = createClass({
 
   displayName: "App",
 
   propTypes: {
     boxModel: PropTypes.shape(BoxModelTypes.boxModel).isRequired,
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(GridTypes.grid)).isRequired,
@@ -55,23 +58,31 @@ const App = createClass({
         className: "devtools-monospace",
       },
       Accordion({
         items: [
           {
             header: BOXMODEL_L10N.getStr("boxmodel.title"),
             component: BoxModel,
             componentProps: this.props,
-            opened: true,
+            opened: Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+            onToggled: () => {
+              let opened = Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF);
+              Services.prefs.setBoolPref(BOXMODEL_OPENED_PREF, !opened);
+            }
           },
           {
             header: LAYOUT_L10N.getStr("layout.header"),
             component: Grid,
             componentProps: this.props,
-            opened: true,
+            opened: Services.prefs.getBoolPref(GRID_OPENED_PREF),
+            onToggled: () => {
+              let opened = Services.prefs.getBoolPref(GRID_OPENED_PREF);
+              Services.prefs.setBoolPref(GRID_OPENED_PREF, !opened);
+            }
           },
         ]
       })
     );
   },
 
 });
 
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -1,12 +1,11 @@
-# -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
-# 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/.
+/* 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/. */
 
 // Developer edition promo preferences
 pref("devtools.devedition.promo.shown", false);
 pref("devtools.devedition.promo.url", "https://www.mozilla.org/firefox/developer/?utm_source=firefox-dev-tools&utm_medium=firefox-browser&utm_content=betadoorhanger");
 
 // Only potentially show in beta release
 #if MOZ_UPDATE_CHANNEL == beta
   pref("devtools.devedition.promo.enabled", true);
@@ -73,16 +72,23 @@ pref("devtools.fontinspector.enabled", t
 pref("devtools.layoutview.enabled", false);
 
 // Grid highlighter preferences
 pref("devtools.gridinspector.showGridAreas", false);
 pref("devtools.gridinspector.showGridLineNumbers", false);
 pref("devtools.gridinspector.showGridOutline", false);
 pref("devtools.gridinspector.showInfiniteLines", false);
 
+// Whether or not the box model panel is opened in the computed view
+pref("devtools.computed.boxmodel.opened", true);
+// Whether or not the box model panel is opened in the layout view
+pref("devtools.layout.boxmodel.opened", true);
+// Whether or not the grid inspector panel is opened in the layout view
+pref("devtools.layout.grid.opened", true);
+
 // By how many times eyedropper will magnify pixels
 pref("devtools.eyedropper.zoom", 6);
 
 // Enable to collapse attributes that are too long.
 pref("devtools.markup.collapseAttributes", true);
 
 // Length to collapse attributes
 pref("devtools.markup.collapseAttributeLength", 120);