Skip to content

Commit 69d9a04

Browse files
Regaddidarkwing
authored andcommitted
Fixes firefox-devtools#7656 - Remove absolute positioning in directory view of sources pane (firefox-devtools#7691)
1 parent 2864f9c commit 69d9a04

File tree

8 files changed

+241
-163
lines changed

8 files changed

+241
-163
lines changed

jest-test.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module.exports = {
2626
"!**/*.mock.js",
2727
"!**/*.spec.js"
2828
],
29-
transformIgnorePatterns: ["node_modules/(?!devtools-)"],
29+
transformIgnorePatterns: ["node_modules/(?!(devtools-|react-aria-))"],
3030
setupTestFrameworkScriptFile: "<rootDir>/src/test/tests-setup.js",
3131
setupFiles: ["<rootDir>/src/test/shim.js", "jest-localstorage-mock"],
3232
snapshotSerializers: [

src/components/PrimaryPanes/Sources.css

+4-5
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@
2323
white-space: nowrap;
2424
color: inherit;
2525
display: block;
26-
position: absolute;
27-
top: 0;
28-
left: 0;
2926
border-bottom: 1px solid var(--theme-splitter-color);
3027
}
3128

@@ -62,7 +59,6 @@
6259
display: flex;
6360
flex: 1;
6461
flex-direction: column;
65-
height: 100%;
6662
}
6763

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

207+
.source-outline-panel.has-root .thread-header {
208+
margin-top: 4px;
209+
}
210+
211211
.sources-list .managed-tree .tree .node .img.blackBox {
212212
mask: url(/images/blackBox.svg) no-repeat;
213213
mask-size: 100%;
@@ -236,7 +236,6 @@
236236
display: block;
237237
}
238238

