зеркало из https://github.com/mozilla/gecko-dev.git
Bug 888161 - Adds download file option to the context menu of source tree items r=davidwalsh
This patch addresses bug 888161 by adding a ‘Download File’ option to the context menu of individual source tree item files. Differential Revision: https://phabricator.services.mozilla.com/D32828 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
5da02d9c69
Коммит
0e820e6a25
|
@ -19,6 +19,7 @@ import {
|
||||||
hasPrettySource as checkHasPrettySource,
|
hasPrettySource as checkHasPrettySource,
|
||||||
getContext,
|
getContext,
|
||||||
getMainThread,
|
getMainThread,
|
||||||
|
getSourceContent,
|
||||||
} from "../../selectors";
|
} from "../../selectors";
|
||||||
import actions from "../../actions";
|
import actions from "../../actions";
|
||||||
|
|
||||||
|
@ -31,9 +32,16 @@ import {
|
||||||
import { isDirectory } from "../../utils/sources-tree";
|
import { isDirectory } from "../../utils/sources-tree";
|
||||||
import { copyToTheClipboard } from "../../utils/clipboard";
|
import { copyToTheClipboard } from "../../utils/clipboard";
|
||||||
import { features } from "../../utils/prefs";
|
import { features } from "../../utils/prefs";
|
||||||
|
import { downloadFile } from "../../utils/utils";
|
||||||
|
|
||||||
import type { TreeNode } from "../../utils/sources-tree/types";
|
import type { TreeNode } from "../../utils/sources-tree/types";
|
||||||
import type { Source, Context, MainThread, Thread } from "../../types";
|
import type {
|
||||||
|
Source,
|
||||||
|
Context,
|
||||||
|
MainThread,
|
||||||
|
Thread,
|
||||||
|
SourceContent,
|
||||||
|
} from "../../types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
autoExpand: ?boolean,
|
autoExpand: ?boolean,
|
||||||
|
@ -42,6 +50,7 @@ type Props = {
|
||||||
projectRoot: string,
|
projectRoot: string,
|
||||||
source: ?Source,
|
source: ?Source,
|
||||||
item: TreeNode,
|
item: TreeNode,
|
||||||
|
sourceContent: SourceContent,
|
||||||
depth: number,
|
depth: number,
|
||||||
focused: boolean,
|
focused: boolean,
|
||||||
expanded: boolean,
|
expanded: boolean,
|
||||||
|
@ -56,6 +65,7 @@ type Props = {
|
||||||
clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
|
clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
|
||||||
setProjectDirectoryRoot: typeof actions.setProjectDirectoryRoot,
|
setProjectDirectoryRoot: typeof actions.setProjectDirectoryRoot,
|
||||||
toggleBlackBox: typeof actions.toggleBlackBox,
|
toggleBlackBox: typeof actions.toggleBlackBox,
|
||||||
|
loadSourceText: typeof actions.loadSourceText,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {};
|
type State = {};
|
||||||
|
@ -122,7 +132,14 @@ class SourceTreeItem extends Component<Props, State> {
|
||||||
disabled: !shouldBlackbox(source),
|
disabled: !shouldBlackbox(source),
|
||||||
click: () => this.props.toggleBlackBox(cx, source),
|
click: () => this.props.toggleBlackBox(cx, source),
|
||||||
};
|
};
|
||||||
menuOptions.push(copySourceUri2, blackBoxMenuItem);
|
const downloadFileItem = {
|
||||||
|
id: "node-menu-download-file",
|
||||||
|
label: L10N.getStr("downloadFile.label"),
|
||||||
|
accesskey: L10N.getStr("downloadFile.accesskey"),
|
||||||
|
disabled: false,
|
||||||
|
click: () => this.handleDownloadFile(cx, source, item),
|
||||||
|
};
|
||||||
|
menuOptions.push(copySourceUri2, blackBoxMenuItem, downloadFileItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,6 +173,15 @@ class SourceTreeItem extends Component<Props, State> {
|
||||||
showMenu(event, menuOptions);
|
showMenu(event, menuOptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleDownloadFile = async (cx: Context, source: ?Source, item: TreeNode) => {
|
||||||
|
const name = item.name;
|
||||||
|
if (!this.props.sourceContent) {
|
||||||
|
await this.props.loadSourceText({ cx, source });
|
||||||
|
}
|
||||||
|
const data = this.props.sourceContent;
|
||||||
|
downloadFile(data, name);
|
||||||
|
};
|
||||||
|
|
||||||
addCollapseExpandAllOptions = (menuOptions: ContextMenu, item: TreeNode) => {
|
addCollapseExpandAllOptions = (menuOptions: ContextMenu, item: TreeNode) => {
|
||||||
const { setExpanded } = this.props;
|
const { setExpanded } = this.props;
|
||||||
|
|
||||||
|
@ -310,6 +336,11 @@ function getHasMatchingGeneratedSource(state, source: ?Source) {
|
||||||
return !!getGeneratedSourceByURL(state, source.url);
|
return !!getGeneratedSourceByURL(state, source.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSourceContentValue(state, source: Source) {
|
||||||
|
const content = getSourceContent(state, source.id);
|
||||||
|
return content !== null ? content.value : false;
|
||||||
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const { source } = props;
|
const { source } = props;
|
||||||
return {
|
return {
|
||||||
|
@ -318,6 +349,7 @@ const mapStateToProps = (state, props) => {
|
||||||
hasMatchingGeneratedSource: getHasMatchingGeneratedSource(state, source),
|
hasMatchingGeneratedSource: getHasMatchingGeneratedSource(state, source),
|
||||||
hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
|
hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
|
||||||
hasPrettySource: source ? checkHasPrettySource(state, source.id) : false,
|
hasPrettySource: source ? checkHasPrettySource(state, source.id) : false,
|
||||||
|
sourceContent: source ? getSourceContentValue(state, source) : false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -327,5 +359,6 @@ export default connect(
|
||||||
setProjectDirectoryRoot: actions.setProjectDirectoryRoot,
|
setProjectDirectoryRoot: actions.setProjectDirectoryRoot,
|
||||||
clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot,
|
clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot,
|
||||||
toggleBlackBox: actions.toggleBlackBox,
|
toggleBlackBox: actions.toggleBlackBox,
|
||||||
|
loadSourceText: actions.loadSourceText,
|
||||||
}
|
}
|
||||||
)(SourceTreeItem);
|
)(SourceTreeItem);
|
||||||
|
|
|
@ -91,6 +91,13 @@ describe("SourceTreeItem", () => {
|
||||||
id: "node-menu-blackbox",
|
id: "node-menu-blackbox",
|
||||||
label: "Blackbox source",
|
label: "Blackbox source",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accesskey: "d",
|
||||||
|
click: expect.any(Function),
|
||||||
|
disabled: false,
|
||||||
|
id: "node-menu-download-file",
|
||||||
|
label: "Download file",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const mockEvent = {
|
const mockEvent = {
|
||||||
preventDefault: jest.fn(),
|
preventDefault: jest.fn(),
|
||||||
|
@ -104,7 +111,6 @@ describe("SourceTreeItem", () => {
|
||||||
await instance.onContextMenu(mockEvent, item, source);
|
await instance.onContextMenu(mockEvent, item, source);
|
||||||
|
|
||||||
expect(showMenu).toHaveBeenCalledWith(mockEvent, menuOptions);
|
expect(showMenu).toHaveBeenCalledWith(mockEvent, menuOptions);
|
||||||
|
|
||||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||||
|
|
||||||
|
@ -130,6 +136,13 @@ describe("SourceTreeItem", () => {
|
||||||
id: "node-menu-blackbox",
|
id: "node-menu-blackbox",
|
||||||
label: "Blackbox source",
|
label: "Blackbox source",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accesskey: "d",
|
||||||
|
click: expect.any(Function),
|
||||||
|
disabled: false,
|
||||||
|
id: "node-menu-download-file",
|
||||||
|
label: "Download file",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const mockEvent = {
|
const mockEvent = {
|
||||||
preventDefault: jest.fn(),
|
preventDefault: jest.fn(),
|
||||||
|
@ -153,6 +166,57 @@ describe("SourceTreeItem", () => {
|
||||||
expect(props.toggleBlackBox).toHaveBeenCalled();
|
expect(props.toggleBlackBox).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows context menu on file to download source file", async () => {
|
||||||
|
const menuOptions = [
|
||||||
|
{
|
||||||
|
accesskey: "u",
|
||||||
|
click: expect.any(Function),
|
||||||
|
disabled: false,
|
||||||
|
id: "node-menu-copy-source",
|
||||||
|
label: "Copy source URI",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accesskey: "B",
|
||||||
|
click: expect.any(Function),
|
||||||
|
disabled: false,
|
||||||
|
id: "node-menu-blackbox",
|
||||||
|
label: "Blackbox source",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accesskey: "d",
|
||||||
|
click: expect.any(Function),
|
||||||
|
disabled: false,
|
||||||
|
id: "node-menu-download-file",
|
||||||
|
label: "Download file",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const mockEvent = {
|
||||||
|
preventDefault: jest.fn(),
|
||||||
|
stopPropagation: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { props, instance } = render({
|
||||||
|
projectRoot: "root/",
|
||||||
|
});
|
||||||
|
const { item, source } = instance.props;
|
||||||
|
|
||||||
|
instance.handleDownloadFile = jest.fn(() => {});
|
||||||
|
|
||||||
|
await instance.onContextMenu(mockEvent, item, source);
|
||||||
|
|
||||||
|
expect(showMenu).toHaveBeenCalledWith(mockEvent, menuOptions);
|
||||||
|
|
||||||
|
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||||
|
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||||
|
|
||||||
|
showMenu.mock.calls[0][1][2].click();
|
||||||
|
expect(props.setProjectDirectoryRoot).not.toHaveBeenCalled();
|
||||||
|
expect(props.clearProjectDirectoryRoot).not.toHaveBeenCalled();
|
||||||
|
expect(props.toggleBlackBox).not.toHaveBeenCalled();
|
||||||
|
expect(copyToTheClipboard).not.toHaveBeenCalled();
|
||||||
|
expect(instance.handleDownloadFile).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("shows context menu on root to remove directory root", async () => {
|
it("shows context menu on root to remove directory root", async () => {
|
||||||
const menuOptions = [
|
const menuOptions = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,6 +38,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -263,6 +264,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -494,6 +496,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -708,6 +711,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -880,6 +884,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -1055,6 +1060,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -1198,6 +1204,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -1367,6 +1374,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -1504,6 +1512,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -1647,6 +1656,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -1819,6 +1829,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -2000,6 +2011,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -2223,6 +2235,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
@ -2449,6 +2462,7 @@ Object {
|
||||||
"instance": SourceTreeItem {
|
"instance": SourceTreeItem {
|
||||||
"addCollapseExpandAllOptions": [Function],
|
"addCollapseExpandAllOptions": [Function],
|
||||||
"context": Object {},
|
"context": Object {},
|
||||||
|
"handleDownloadFile": [Function],
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
"onContextMenu": [Function],
|
"onContextMenu": [Function],
|
||||||
"props": Object {
|
"props": Object {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче