Bug 1496458 - Display the flex-container accordion first if a container+item is selected in markup-view; r=gl
authorPatrick Brosset <pbrosset@mozilla.com>
Fri, 30 Nov 2018 16:41:57 +0000
changeset 508273 00269c0edf56d1a920d1b0dfd757ae9221c40e30
parent 508272 65bf3e37a4409f3ca0350366d7be19368adfa21b
child 508274 1382e8cdf8a670af149e138585188edd4a5c11e2
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgl
bugs1496458
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1496458 - Display the flex-container accordion first if a container+item is selected in markup-view; r=gl Flex elements can be both containers and items at the same time. When they get selected in the inspector the sidebar shows both the container accordion and the item accordion. This changeset makes sure the container accordion is displayed before the item accordion only when the element is selected from the markup-view. Differential Revision: https://phabricator.services.mozilla.com/D13416
devtools/client/inspector/flexbox/components/FlexItemSizingProperties.js
devtools/client/inspector/flexbox/components/Flexbox.js
devtools/client/inspector/flexbox/flexbox.js
devtools/client/inspector/flexbox/reducers/flexbox.js
devtools/client/inspector/flexbox/test/browser.ini
devtools/client/inspector/flexbox/test/browser_flexbox_accordion_state.js
devtools/client/inspector/flexbox/test/browser_flexbox_container_and_item_accordion_state.js
devtools/client/inspector/flexbox/test/browser_flexbox_empty_state.js
devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_ESC.js
devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_RETURN.js
devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_has_correct_layout.js
devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_renders_basisfinal_points_correctly.js
devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_rotates_for_column.js
devtools/client/inspector/flexbox/test/browser_flexbox_non_flex_item_is_not_shown.js
devtools/client/inspector/flexbox/test/browser_flexbox_pseudo_elements_are_listed.js
devtools/client/inspector/flexbox/test/browser_flexbox_sizing_flexibility_not_displayed_when_useless.js
devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_do_not_show_unspecified_min_dimension.js
devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_for_different_writing_modes.js
devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_has_correct_sections.js
devtools/client/inspector/flexbox/test/browser_flexbox_text_nodes_are_listed.js
devtools/client/inspector/flexbox/test/doc_flexbox_specific_cases.html
devtools/client/inspector/flexbox/test/head.js
devtools/client/inspector/layout/components/Accordion.js
devtools/client/inspector/layout/components/LayoutApp.js
--- a/devtools/client/inspector/flexbox/components/FlexItemSizingProperties.js
+++ b/devtools/client/inspector/flexbox/components/FlexItemSizingProperties.js
@@ -108,21 +108,20 @@ class FlexItemSizingProperties extends P
           title,
           property
         ),
         this.renderSize(mainBaseSize)
       )
     );
   }
 
-  renderFlexibilitySection(flexItemSizing, properties, computedStyle) {
+  renderFlexibilitySection(flexItemSizing, mainFinalSize, properties, computedStyle) {
     const {
       mainDeltaSize,
       mainBaseSize,
-      mainFinalSize,
       lineGrowthState,
     } = flexItemSizing;
 
     // Don't display anything if all interesting sizes are 0.
     if (!mainFinalSize && !mainBaseSize && !mainDeltaSize) {
       return null;
     }
 
@@ -244,17 +243,17 @@ class FlexItemSizingProperties extends P
             null
         ),
         this.renderSize(mainMaxSize),
         this.renderReasons(reasons)
       )
     );
   }
 
-  renderFinalSizeSection({ mainFinalSize }) {
+  renderFinalSizeSection(mainFinalSize) {
     return (
       dom.li({ className: "section final no-property" },
         dom.span({ className: "name" },
           getStr("flexbox.itemSizing.finalSizeSectionHeader")
         ),
         this.renderSize(mainFinalSize)
       )
     );
@@ -277,23 +276,23 @@ class FlexItemSizingProperties extends P
       mainMinSize,
     } = flexItemSizing;
     const dimension = mainAxisDirection.startsWith("horizontal") ? "width" : "height";
 
     // Calculate the final size. This is base + delta, then clamped by min or max.
     let mainFinalSize = mainBaseSize + mainDeltaSize;
     mainFinalSize = Math.max(mainFinalSize, mainMinSize);
     mainFinalSize = Math.min(mainFinalSize, mainMaxSize);
-    flexItemSizing.mainFinalSize = mainFinalSize;
 
     return (
       dom.ul({ className: "flex-item-sizing" },
         this.renderBaseSizeSection(flexItemSizing, properties, dimension),
-        this.renderFlexibilitySection(flexItemSizing, properties, computedStyle),
+        this.renderFlexibilitySection(flexItemSizing, mainFinalSize, properties,
+          computedStyle),
         this.renderMinimumSizeSection(flexItemSizing, properties, dimension),
         this.renderMaximumSizeSection(flexItemSizing, properties, dimension),
-        this.renderFinalSizeSection(flexItemSizing)
+        this.renderFinalSizeSection(mainFinalSize)
       )
     );
   }
 }
 
 module.exports = FlexItemSizingProperties;