239-
.sources-list-custom-root .sources-list,
240239
.sources-list-custom-root .no-sources-message {
241240
position: absolute;
242241
top: 26px;

src/components/PrimaryPanes/SourcesTree.js

+2-43
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import actions from "../../actions";
3232
import AccessibleImage from "../shared/AccessibleImage";
3333
import SourcesTreeItem from "./SourcesTreeItem";
3434
import ManagedTree from "../shared/ManagedTree";
35-
import Svg from "../shared/Svg";
3635

3736
// Utils
3837
import {
@@ -69,7 +68,6 @@ type Props = {
6968
expanded: Set<string>,
7069
selectSource: typeof actions.selectSource,
7170
setExpandedState: typeof actions.setExpandedState,
72-
clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
7371
focusItem: typeof actions.focusItem,
7472
focused: TreeNode,
7573
workerCount: number
@@ -215,30 +213,6 @@ class SourcesTree extends Component<Props, State> {
215213
);
216214
}
217215

218-
renderProjectRootHeader() {
219-
const { projectRoot } = this.props;
220-
221-
if (!projectRoot) {
222-
return null;
223-
}
224-
225-
const rootLabel = projectRoot.split("/").pop();
226-
227-
return (
228-
<div key="root" className="sources-clear-root-container">
229-
<button
230-
className="sources-clear-root"
231-
onClick={() => this.props.clearProjectDirectoryRoot()}
232-
title={L10N.getStr("removeDirectoryRoot.label")}
233-
>
234-
<Svg name="home" />
235-
<Svg name="breadcrumb" />
236-
<span className="sources-clear-root-label">{rootLabel}</span>
237-
</button>
238-
</div>
239-
);
240-
}
241-
242216
getRoots = () => {
243217
const { projectRoot } = this.props;
244218
const { sourceTree } = this.state;
@@ -248,7 +222,7 @@ class SourcesTree extends Component<Props, State> {
248222

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

352326
render() {
353-
const { projectRoot, worker } = this.props;
327+
const { worker } = this.props;
354328

355329
if (!features.windowlessWorkers && worker) {
356330
return null;
357331
}
358332

359-
if (this.isEmpty()) {
360-
if (projectRoot) {
361-
return this.renderPane(
362-
this.renderProjectRootHeader(),
363-
this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailableRoot"))
364-
);
365-
}
366-
367-
return this.renderPane(
368-
this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailable"))
369-
);
370-
}
371-
372333
return this.renderPane(
373334
this.renderThreadHeader(),
374-
this.renderProjectRootHeader(),
375335
<div key="tree" className="sources-list" onKeyDown={this.onKeyDown}>
376336
{this.renderTree()}
377337
</div>
@@ -420,7 +380,6 @@ export default connect(
420380
{
421381
selectSource: actions.selectSource,
422382
setExpandedState: actions.setExpandedState,
423-
clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot,
424383
focusItem: actions.focusItem
425384
}
426385
)(SourcesTree);

src/components/PrimaryPanes/index.js

+33-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import type { SourcesMapByThread } from "../../reducers/types";
2727
import type { SelectedPrimaryPaneTabType } from "../../selectors";
2828
import type { Thread } from "../../types";
2929

30+
import Svg from "../shared/Svg";
31+
3032
import "./Sources.css";
3133

3234
type State = {
@@ -42,6 +44,7 @@ type Props = {
4244
setPrimaryPaneTab: typeof actions.setPrimaryPaneTab,
4345
setActiveSearch: typeof actions.setActiveSearch,
4446
closeActiveSearch: typeof actions.closeActiveSearch,
47+
clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
4548
threads: Thread[]
4649
};
4750

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

104+
renderProjectRootHeader() {
105+
const { projectRoot } = this.props;
106+
107+
if (!projectRoot) {
108+
return null;
109+
}
110+
111+
const rootLabel = projectRoot.split("/").pop();
112+
113+
return (
114+
<div key="root" className="sources-clear-root-container">
115+
<button
116+
className="sources-clear-root"
117+
onClick={() => this.props.clearProjectDirectoryRoot()}
118+
title={L10N.getStr("removeDirectoryRoot.label")}
119+
>
120+
<Svg name="home" />
121+
<Svg name="breadcrumb" />
122+
<span className="sources-clear-root-label">{rootLabel}</span>
123+
</button>
124+
</div>
125+
);
126+
}
127+
101128
renderThreadSources() {
102129
return this.props.threads.map(({ actor }) => (
103130
<SourcesTree thread={actor} key={actor} />
@@ -123,7 +150,10 @@ class PrimaryPanes extends Component<Props, State> {
123150
})}
124151
hasFocusableContent
125152
>
126-
<div>{this.renderThreadSources()}</div>
153+
<div>
154+
{this.renderProjectRootHeader()}
155+
{this.renderThreadSources()}
156+
</div>
127157
<Outline
128158
alphabetizeOutline={this.state.alphabetizeOutline}
129159
onAlphabetizeClick={this.onAlphabetizeClick}
@@ -147,7 +177,8 @@ const connector = connect(
147177
{
148178
setPrimaryPaneTab: actions.setPrimaryPaneTab,
149179
setActiveSearch: actions.setActiveSearch,
150-
closeActiveSearch: actions.closeActiveSearch
180+
closeActiveSearch: actions.closeActiveSearch,
181+
clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot
151182
}
152183
);
153184

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
4+
5+
import React from "react";
6+
import { shallow } from "enzyme";
7+
import { showMenu } from "devtools-contextmenu";
8+
9+
import { copyToTheClipboard } from "../../../utils/clipboard";
10+
import PrimaryPanes from "..";
11+
12+
jest.mock("devtools-contextmenu", () => ({ showMenu: jest.fn() }));
13+
jest.mock("../../../utils/clipboard", () => ({
14+
copyToTheClipboard: jest.fn()
15+
}));
16+
17+
describe("PrimaryPanes", () => {
18+
afterEach(() => {
19+
copyToTheClipboard.mockClear();
20+
showMenu.mockClear();
21+
});
22+
23+
describe("with custom root", () => {
24+
it("renders custom root source list", async () => {
25+
const { component } = render({
26+
projectRoot: "mdn.com"
27+
});
28+
expect(component).toMatchSnapshot();
29+
});
30+
31+
it("calls clearProjectDirectoryRoot on click", async () => {
32+
const { component, props } = render({
33+
projectRoot: "mdn"
34+
});
35+
component.find(".sources-clear-root").simulate("click");
36+
expect(props.clearProjectDirectoryRoot).toHaveBeenCalled();
37+
});
38+
39+
it("renders empty custom root source list", async () => {
40+
const { component } = render({
41+
projectRoot: "custom",
42+
sources: {}
43+
});
44+
expect(component).toMatchSnapshot();
45+
});
46+
});
47+
});
48+
49+
function generateDefaults(overrides) {
50+
return {
51+
horizontal: false,
52+
projectRoot: "",
53+
sourceSearchOn: false,
54+
setPrimaryPaneTab: jest.fn(),
55+
setActiveSearch: jest.fn(),
56+
closeActiveSearch: jest.fn(),
57+
clearProjectDirectoryRoot: jest.fn(),
58+
threads: [],
59+
...overrides
60+
};
61+
}
62+
63+
function render(overrides = {}) {
64+
const props = generateDefaults(overrides);
65+
const component = shallow(<PrimaryPanes.WrappedComponent {...props} />);
66+
const defaultState = component.state();
67+
const instance = component.instance();
68+
69+
instance.shouldComponentUpdate = () => true;
70+
71+
return { component, props, defaultState, instance };
72+
}

src/components/PrimaryPanes/tests/SourcesTree.spec.js

-25
Original file line numberDiff line numberDiff line change
@@ -198,31 +198,6 @@ describe("SourcesTree", () => {
198198
});
199199
});
200200

201-
describe("with custom root", () => {
202-
it("renders custom root source list", async () => {
203-
const { component } = render({
204-
projectRoot: "mdn.com"
205-
});
206-
expect(component).toMatchSnapshot();
207-
});
208-
209-
it("calls clearProjectDirectoryRoot on click", async () => {
210-
const { component, props } = render({
211-
projectRoot: "mdn"
212-
});
213-
component.find(".sources-clear-root").simulate("click");
214-
expect(props.clearProjectDirectoryRoot).toHaveBeenCalled();
215-
});
216-
217-
it("renders empty custom root source list", async () => {
218-
const { component } = render({
219-
projectRoot: "custom",
220-
sources: {}
221-
});
222-
expect(component).toMatchSnapshot();
223-
});
224-
});
225-
226201
describe("selectItem", () => {
227202
it("should select item with no children", async () => {
228203
const { instance, props } = render();

0 commit comments

Comments
 (0)