зеркало из 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,
|
||||
getContext,
|
||||
getMainThread,
|
||||
getSourceContent,
|
||||
} from "../../selectors";
|
||||
import actions from "../../actions";
|
||||
|
||||
|
@ -31,9 +32,16 @@ import {
|
|||
import { isDirectory } from "../../utils/sources-tree";
|
||||
import { copyToTheClipboard } from "../../utils/clipboard";
|
||||
import { features } from "../../utils/prefs";
|
||||
import { downloadFile } from "../../utils/utils";
|
||||
|
||||
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 = {
|
||||
autoExpand: ?boolean,
|
||||
|
@ -42,6 +50,7 @@ type Props = {
|
|||
projectRoot: string,
|
||||
source: ?Source,
|
||||
item: TreeNode,
|
||||
sourceContent: SourceContent,
|
||||
depth: number,
|
||||
focused: boolean,
|
||||
expanded: boolean,
|
||||
|
@ -56,6 +65,7 @@ type Props = {
|
|||
clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
|
||||
setProjectDirectoryRoot: typeof actions.setProjectDirectoryRoot,
|
||||
toggleBlackBox: typeof actions.toggleBlackBox,
|
||||
loadSourceText: typeof actions.loadSourceText,
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
@ -122,7 +132,14 @@ class SourceTreeItem extends Component<Props, State> {
|
|||
disabled: !shouldBlackbox(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);
|
||||
};
|
||||
|
||||
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) => {
|
||||
const { setExpanded } = this.props;
|
||||
|
||||
|
@ -310,6 +336,11 @@ function getHasMatchingGeneratedSource(state, source: ?Source) {
|
|||
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 { source } = props;
|
||||
return {
|
||||
|
@ -318,6 +349,7 @@ const mapStateToProps = (state, props) => {
|
|||
hasMatchingGeneratedSource: getHasMatchingGeneratedSource(state, source),
|
||||
hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
|
||||
hasPrettySource: source ? checkHasPrettySource(state, source.id) : false,
|
||||
sourceContent: source ? getSourceContentValue(state, source) : false,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -327,5 +359,6 @@ export default connect(
|
|||
setProjectDirectoryRoot: actions.setProjectDirectoryRoot,
|
||||
clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot,
|
||||
toggleBlackBox: actions.toggleBlackBox,
|
||||
loadSourceText: actions.loadSourceText,
|
||||
}
|
||||
)(SourceTreeItem);
|
||||
|
|
|
@ -91,6 +91,13 @@ describe("SourceTreeItem", () => {
|
|||
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(),
|
||||
|
@ -104,7 +111,6 @@ describe("SourceTreeItem", () => {
|
|||
await instance.onContextMenu(mockEvent, item, source);
|
||||
|
||||
expect(showMenu).toHaveBeenCalledWith(mockEvent, menuOptions);
|
||||
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||
|
||||
|
@ -130,6 +136,13 @@ describe("SourceTreeItem", () => {
|
|||
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(),
|
||||
|
@ -153,6 +166,57 @@ describe("SourceTreeItem", () => {
|
|||
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 () => {
|
||||
const menuOptions = [
|
||||
{
|
||||
|
|
|
@ -38,6 +38,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -263,6 +264,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -494,6 +496,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -708,6 +711,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -880,6 +884,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -1055,6 +1060,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -1198,6 +1204,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -1367,6 +1374,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -1504,6 +1512,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -1647,6 +1656,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -1819,6 +1829,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -2000,6 +2011,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -2223,6 +2235,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
@ -2449,6 +2462,7 @@ Object {
|
|||
"instance": SourceTreeItem {
|
||||
"addCollapseExpandAllOptions": [Function],
|
||||
"context": Object {},
|
||||
"handleDownloadFile": [Function],
|
||||
"onClick": [Function],
|
||||
"onContextMenu": [Function],
|
||||
"props": Object {
|
||||
|
|
Загрузка…
Ссылка в новой задаче