Commit 46d7c99b authored by Christian Stuff's avatar Christian Stuff Committed by Jason Laster
Browse files

Bug 1520957 - [release 119] Fixes #7656 - Remove absolute positioning in...

Bug 1520957 - [release 119] Fixes #7656 - Remove absolute positioning in directory view of sources pane (#7691). r=dwalsh
parent 623a5286
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -2052,9 +2052,6 @@ menuseparator {
  white-space: nowrap;
  color: inherit;
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  border-bottom: 1px solid var(--theme-splitter-color);
}

@@ -2091,7 +2088,6 @@ menuseparator {
  display: flex;
  flex: 1;
  flex-direction: column;
  height: 100%;
}

.sources-list {
@@ -2237,6 +2233,10 @@ menuseparator {
  height: 100%;
}

.source-outline-panel.has-root .thread-header {
  margin-top: 4px;
}

.sources-list .managed-tree .tree .node .img.blackBox {
  mask: url("resource://devtools/client/debugger/new/images/blackBox.svg") no-repeat;
  mask-size: 100%;
@@ -2265,7 +2265,6 @@ menuseparator {
  display: block;
}

.sources-list-custom-root .sources-list,
.sources-list-custom-root .no-sources-message {
  position: absolute;
  top: 26px;
+4 −5
Original line number Diff line number Diff line
@@ -23,9 +23,6 @@
  white-space: nowrap;
  color: inherit;
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  border-bottom: 1px solid var(--theme-splitter-color);
}

@@ -62,7 +59,6 @@
  display: flex;
  flex: 1;
  flex-direction: column;
  height: 100%;
}

.sources-list {
@@ -208,6 +204,10 @@
  height: 100%;
}

.source-outline-panel.has-root .thread-header {
  margin-top: 4px;
}

.sources-list .managed-tree .tree .node .img.blackBox {
  mask: url(/images/blackBox.svg) no-repeat;
  mask-size: 100%;
@@ -236,7 +236,6 @@
  display: block;
}

.sources-list-custom-root .sources-list,
.sources-list-custom-root .no-sources-message {
  position: absolute;
  top: 26px;
+2 −43
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import actions from "../../actions";
import AccessibleImage from "../shared/AccessibleImage";
import SourcesTreeItem from "./SourcesTreeItem";
import ManagedTree from "../shared/ManagedTree";
import Svg from "../shared/Svg";

// Utils
import {
@@ -69,7 +68,6 @@ type Props = {
  expanded: Set<string>,
  selectSource: typeof actions.selectSource,
  setExpandedState: typeof actions.setExpandedState,
  clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
  focusItem: typeof actions.focusItem,
  focused: TreeNode,
  workerCount: number
@@ -216,30 +214,6 @@ class SourcesTree extends Component<Props, State> {
    );
  }

  renderProjectRootHeader() {
    const { projectRoot } = this.props;

    if (!projectRoot) {
      return null;
    }

    const rootLabel = projectRoot.split("/").pop();

    return (
      <div key="root" className="sources-clear-root-container">
        <button
          className="sources-clear-root"
          onClick={() => this.props.clearProjectDirectoryRoot()}
          title={L10N.getStr("removeDirectoryRoot.label")}
        >
          <Svg name="home" />
          <Svg name="breadcrumb" />
          <span className="sources-clear-root-label">{rootLabel}</span>
        </button>
      </div>
    );
  }

  getRoots = () => {
    const { projectRoot } = this.props;
    const { sourceTree } = this.state;
@@ -249,7 +223,7 @@ class SourcesTree extends Component<Props, State> {

    // The "sourceTree.contents[0]" check ensures that there are contents
    // A custom root with no existing sources will be ignored
    if (projectRoot) {
    if (projectRoot && sourceContents) {
      if (sourceContents && sourceContents.name !== rootLabel) {
        return sourceContents.contents[0].contents;
      }
@@ -351,28 +325,14 @@ class SourcesTree extends Component<Props, State> {
  }

  render() {
    const { projectRoot, worker } = this.props;
    const { worker } = this.props;

    if (!features.windowlessWorkers && worker) {
      return null;
    }

    if (this.isEmpty()) {
      if (projectRoot) {
        return this.renderPane(
          this.renderProjectRootHeader(),
          this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailableRoot"))
        );
      }

      return this.renderPane(
        this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailable"))
      );
    }

    return this.renderPane(
      this.renderThreadHeader(),
      this.renderProjectRootHeader(),
      <div key="tree" className="sources-list" onKeyDown={this.onKeyDown}>
        {this.renderTree()}
      </div>
@@ -421,7 +381,6 @@ export default connect(
  {
    selectSource: actions.selectSource,
    setExpandedState: actions.setExpandedState,
    clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot,
    focusItem: actions.focusItem
  }
)(SourcesTree);
+33 −2
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import type { SourcesMapByThread } from "../../reducers/types";
import type { SelectedPrimaryPaneTabType } from "../../selectors";
import type { Thread } from "../../types";

import Svg from "../shared/Svg";

import "./Sources.css";

type State = {
@@ -42,6 +44,7 @@ type Props = {
  setPrimaryPaneTab: typeof actions.setPrimaryPaneTab,
  setActiveSearch: typeof actions.setActiveSearch,
  closeActiveSearch: typeof actions.closeActiveSearch,
  clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
  threads: Thread[]
};

@@ -98,6 +101,30 @@ class PrimaryPanes extends Component<Props, State> {
    ];
  }

  renderProjectRootHeader() {
    const { projectRoot } = this.props;

    if (!projectRoot) {
      return null;
    }

    const rootLabel = projectRoot.split("/").pop();

    return (
      <div key="root" className="sources-clear-root-container">
        <button
          className="sources-clear-root"
          onClick={() => this.props.clearProjectDirectoryRoot()}
          title={L10N.getStr("removeDirectoryRoot.label")}
        >
          <Svg name="home" />
          <Svg name="breadcrumb" />
          <span className="sources-clear-root-label">{rootLabel}</span>
        </button>
      </div>
    );
  }

  renderThreadSources() {
    return this.props.threads.map(({ actor }) => (
      <SourcesTree thread={actor} key={actor} />
@@ -123,7 +150,10 @@ class PrimaryPanes extends Component<Props, State> {
          })}
          hasFocusableContent
        >
          <div>{this.renderThreadSources()}</div>
          <div>
            {this.renderProjectRootHeader()}
            {this.renderThreadSources()}
          </div>
          <Outline
            alphabetizeOutline={this.state.alphabetizeOutline}
            onAlphabetizeClick={this.onAlphabetizeClick}
@@ -147,7 +177,8 @@ const connector = connect(
  {
    setPrimaryPaneTab: actions.setPrimaryPaneTab,
    setActiveSearch: actions.setActiveSearch,
    closeActiveSearch: actions.closeActiveSearch
    closeActiveSearch: actions.closeActiveSearch,
    clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot
  }
);

+72 −0
Original line number Diff line number Diff line
/* 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/>. */

import React from "react";
import { shallow } from "enzyme";
import { showMenu } from "devtools-contextmenu";

import { copyToTheClipboard } from "../../../utils/clipboard";
import PrimaryPanes from "..";

jest.mock("devtools-contextmenu", () => ({ showMenu: jest.fn() }));
jest.mock("../../../utils/clipboard", () => ({
  copyToTheClipboard: jest.fn()
}));

describe("PrimaryPanes", () => {
  afterEach(() => {
    copyToTheClipboard.mockClear();
    showMenu.mockClear();
  });

  describe("with custom root", () => {
    it("renders custom root source list", async () => {
      const { component } = render({
        projectRoot: "mdn.com"
      });
      expect(component).toMatchSnapshot();
    });

    it("calls clearProjectDirectoryRoot on click", async () => {
      const { component, props } = render({
        projectRoot: "mdn"
      });
      component.find(".sources-clear-root").simulate("click");
      expect(props.clearProjectDirectoryRoot).toHaveBeenCalled();
    });

    it("renders empty custom root source list", async () => {
      const { component } = render({
        projectRoot: "custom",
        sources: {}
      });
      expect(component).toMatchSnapshot();
    });
  });
});

function generateDefaults(overrides) {
  return {
    horizontal: false,
    projectRoot: "",
    sourceSearchOn: false,
    setPrimaryPaneTab: jest.fn(),
    setActiveSearch: jest.fn(),
    closeActiveSearch: jest.fn(),
    clearProjectDirectoryRoot: jest.fn(),
    threads: [],
    ...overrides
  };
}

function render(overrides = {}) {
  const props = generateDefaults(overrides);
  const component = shallow(<PrimaryPanes.WrappedComponent {...props} />);
  const defaultState = component.state();
  const instance = component.instance();

  instance.shouldComponentUpdate = () => true;

  return { component, props, defaultState, instance };
}
Loading