--- a/devtools/client/inspector/flexbox/components/Flexbox.js
+++ b/devtools/client/inspector/flexbox/components/Flexbox.js
@@ -105,17 +105,17 @@ class Flexbox extends PureComponent {
       );
     }
 
     const {
       flexItemShown,
     } = flexContainer;
 
     return (
-      dom.div({ id: "layout-flexbox-container" },
+      dom.div({ className: "layout-flexbox-wrapper" },
         Header({
           flexContainer,
           getSwatchColorPickerTooltip,
           onHideBoxModelHighlighter,
           onSetFlexboxOverlayColor,
           onShowBoxModelHighlighterForNode,
           onToggleFlexboxHighlighter,
           setSelectedNode,
--- a/devtools/client/inspector/flexbox/flexbox.js
+++ b/devtools/client/inspector/flexbox/flexbox.js
@@ -300,17 +300,18 @@ class FlexboxInspector {
    * Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows,
    * updates the flexbox panel because the shape of the flexbox on the page may have
    * changed.
    */
   async onReflow() {
     if (!this.isPanelVisible() ||
         !this.store ||
         !this.selection.nodeFront ||
-        !this.hasGetCurrentFlexbox) {
+        !this.hasGetCurrentFlexbox ||
+        this._isUpdating) {
       return;
     }
 
     try {
       const flexContainer = await this.getFlexContainerProps(this.selection.nodeFront);
 
       // Clear the flexbox panel if there is no flex container for the current node
       // selection.
@@ -404,23 +405,30 @@ class FlexboxInspector {
     this.highlighters.toggleFlexboxHighlighter(node, "layout");
     this.store.dispatch(updateFlexboxHighlighted(node !==
       this.highlighters.flexboxHighlighterShow));
   }
 
   /**
    * Handler for "new-root" event fired by the inspector and "new-node-front" event fired
    * by the inspector selection. Updates the flexbox panel if it is visible.
+   *
+   * @param  {Object}
+   *         This callback is sometimes executed on "new-node-front" events which means
+   *         that a first param is passed here (the nodeFront), which we don't care about.
+   * @param  {String} reason
+   *         On "new-node-front" events, a reason is passed here, and we need it to detect
+   *         if this update was caused by a node selection from the markup-view.
    */
-  onUpdatePanel() {
+  onUpdatePanel(_, reason) {
     if (!this.isPanelVisible()) {
       return;
     }
 
-    this.update();
+    this.update(null, null, reason === "treepanel");
   }
 
   /**
    * Track usage of the tool via telemetry.
    *
    * @param  {Boolean} isContainerInfoShown
    *         Whether the flex container accordion is displayed.
    * @param  {Boolean} isItemInfoShown
@@ -446,37 +454,43 @@ class FlexboxInspector {
    * with new flexbox data.
    *
    * @param  {Object|null} flexContainer
    *         An object consisting of the current flex container's flex items and
    *         properties.
    * @param  {Object|null} flexItemContainer
    *         An object consisting of the parent flex container's flex items and
    *         properties.
+   * @param  {Boolean} initiatedByMarkupViewSelection
+   *         True if the update was due to a node selection in the markup-view.
    */
-  async update(flexContainer, flexItemContainer) {
+  async update(flexContainer, flexItemContainer, initiatedByMarkupViewSelection) {
+    this._isUpdating = true;
+
     // Stop refreshing if the inspector or store is already destroyed or no node is
     // selected.
     if (!this.inspector ||
         !this.store ||
         !this.selection.nodeFront ||
         !this.hasGetCurrentFlexbox) {
+      this._isUpdating = false;
       return;
     }
 
     try {
       // Fetch the current flexbox if no flexbox front was passed into this update.
       if (!flexContainer) {
         flexContainer = await this.getFlexContainerProps(this.selection.nodeFront);
       }
 
       // Clear the flexbox panel if there is no flex container for the current node
       // selection.
       if (!flexContainer) {
         this.store.dispatch(clearFlexbox());
+        this._isUpdating = false;
         return;
       }
 
       if (!flexItemContainer && flexContainer.nodeFront === this.selection.nodeFront) {
         flexItemContainer = await this.getFlexContainerProps(this.selection.nodeFront,
           true);
       }
 
@@ -484,25 +498,28 @@ class FlexboxInspector {
         flexContainer.nodeFront === this.highlighters.flexboxHighlighterShown;
       const color = await this.getOverlayColor();
 
       this.store.dispatch(updateFlexbox({
         color,
         flexContainer,
         flexItemContainer,
         highlighted,
+        initiatedByMarkupViewSelection,
       }));
 
       const isContainerInfoShown = !flexContainer.flexItemShown || !!flexItemContainer;
       const isItemInfoShown = !!flexContainer.flexItemShown || !!flexItemContainer;
       this.sendTelemetryProbes(isContainerInfoShown, isItemInfoShown);
     } catch (e) {
       // This call might fail if called asynchrously after the toolbox is finished
       // closing.
     }
+
+    this._isUpdating = false;
   }
 }
 
 /**
  * For a given flex container object, returns the flex container properties that can be
  * used to check if 2 flex container objects are the same.
  *
  * @param  {Object|null} flexContainer
--- a/devtools/client/inspector/flexbox/reducers/flexbox.js
+++ b/devtools/client/inspector/flexbox/reducers/flexbox.js
@@ -46,16 +46,20 @@ const INITIAL_FLEXBOX = {
     isFlexItemContainer: true,
     // The NodeFront of the parent flex container.
     nodeFront: null,
     // The computed styles properties of the parent flex container.
     properties: null,
   },
   // Whether or not the flexbox highlighter is highlighting the flex container.
   highlighted: false,
+  // Whether or not the node selection that led to the flexbox tool being shown came from
+  // the user selecting a node in the markup-view (whereas, say, selecting in the flex
+  // items list)
+  initiatedByMarkupViewSelection: false,
 };
 
 const reducers = {
 
   [CLEAR_FLEXBOX](flexbox, _) {
     return INITIAL_FLEXBOX;
   },
 
--- a/devtools/client/inspector/flexbox/test/browser.ini
+++ b/devtools/client/inspector/flexbox/test/browser.ini
@@ -15,16 +15,17 @@ support-files =
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_flexbox_accordion_state.js]
 [browser_flexbox_container_and_item.js]
+[browser_flexbox_container_and_item_accordion_state.js]
 [browser_flexbox_container_and_item_updates_on_change.js]
 [browser_flexbox_container_element_rep.js]
 [browser_flexbox_container_properties.js]
 [browser_flexbox_empty_state.js]
 [browser_flexbox_highlighter_color_picker_on_ESC.js]
 [browser_flexbox_highlighter_color_picker_on_RETURN.js]
 [browser_flexbox_highlighter_opened_telemetry.js]
 [browser_flexbox_item_list_01.js]
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_accordion_state.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_accordion_state.js
@@ -18,18 +18,18 @@ add_task(async function() {
   await testAccordionStateAfterClickingHeader(doc);
   await testAccordionStateAfterSwitchingSidebars(inspector, doc);
   await testAccordionStateAfterReopeningLayoutView(toolbox);
 
   Services.prefs.clearUserPref(FLEXBOX_OPENED_PREF);
 });
 
 function testAccordionStateAfterClickingHeader(doc) {
-  const header = doc.querySelector(".flexbox-pane ._header");
-  const content = doc.querySelector(".flexbox-pane ._content");
+  const header = doc.querySelector(".flex-accordion ._header");
+  const content = doc.querySelector(".flex-accordion ._content");
 
   info("Checking initial state of the flexbox panel.");
   is(content.style.display, "block", "The flexbox panel content is 'display: block'.");
   ok(Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
     `${FLEXBOX_OPENED_PREF} is pref on by default.`);
 
   info("Clicking the flexbox header to hide the flexbox panel.");
   header.click();
@@ -38,17 +38,17 @@ function testAccordionStateAfterClicking
   is(content.style.display, "none", "The flexbox panel content is 'display: none'.");
   ok(!Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
     `${FLEXBOX_OPENED_PREF} is pref off.`);
 }
 
 function testAccordionStateAfterSwitchingSidebars(inspector, doc) {
   info("Checking the flexbox accordion state is persistent after switching sidebars.");
 
-  const content = doc.querySelector(".flexbox-pane ._content");
+  const content = doc.querySelector(".flex-accordion ._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 flexbox panel.");
@@ -62,15 +62,15 @@ async function testAccordionStateAfterRe
     + "the layout view.");
 
   info("Closing the toolbox.");
   await toolbox.destroy();
 
   info("Re-opening the layout view.");
   const { flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
-  const content = doc.querySelector(".flexbox-pane ._content");
+  const content = doc.querySelector(".flex-accordion ._content");
 
   info("Checking the state of the flexbox panel.");
   ok(!content, "The flexbox panel content is not rendered.");
   ok(!Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
     `${FLEXBOX_OPENED_PREF} is pref off.`);
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_container_and_item_accordion_state.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test the order in which accordion items are shown for container-item elements.
+// For those combined types, the container accordion is shown first if the selection came
+// from the markup-view, because we assume in this case that users do want to see the
+// element selected as a container first.
+// However when users select an item in the list of items in the container accordion (or
+// in the item selector dropdown), then the item accordion should be shown first.
+
+const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
+
+add_task(async function() {
+  await addTab(TEST_URI);
+  const { inspector, flexboxInspector } = await openLayoutView();
+  const { document: doc } = flexboxInspector;
+
+  info("Select a flex container-only node");
+  await selectNode("#container-only", inspector);
+  await waitUntil(() => doc.querySelectorAll(".flex-header-container-properties").length);
+
+  info("Check that there is only 1 accordion for displayed");
+  let accordions = doc.querySelectorAll(".flex-accordion");
+  is(accordions.length, 1, "There's only 1 accordion");
+  ok(accordions[0].classList.contains("container"),
+     "The accordion is the container type");
+
+  info("Select a flex container+item node by clicking in the markup-view");
+  await clickOnNodeInMarkupView("#container-and-item", inspector);
+  await waitUntil(() => doc.querySelectorAll(".flex-accordion").length === 2);
+
+  info("Check that the 2 accordions are displayed, with container type being first");
+  accordions = doc.querySelectorAll(".flex-accordion");
+  is(accordions.length, 2, "There are 2 accordions");
+  ok(accordions[0].classList.contains("container"),
+     "The first accordion is the container type");
+  ok(accordions[1].classList.contains("item"),
+     "The second accordion is the item type");
+
+  info("Select the container-only node again");
+  await selectNode("#container-only", inspector);
+  await waitUntil(() => doc.querySelectorAll(".flex-accordion").length === 1);
+
+  info("Click on the container+item node right there in the accordion item list");
+  doc.querySelector(".flex-item-list button").click();
+  await waitUntil(() => doc.querySelectorAll(".flex-accordion").length === 2);
+
+  info("Check that the 2 accordions are displayed again, with item type being first");
+  accordions = doc.querySelectorAll(".flex-accordion");
+  is(accordions.length, 2, "There are 2 accordions again");
+  ok(accordions[0].classList.contains("item"),
+     "The first accordion is the item type");
+  ok(accordions[1].classList.contains("container"),
+     "The second accordion is the container type");
+});
+
+async function clickOnNodeInMarkupView(selector, inspector) {
+  const { selection, markup } = inspector;
+
+  await markup.expandAll(selection.nodeFront);
+  const nodeFront = await getNodeFront(selector, inspector);
+  const markupContainer = markup.getContainer(nodeFront);
+
+  const onSelected = inspector.once("inspector-updated");
+  EventUtils.synthesizeMouseAtCenter(markupContainer.tagLine, {type: "mousedown"},
+    markup.doc.defaultView);
+  EventUtils.synthesizeMouseAtCenter(markupContainer.tagLine, {type: "mouseup"},
+    markup.doc.defaultView);
+  await onSelected;
+}
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_empty_state.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_empty_state.js
@@ -11,13 +11,13 @@ const TEST_URI = `
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
   const { highlighters } = inspector;
 
   info("Checking the initial state of the Flexbox Inspector.");
-  ok(doc.querySelector(".flexbox-pane .devtools-sidepanel-no-result",
+  ok(doc.querySelector(".flex-accordion .devtools-sidepanel-no-result",
     "A message is displayede when no flex container is selected."));
   ok(!highlighters.flexboxHighlighterShown,
     "No flexbox highlighter exists in the highlighters overlay.");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_ESC.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_ESC.js
@@ -12,17 +12,17 @@ add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector, layoutView } = await openLayoutView();
   const { document: doc } = flexboxInspector;
   const { store } = inspector;
   const cPicker = layoutView.swatchColorPickerTooltip;
   const spectrum = cPicker.spectrum;
 
   const onColorSwatchRendered = waitForDOM(doc,
-    "#layout-flexbox-container .layout-color-swatch");
+    ".layout-flexbox-wrapper .layout-color-swatch");
   await selectNode("#container", inspector);
   const [swatch] = await onColorSwatchRendered;
 
   info("Checking the initial state of the Flexbox Inspector color picker.");
   is(swatch.style.backgroundColor, "rgb(148, 0, 255)",
     "The color swatch's background is correct.");
   is(store.getState().flexbox.color, "#9400FF", "The flexbox color state is correct.");
 
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_RETURN.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_RETURN.js
@@ -12,17 +12,17 @@ add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector, layoutView } = await openLayoutView();
   const { document: doc } = flexboxInspector;
   const { highlighters, store } = inspector;
   const cPicker = layoutView.swatchColorPickerTooltip;
   const spectrum = cPicker.spectrum;
 
   const onColorSwatchRendered = waitForDOM(doc,
-    "#layout-flexbox-container .layout-color-swatch");
+    ".layout-flexbox-wrapper .layout-color-swatch");
   await selectNode("#container", inspector);
   const [swatch] = await onColorSwatchRendered;
 
   const checkbox = doc.getElementById("flexbox-checkbox-toggle");
 
   info("Checking the initial state of the Flexbox Inspector color picker.");
   ok(!checkbox.checked, "Flexbox highlighter toggle is unchecked.");
   is(swatch.style.backgroundColor, "rgb(148, 0, 255)",
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_has_correct_layout.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_has_correct_layout.js
@@ -33,24 +33,21 @@ const TEST_DATA = [{
                         "60fr [final-end min] 140fr [basis-end delta-end]",
 }];
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
 
-  for (const {selector, expectedGridTemplate} of TEST_DATA) {
+  for (const { selector, expectedGridTemplate } of TEST_DATA) {
     info(`Checking the grid template for the flex item outline for ${selector}`);
 
-    const flexOutline = await selectNodeAndGetFlexOutline(selector, inspector, doc);
+    await selectNode(selector, inspector);
+    await waitUntil(() => {
+      const flexOutline = doc.querySelector(".flex-outline");
+      return flexOutline &&
+             flexOutline.style.gridTemplateColumns === expectedGridTemplate;
+    });
 
-    is(flexOutline.style.gridTemplateColumns, expectedGridTemplate,
-       "Grid template is correct");
+    ok(true, "Grid template is correct");
   }
 });
-
-async function selectNodeAndGetFlexOutline(selector, inspector, doc) {
-  const onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline");
-  await selectNode(selector, inspector);
-  const [flexOutlineContainer] = await onFlexItemOutlineRendered;
-  return flexOutlineContainer;
-}
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_renders_basisfinal_points_correctly.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_renders_basisfinal_points_correctly.js
@@ -7,31 +7,30 @@
 // Test that the flex item outline renders the basis and final points as a single point
 // if their sizes are equal. If not, then render as separate points.
 
 const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
   info("Select a flex item whose basis size matches its final size.");
-  let onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline-container");
+  let onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(".item", inspector);
-  let [flexOutlineContainer] = await onFlexItemOutlineRendered;
+  await onUpdate;
 
-  const [basisFinalPoint] = [...flexOutlineContainer.querySelectorAll(
-    ".flex-outline-point.basisfinal")];
+  const [basisFinalPoint] = [...doc.querySelectorAll(".flex-outline-point.basisfinal")];
 
   ok(basisFinalPoint, "The basis/final point exists");
 
   info("Select a flex item whose basis size is different than its final size.");
-  onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline-container");
+  onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(".shrinking .item", inspector);
-  [flexOutlineContainer] = await onFlexItemOutlineRendered;
+  await onUpdate;
 
-  const [basis, final] = [...flexOutlineContainer.querySelectorAll(
+  const [basis, final] = [...doc.querySelectorAll(
     ".flex-outline-point.basis, .flex-outline-point.final")];
 
   ok(basis, "The basis point exists");
   ok(final, "The final point exists");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_rotates_for_column.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_rotates_for_column.js
@@ -24,16 +24,18 @@ add_task(async function() {
   // Check that the outline is wider than it is tall in the configuration.
   let bounds = flexOutline.getBoxQuads()[0].getBounds();
   ok(bounds.width > bounds.height, "The outline looks like a row");
 
   // Select a flex item in the column flexbox layout.
   onFlexItemOutlineRendered = waitForDOM(doc,
     ".flex-outline-container .flex-outline");
   await selectNode(".container.column .item", inspector);
-  ([flexOutline] = await onFlexItemOutlineRendered);
-
-  ok(flexOutline.classList.contains("column"), "The flex outline has the column class");
+  await waitUntil(() => {
+    flexOutline = doc.querySelector(".flex-outline-container .flex-outline.column");
+    return flexOutline;
+  });
+  ok(true, "The flex outline has the column class");
 
   // Check that the outline is taller than it is wide in the configuration.
   bounds = flexOutline.getBoxQuads()[0].getBounds();
   ok(bounds.height > bounds.width, "The outline looks like a column");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_non_flex_item_is_not_shown.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_non_flex_item_is_not_shown.js
@@ -16,13 +16,13 @@ const TEST_URI = `
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
 
   info("Select the container's supposed flex item.");
   await selectNode("#item", inspector);
   const noFlexContainerOrItemSelected =
-    doc.querySelector(".flexbox-pane .devtools-sidepanel-no-result");
+    doc.querySelector(".flex-accordion .devtools-sidepanel-no-result");
 
   ok(noFlexContainerOrItemSelected,
     "The flexbox pane shows a message to select a flex container or item to continue.");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_pseudo_elements_are_listed.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_pseudo_elements_are_listed.js
@@ -10,17 +10,17 @@ const TEST_URI = URL_ROOT + "doc_flexbox
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
 
   // Select the flex container in the inspector.
   const onItemsListRendered = waitForDOM(doc,
-    "#layout-flexbox-container .flex-item-list");
+    ".layout-flexbox-wrapper .flex-item-list");
   await selectNode(".container", inspector);
   const [flexItemList] = await onItemsListRendered;
 
   const items = [...flexItemList.querySelectorAll("button .objectBox")];
   is(items.length, 2, "There are 2 items displayed in the list");
 
   is(items[0].textContent, "::before", "The first item is ::before");
   is(items[1].textContent, "::after", "The second item is ::after");
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_flexibility_not_displayed_when_useless.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_flexibility_not_displayed_when_useless.js
@@ -7,26 +7,26 @@
 // Test that the flexibility section in the flex item sizing properties is not displayed
 // when the item did not grow or shrink.
 
 const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
   info("Select an item with flex:0 and wait for the sizing info to be rendered");
-  let onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+  let onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode("#did-not-grow-or-shrink div", inspector);
-  let [flexSizingContainer] = await onFlexItemSizingRendered;
+  await onUpdate;
 
-  let flexSections = flexSizingContainer.querySelectorAll(".section.flexibility");
+  let flexSections = doc.querySelectorAll(".flex-item-sizing .section.flexibility");
   is(flexSections.length, 0, "The flexibility section was not found in the DOM");
 
   info("Select a more complex item which also doesn't flex and wait for the sizing info");
-  onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+  onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode("#just-enough-space-for-clamped-items div:last-child", inspector);
-  ([flexSizingContainer] = await onFlexItemSizingRendered);
+  await onUpdate;
 
-  flexSections = flexSizingContainer.querySelectorAll(".section.flexibility");
+  flexSections = doc.querySelectorAll(".flex-item-sizing .section.flexibility");
   is(flexSections.length, 0, "The flexibility section was not found in the DOM");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_do_not_show_unspecified_min_dimension.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_do_not_show_unspecified_min_dimension.js
@@ -4,30 +4,31 @@ http://creativecommons.org/publicdomain/
 
 "use strict";
 
 // Test that a flex item's min width/height value is not displayed if it's unspecified in
 // the CSS.
 
 const TEST_URI = URL_ROOT + "doc_flexbox_unauthored_min_dimension.html";
 
-async function checkFlexItemCSSProperty(inspector, doc, selector) {
+async function checkFlexItemCSSProperty(inspector, store, doc, selector) {
   info("Select the container's flex item sizing info.");
-  const onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+  const onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(selector, inspector);
-  const [flexItemSizingContainer] = await onFlexItemSizingRendered;
+  await onUpdate;
 
   info("Check that the minimum size section does not display minimum dimension text.");
-  const [sectionMinRowItem] = [...flexItemSizingContainer.querySelectorAll(
-    ".section.min")];
+  const [sectionMinRowItem] = [...doc.querySelectorAll(".flex-item-sizing .section.min")];
   const minDimension = sectionMinRowItem.querySelector(".css-property-link");
 
   ok(!minDimension, "Minimum dimension property should not be displayed.");
 }
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
-  await checkFlexItemCSSProperty(inspector, doc, "#flex-item-with-unauthored-min-width");
-  await checkFlexItemCSSProperty(inspector, doc, "#flex-item-with-unauthored-min-height");
+  await checkFlexItemCSSProperty(inspector, store, doc,
+    "#flex-item-with-unauthored-min-width");
+  await checkFlexItemCSSProperty(inspector, store, doc,
+    "#flex-item-with-unauthored-min-height");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_for_different_writing_modes.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_for_different_writing_modes.js
@@ -6,33 +6,36 @@ http://creativecommons.org/publicdomain/
 
 // Test that the flex item sizing info shows the correct dimension values for different
 // writing modes. For vertical writing modes, row items should display height values and
 // column items should display width values. The opposite is true for horizontal mode
 // where rows display width values and columns display height.
 
 const TEST_URI = URL_ROOT + "doc_flexbox_writing_modes.html";
 
-async function checkFlexItemDimension(inspector, doc, selector, expectedDimension) {
+async function checkFlexItemDimension(inspector, store, doc, selector, expected) {
   info("Select the container's flex item.");
-  const onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+  const onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(selector, inspector);
-  const [flexItemSizingContainer] = await onFlexItemSizingRendered;
+  await onUpdate;
 
   info("Check that the minimum size section shows the correct dimension.");
-  const [sectionMinRowItem] = [...flexItemSizingContainer.querySelectorAll(
-    ".section.min")];
+  const [sectionMinRowItem] = [...doc.querySelectorAll(".flex-item-sizing .section.min")];
   const minDimension = sectionMinRowItem.querySelector(".css-property-link");
 
-  ok(minDimension.textContent.includes(expectedDimension),
+  ok(minDimension.textContent.includes(expected),
      "The flex item sizing has the correct dimension value.");
 }
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
-  await checkFlexItemDimension(inspector, doc, ".row.vertical.item", "min-height");
-  await checkFlexItemDimension(inspector, doc, ".column.vertical.item", "min-width");
-  await checkFlexItemDimension(inspector, doc, ".row.horizontal.item", "min-width");
-  await checkFlexItemDimension(inspector, doc, ".column.horizontal.item", "min-height");
+  await checkFlexItemDimension(inspector, store, doc,
+    ".row.vertical.item", "min-height");
+  await checkFlexItemDimension(inspector, store, doc,
+    ".column.vertical.item", "min-width");
+  await checkFlexItemDimension(inspector, store, doc,
+    ".row.horizontal.item", "min-width");
+  await checkFlexItemDimension(inspector, store, doc,
+    ".column.horizontal.item", "min-height");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_has_correct_sections.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_has_correct_sections.js
@@ -22,33 +22,34 @@ const TEST_DATA = [{
 }, {
   selector: ".growing.is-clamped .item",
   expectedSections: ["Base Size", "Flexibility", "Maximum Size", "Final Size"],
 }];
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
   for (const { selector, expectedSections } of TEST_DATA) {
     info(`Checking the list of sections for the flex item ${selector}`);
-    const sections = await selectNodeAndGetFlexSizingSections(selector, inspector, doc);
+    const sections = await selectNodeAndGetFlexSizingSections(
+      selector, store, inspector, doc);
 
     is(sections.length, expectedSections.length, "Correct number of sections found");
     expectedSections.forEach((expectedSection, i) => {
       ok(sections[i].includes(expectedSection),
          `The ${expectedSection} section was found`);
     });
   }
 });
 
-async function selectNodeAndGetFlexSizingSections(selector, inspector, doc) {
-  const onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+async function selectNodeAndGetFlexSizingSections(selector, store, inspector, doc) {
+  const onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(selector, inspector);
-  const [flexSizingContainer] = await onFlexItemSizingRendered;
+  await onUpdate;
 
   info(`Getting the list of displayed sections for ${selector}`);
-  const allSections = [...flexSizingContainer.querySelectorAll(".section .name")];
+  const allSections = [...doc.querySelectorAll("ul.flex-item-sizing .section .name")];
   const allSectionTitles = allSections.map(el => el.textContent);
 
   return allSectionTitles;
 }
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_text_nodes_are_listed.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_text_nodes_are_listed.js
@@ -10,17 +10,17 @@ const TEST_URI = URL_ROOT + "doc_flexbox
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
 
   // Select the flex container in the inspector.
   const onItemsListRendered = waitForDOM(doc,
-    "#layout-flexbox-container .flex-item-list");
+    ".layout-flexbox-wrapper .flex-item-list");
   await selectNode(".container", inspector);
   const [flexItemList] = await onItemsListRendered;
 
   const items = [...flexItemList.querySelectorAll("button .objectBox")];
   is(items.length, 3, "There are 3 items displayed in the list");
 
   is(items[0].textContent, "#text", "The first item is a text node");
   is(items[2].textContent, "#text", "The third item is a text node");
--- a/devtools/client/inspector/flexbox/test/doc_flexbox_specific_cases.html
+++ b/devtools/client/inspector/flexbox/test/doc_flexbox_specific_cases.html
@@ -109,8 +109,13 @@
 <div id="just-enough-space-for-clamped-items">
  <div></div>
  <div></div>
 </div>
 <div id="wanted-to-shrink-more-than-basis">
   <div>item wants to shrink more than its basis</div>
   <div></div>
 </div>
+<div class="container" id="container-only">
+  <div class="container" id="container-and-item">
+    <div>This item is inside a container-item element</div>
+  </div>
+</div>
--- a/devtools/client/inspector/flexbox/test/head.js
+++ b/devtools/client/inspector/flexbox/test/head.js
@@ -1,14 +1,16 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
-/* import-globals-from ../../../shared/test/telemetry-test-helpers.js */
 /* import-globals-from ../../test/head.js */
+/* import-globals-from ../../../inspector/rules/test/head.js */
+/* import-globals-from ../../../inspector/test/shared-head.js */
+/* import-globals-from ../../../shared/test/shared-redux-head.js */
 "use strict";
 
 // Import the inspector's head.js first (which itself imports shared-head.js).
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
 // Load the shared Redux helpers into this compartment.
--- a/devtools/client/inspector/layout/components/Accordion.js
+++ b/devtools/client/inspector/layout/components/Accordion.js
@@ -52,18 +52,19 @@ class Accordion extends PureComponent {
       item.onToggled();
     }
 
     this.setState({ opened, created });
   }
 
   renderContainer(item, i) {
     const { opened, created } = this.state;
-    const containerClassName =
-          item.header.toLowerCase().replace(/\s/g, "-") + "-pane";
+    const containerClassName = item.className
+      ? item.className
+      : item.header.toLowerCase().replace(/\s/g, "-") + "-pane";
     let arrowClassName = "arrow theme-twisty";
     if (opened[i]) {
       arrowClassName += " open";
     }
 
     return div(
       { className: containerClassName, key: i },
 
--- a/devtools/client/inspector/layout/components/LayoutApp.js
+++ b/devtools/client/inspector/layout/components/LayoutApp.js
@@ -108,52 +108,59 @@ class LayoutApp extends PureComponent {
         onToggled: () => {
           const opened = Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF);
           Services.prefs.setBoolPref(BOXMODEL_OPENED_PREF, !opened);
         },
       },
     ];
 
     if (Services.prefs.getBoolPref(FLEXBOX_ENABLED_PREF)) {
+      const { flexContainer, flexItemContainer } = this.props.flexbox;
+      const opened = Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
+
       // Since the flexbox panel is hidden behind a pref. We insert the flexbox container
       // to the first index of the accordion item list.
       items.splice(0, 0, {
+        className: `flex-accordion ${flexContainer.flexItemShown ? "item" : "container"}`,
         component: Flexbox,
         componentProps: {
           ...this.props,
-          flexContainer: this.props.flexbox.flexContainer,
+          flexContainer,
           scrollToTop: this.scrollToTop,
         },
-        header: this.getFlexboxHeader(this.props.flexbox.flexContainer),
-        opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
+        header: this.getFlexboxHeader(flexContainer),
+        opened,
         onToggled: () => {
-          const opened =  Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
-          Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened);
+          Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF,
+            !Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF));
         },
       });
 
       // If the current selected node is both a flex container and flex item. Render
-      // an accordion with another Flexbox component where the flexbox to show is the
-      // parent flex container of the current selected node.
-      if (this.props.flexbox.flexItemContainer &&
-          this.props.flexbox.flexItemContainer.actorID) {
-        // Insert the parent flex container to the first index of the accordion item
-        // list.
-        items.splice(0, 0, {
+      // an extra accordion with another Flexbox component where the node is shown as an
+      // item of its parent flex container.
+      // If the node was selected from the markup-view, then show this accordion after the
+      // container accordion. Otherwise show it first.
+      // The reason is that if the user selects an item-container in the markup view, it
+      // is assumed that they want to primarily see that element as a container, so the
+      // container info should be at the top.
+      if (flexItemContainer && flexItemContainer.actorID) {
+        items.splice(this.props.flexbox.initiatedByMarkupViewSelection ? 1 : 0, 0, {
+          className: "flex-accordion item",
           component: Flexbox,
           componentProps: {
             ...this.props,
-            flexContainer: this.props.flexbox.flexItemContainer,
+            flexContainer: flexItemContainer,
             scrollToTop: this.scrollToTop,
           },
-          header: this.getFlexboxHeader(this.props.flexbox.flexItemContainer),
-          opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
+          header: this.getFlexboxHeader(flexItemContainer),
+          opened,
           onToggled: () => {
-            const opened =  Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
-            Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened);
+            Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF,
+              !Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF));
           },
         });
       }
     }
 
     return (
       dom.div({ className: "layout-container", ref: this.containerRef },
         Accordion({ items })