Migrate Load Query Pane to React (#579)
Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
This commit is contained in:
parent
36f8fc1d22
commit
2bf9313951
|
@ -126,7 +126,6 @@ src/Explorer/Panes/DeleteCollectionConfirmationPane.ts
|
|||
src/Explorer/Panes/DeleteDatabaseConfirmationPane.test.ts
|
||||
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
||||
src/Explorer/Panes/GraphStylingPane.ts
|
||||
src/Explorer/Panes/LoadQueryPane.ts
|
||||
src/Explorer/Panes/NewVertexPane.ts
|
||||
src/Explorer/Panes/PaneComponents.ts
|
||||
src/Explorer/Panes/RenewAdHocAccessPane.ts
|
||||
|
|
|
@ -14,7 +14,6 @@ import DatabaseSettingsTab from "./Tabs/DatabaseSettingsTab";
|
|||
import DocumentsTab from "./Tabs/DocumentsTab";
|
||||
import GalleryTab from "./Tabs/GalleryTab";
|
||||
import GraphTab from "./Tabs/GraphTab";
|
||||
import MongoDocumentsTab from "./Tabs/MongoDocumentsTab";
|
||||
import MongoShellTab from "./Tabs/MongoShellTab";
|
||||
import NotebookTabV2 from "./Tabs/NotebookV2Tab";
|
||||
import NotebookViewerTab from "./Tabs/NotebookViewerTab";
|
||||
|
@ -73,7 +72,6 @@ ko.components.register("table-edit-entity-pane", new PaneComponents.TableEditEnt
|
|||
ko.components.register("table-column-options-pane", new PaneComponents.TableColumnOptionsPaneComponent());
|
||||
ko.components.register("table-query-select-pane", new PaneComponents.TableQuerySelectPaneComponent());
|
||||
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
||||
ko.components.register("load-query-pane", new PaneComponents.LoadQueryPaneComponent());
|
||||
ko.components.register("browse-queries-pane", new PaneComponents.BrowseQueriesPaneComponent());
|
||||
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
||||
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
||||
|
|
|
@ -371,20 +371,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
BrowseQueriesPane {
|
||||
"canSaveQueries": [Function],
|
||||
"container": [Circular],
|
||||
|
@ -789,20 +775,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"isSparkEnabledForAccount": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"loadQueryPane": LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
|
@ -1325,20 +1297,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
BrowseQueriesPane {
|
||||
"canSaveQueries": [Function],
|
||||
"container": [Circular],
|
||||
|
@ -1743,20 +1701,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"isSparkEnabledForAccount": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"loadQueryPane": LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
|
@ -2292,20 +2236,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
BrowseQueriesPane {
|
||||
"canSaveQueries": [Function],
|
||||
"container": [Circular],
|
||||
|
@ -2710,20 +2640,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"isSparkEnabledForAccount": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"loadQueryPane": LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
|
@ -3246,20 +3162,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
BrowseQueriesPane {
|
||||
"canSaveQueries": [Function],
|
||||
"container": [Circular],
|
||||
|
@ -3664,20 +3566,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"isSparkEnabledForAccount": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"loadQueryPane": LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
|
|
|
@ -58,7 +58,7 @@ import { DeleteCollectionConfirmationPanel } from "./Panes/DeleteCollectionConfi
|
|||
import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmationPanel";
|
||||
import { ExecuteSprocParamsPanel } from "./Panes/ExecuteSprocParamsPanel";
|
||||
import GraphStylingPane from "./Panes/GraphStylingPane";
|
||||
import { LoadQueryPane } from "./Panes/LoadQueryPane";
|
||||
import { LoadQueryPanel } from "./Panes/LoadQueryPanel";
|
||||
import NewVertexPane from "./Panes/NewVertexPane";
|
||||
import { SaveQueryPanel } from "./Panes/SaveQueryPanel";
|
||||
import { SettingsPane } from "./Panes/SettingsPane";
|
||||
|
@ -210,7 +210,6 @@ export default class Explorer {
|
|||
public querySelectPane: QuerySelectPane;
|
||||
public newVertexPane: NewVertexPane;
|
||||
public cassandraAddCollectionPane: CassandraAddCollectionPane;
|
||||
public loadQueryPane: LoadQueryPane;
|
||||
public browseQueriesPane: BrowseQueriesPane;
|
||||
public stringInputPane: StringInputPane;
|
||||
public setupNotebooksPane: SetupNotebooksPane;
|
||||
|
@ -610,13 +609,6 @@ export default class Explorer {
|
|||
container: this,
|
||||
});
|
||||
|
||||
this.loadQueryPane = new LoadQueryPane({
|
||||
id: "loadquerypane",
|
||||
visible: ko.observable<boolean>(false),
|
||||
|
||||
container: this,
|
||||
});
|
||||
|
||||
this.browseQueriesPane = new BrowseQueriesPane({
|
||||
id: "browsequeriespane",
|
||||
visible: ko.observable<boolean>(false),
|
||||
|
@ -651,7 +643,6 @@ export default class Explorer {
|
|||
this.querySelectPane,
|
||||
this.newVertexPane,
|
||||
this.cassandraAddCollectionPane,
|
||||
this.loadQueryPane,
|
||||
this.browseQueriesPane,
|
||||
this.stringInputPane,
|
||||
this.setupNotebooksPane,
|
||||
|
@ -2415,6 +2406,10 @@ export default class Explorer {
|
|||
);
|
||||
}
|
||||
|
||||
public openLoadQueryPanel(): void {
|
||||
this.openSidePanel("Load Query", <LoadQueryPanel explorer={this} closePanel={() => this.closeSidePanel()} />);
|
||||
}
|
||||
|
||||
public openSaveQueryPanel(): void {
|
||||
this.openSidePanel("Save Query", <SaveQueryPanel explorer={this} closePanel={() => this.closeSidePanel()} />);
|
||||
}
|
||||
|
|
|
@ -433,7 +433,7 @@ function createOpenQueryFromDiskButton(container: Explorer): CommandButtonCompon
|
|||
return {
|
||||
iconSrc: OpenQueryFromDiskIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => container.loadQueryPane.open(),
|
||||
onCommandClick: () => container.openLoadQueryPanel(),
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
|
||||
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
|
||||
<div class="contextual-pane" id="loadQueryPane">
|
||||
<!-- Load Query form -- Start -->
|
||||
<div class="contextual-pane-in">
|
||||
<form class="paneContentContainer" data-bind="submit: submit">
|
||||
<!-- Load Query header - Start -->
|
||||
<div class="firstdivbg headerline">
|
||||
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||
<div
|
||||
class="closeImg"
|
||||
role="button"
|
||||
aria-label="Close pane"
|
||||
tabindex="0"
|
||||
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
|
||||
>
|
||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Load Query header - End -->
|
||||
|
||||
<!-- Load Query errors - Start -->
|
||||
<div
|
||||
class="warningErrorContainer"
|
||||
aria-live="assertive"
|
||||
data-bind="visible: formErrors() && formErrors() !== ''"
|
||||
>
|
||||
<div class="warningErrorContent">
|
||||
<span><img class="paneErrorIcon" src="/error_red.svg" alt="Error" /></span>
|
||||
<span class="warningErrorDetailsLinkContainer">
|
||||
<span class="formErrors" data-bind="text: formErrors, attr: { title: formErrors }"></span>
|
||||
<a
|
||||
class="errorLink"
|
||||
role="link"
|
||||
data-bind="visible: formErrorsDetails() && formErrorsDetails() !== '', click: showErrorDetails"
|
||||
>More details</a
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Load Query errors - End -->
|
||||
|
||||
<!-- Load Query inputs - Start -->
|
||||
<div class="paneMainContent">
|
||||
<div>
|
||||
<div class="renewUploadItemsHeader">Select a query document</div>
|
||||
<input
|
||||
class="importFilesTitle"
|
||||
type="text"
|
||||
role="textbox"
|
||||
disabled
|
||||
data-bind="value: selectedFilesTitle"
|
||||
aria-label="Select a query document"
|
||||
autofocus
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
id="importQueryInput"
|
||||
accept="text/plain"
|
||||
style="display: none"
|
||||
data-bind="event: { change: updateSelectedFiles }"
|
||||
/>
|
||||
<a
|
||||
href="#"
|
||||
id="queryFileImportLink"
|
||||
aria-label="Upload files"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
data-bind="event: { click: onImportLinkClick, keypress: onImportLinkKeyPress }"
|
||||
>
|
||||
<img class="fileImportImg" src="/folder_16x16.svg" alt="upload files" title="upload files" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paneFooter">
|
||||
<div class="leftpanel-okbut"><input type="submit" value="Load" class="btncreatecoll1" /></div>
|
||||
</div>
|
||||
<!-- Load Query inputs - End -->
|
||||
</form>
|
||||
</div>
|
||||
<!-- Load Query form - Start -->
|
||||
<!-- Loader - Start -->
|
||||
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
|
||||
<img class="dataExplorerLoader" src="/LoadingIndicator_3Squares.gif" />
|
||||
</div>
|
||||
<!-- Loader - End -->
|
||||
</div>
|
||||
</div>
|
|
@ -1,147 +0,0 @@
|
|||
import * as ko from "knockout";
|
||||
import * as Q from "q";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import * as Logger from "../../Common/Logger";
|
||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||
import QueryTab from "../Tabs/QueryTab";
|
||||
|
||||
export class LoadQueryPane extends ContextualPaneBase {
|
||||
public selectedFilesTitle: ko.Observable<string>;
|
||||
public files: ko.Observable<FileList>;
|
||||
|
||||
constructor(options: ViewModels.PaneOptions) {
|
||||
super(options);
|
||||
this.title("Load Query");
|
||||
this.resetData();
|
||||
|
||||
this.selectedFilesTitle = ko.observable<string>("");
|
||||
this.files = ko.observable<FileList>();
|
||||
this.files.subscribe((newFiles: FileList) => this.updateSelectedFilesTitle(newFiles));
|
||||
const focusElement = document.getElementById("queryFileImportLink");
|
||||
focusElement && focusElement.focus();
|
||||
}
|
||||
|
||||
public submit() {
|
||||
this.formErrors("");
|
||||
this.formErrorsDetails("");
|
||||
if (!this.files() || this.files().length === 0) {
|
||||
this.formErrors("No file specified");
|
||||
this.formErrorsDetails("No file specified. Please input a file.");
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Error,
|
||||
"Could not load query -- No file specified. Please input a file."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const file: File = this.files().item(0);
|
||||
const id: string = NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.InProgress,
|
||||
`Loading query from file ${file.name}`
|
||||
);
|
||||
this.isExecuting(true);
|
||||
this.loadQueryFromFile(this.files().item(0))
|
||||
.then(
|
||||
() => {
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Info,
|
||||
`Successfully loaded query from file ${file.name}`
|
||||
);
|
||||
this.close();
|
||||
},
|
||||
(error: any) => {
|
||||
this.formErrors("Failed to load query");
|
||||
this.formErrorsDetails(`Failed to load query: ${error}`);
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Error,
|
||||
`Failed to load query from file ${file.name}: ${error}`
|
||||
);
|
||||
}
|
||||
)
|
||||
.finally(() => {
|
||||
this.isExecuting(false);
|
||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
||||
});
|
||||
}
|
||||
|
||||
public updateSelectedFiles(element: any, event: any): void {
|
||||
this.files(event.target.files);
|
||||
}
|
||||
|
||||
public open() {
|
||||
super.open();
|
||||
const focusElement = document.getElementById("queryFileImportLink");
|
||||
focusElement && focusElement.focus();
|
||||
}
|
||||
|
||||
public close() {
|
||||
super.close();
|
||||
this.resetData();
|
||||
this.files(undefined);
|
||||
this.resetFileInput();
|
||||
}
|
||||
|
||||
public onImportLinkClick(source: any, event: MouseEvent): boolean {
|
||||
document.getElementById("importQueryInput").click();
|
||||
return false;
|
||||
}
|
||||
|
||||
public onImportLinkKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
|
||||
this.onImportLinkClick(source, null);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
public loadQueryFromFile(file: File): Q.Promise<void> {
|
||||
const selectedCollection: ViewModels.Collection = this.container && this.container.findSelectedCollection();
|
||||
if (!selectedCollection) {
|
||||
// should never get into this state
|
||||
Logger.logError("No collection was selected", "LoadQueryPane.loadQueryFromFile");
|
||||
return Q.reject("No collection was selected");
|
||||
} else if (this.container.isPreferredApiMongoDB()) {
|
||||
selectedCollection.onNewMongoQueryClick(selectedCollection, null);
|
||||
} else {
|
||||
selectedCollection.onNewQueryClick(selectedCollection, null);
|
||||
}
|
||||
const deferred: Q.Deferred<void> = Q.defer<void>();
|
||||
const reader = new FileReader();
|
||||
reader.onload = (evt: any): void => {
|
||||
const fileData: string = evt.target.result;
|
||||
const queryTab = this.container.tabsManager.activeTab() as QueryTab;
|
||||
queryTab.initialEditorContent(fileData);
|
||||
queryTab.sqlQueryEditorContent(fileData);
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
reader.onerror = (evt: ProgressEvent): void => {
|
||||
deferred.reject((evt as any).error.message);
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
private updateSelectedFilesTitle(fileList: FileList) {
|
||||
this.selectedFilesTitle("");
|
||||
|
||||
if (!fileList || fileList.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < fileList.length; i++) {
|
||||
const originalTitle = this.selectedFilesTitle();
|
||||
this.selectedFilesTitle(originalTitle + `"${fileList.item(i).name}"`);
|
||||
}
|
||||
}
|
||||
|
||||
private resetFileInput(): void {
|
||||
const inputElement = $("#importQueryInput");
|
||||
inputElement.wrap("<form>").closest("form").get(0).reset();
|
||||
inputElement.unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Load Query Pane should render Default properly 1`] = `
|
||||
<GenericRightPaneComponent
|
||||
container={Object {}}
|
||||
formError=""
|
||||
formErrorDetail=""
|
||||
id="loadQueryPane"
|
||||
isExecuting={false}
|
||||
onClose={[Function]}
|
||||
onSubmit={[Function]}
|
||||
submitButtonText="Load"
|
||||
title="Load Query"
|
||||
>
|
||||
<div
|
||||
className="panelFormWrapper"
|
||||
>
|
||||
<div
|
||||
className="panelMainContent"
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<StyledTextFieldBase
|
||||
autoFocus={true}
|
||||
id="confirmCollectionId"
|
||||
label="Select a query document"
|
||||
readOnly={true}
|
||||
styles={
|
||||
Object {
|
||||
"fieldGroup": Object {
|
||||
"width": 300,
|
||||
},
|
||||
}
|
||||
}
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
className="customFileUpload"
|
||||
htmlFor="importQueryInputId"
|
||||
>
|
||||
<StyledImageBase
|
||||
alt="upload files"
|
||||
className="fileIcon"
|
||||
height={20}
|
||||
imageFit={4}
|
||||
src=""
|
||||
width={20}
|
||||
/>
|
||||
<input
|
||||
accept="text/plain"
|
||||
className="fileUpload"
|
||||
id="importQueryInputId"
|
||||
onChange={[Function]}
|
||||
type="file"
|
||||
/>
|
||||
</label>
|
||||
</Stack>
|
||||
</div>
|
||||
</div>
|
||||
</GenericRightPaneComponent>
|
||||
`;
|
|
@ -0,0 +1,17 @@
|
|||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import { LoadQueryPanel } from "./index";
|
||||
|
||||
describe("Load Query Pane", () => {
|
||||
it("should render Default properly", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<LoadQueryPanel {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,134 @@
|
|||
import { useBoolean } from "@uifabric/react-hooks";
|
||||
import { IImageProps, Image, ImageFit, Stack, TextField } from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import folderIcon from "../../../../images/folder_16x16.svg";
|
||||
import { logError } from "../../../Common/Logger";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import QueryTab from "../../Tabs/QueryTab";
|
||||
import { Collection } from "..//../../Contracts/ViewModels";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent";
|
||||
|
||||
interface LoadQueryPanelProps {
|
||||
explorer: Explorer;
|
||||
closePanel: () => void;
|
||||
}
|
||||
|
||||
export const LoadQueryPanel: FunctionComponent<LoadQueryPanelProps> = ({
|
||||
explorer,
|
||||
closePanel,
|
||||
}: LoadQueryPanelProps): JSX.Element => {
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [formErrorsDetails, setFormErrorsDetails] = useState<string>("");
|
||||
const [selectedFileName, setSelectedFileName] = useState<string>("");
|
||||
const [selectedFiles, setSelectedFiles] = useState<FileList>();
|
||||
|
||||
const imageProps: Partial<IImageProps> = {
|
||||
imageFit: ImageFit.centerCover,
|
||||
width: 20,
|
||||
height: 20,
|
||||
className: "fileIcon",
|
||||
};
|
||||
|
||||
const title = "Load Query";
|
||||
const genericPaneProps: GenericRightPaneProps = {
|
||||
container: explorer,
|
||||
formError: formError,
|
||||
formErrorDetail: formErrorsDetails,
|
||||
id: "loadQueryPane",
|
||||
isExecuting: isLoading,
|
||||
title,
|
||||
submitButtonText: "Load",
|
||||
onClose: () => closePanel(),
|
||||
onSubmit: () => submit(),
|
||||
};
|
||||
|
||||
const onFileSelected = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const { files } = e.target;
|
||||
setSelectedFiles(files);
|
||||
setSelectedFileName(files && files[0] && `"${files[0].name}"`);
|
||||
};
|
||||
|
||||
const submit = async (): Promise<void> => {
|
||||
setFormError("");
|
||||
setFormErrorsDetails("");
|
||||
if (!selectedFiles || selectedFiles.length === 0) {
|
||||
setFormError("No file specified");
|
||||
setFormErrorsDetails("No file specified. Please input a file.");
|
||||
logConsoleError("Could not load query -- No file specified. Please input a file.");
|
||||
return;
|
||||
}
|
||||
|
||||
const file: File = selectedFiles[0];
|
||||
logConsoleProgress(`Loading query from file ${file.name}`);
|
||||
setLoadingTrue();
|
||||
try {
|
||||
await loadQueryFromFile(file);
|
||||
logConsoleInfo(`Successfully loaded query from file ${file.name}`);
|
||||
closePanel();
|
||||
setLoadingFalse();
|
||||
} catch (error) {
|
||||
setLoadingFalse();
|
||||
setFormError("Failed to load query");
|
||||
setFormErrorsDetails(`Failed to load query: ${error}`);
|
||||
logConsoleError(`Failed to load query from file ${file.name}: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
const loadQueryFromFile = async (file: File): Promise<void> => {
|
||||
const selectedCollection: Collection = explorer?.findSelectedCollection();
|
||||
if (!selectedCollection) {
|
||||
logError("No collection was selected", "LoadQueryPane.loadQueryFromFile");
|
||||
} else if (userContext.apiType === "Mongo") {
|
||||
selectedCollection.onNewMongoQueryClick(selectedCollection, undefined);
|
||||
} else {
|
||||
selectedCollection.onNewQueryClick(selectedCollection, undefined);
|
||||
}
|
||||
const reader = new FileReader();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
reader.onload = (evt: any): void => {
|
||||
const fileData: string = evt.target.result;
|
||||
const queryTab = explorer.tabsManager.activeTab() as QueryTab;
|
||||
queryTab.initialEditorContent(fileData);
|
||||
queryTab.sqlQueryEditorContent(fileData);
|
||||
};
|
||||
|
||||
reader.onerror = (): void => {
|
||||
setFormError("Failed to load query");
|
||||
setFormErrorsDetails(`Failed to load query`);
|
||||
logConsoleError(`Failed to load query from file ${file.name}`);
|
||||
};
|
||||
return reader.readAsText(file);
|
||||
};
|
||||
|
||||
return (
|
||||
<GenericRightPaneComponent {...genericPaneProps}>
|
||||
<div className="panelFormWrapper">
|
||||
<div className="panelMainContent">
|
||||
<Stack horizontal>
|
||||
<TextField
|
||||
id="confirmCollectionId"
|
||||
label="Select a query document"
|
||||
value={selectedFileName}
|
||||
autoFocus
|
||||
readOnly
|
||||
styles={{ fieldGroup: { width: 300 } }}
|
||||
/>
|
||||
<label htmlFor="importQueryInputId" className="customFileUpload">
|
||||
<Image {...imageProps} src={folderIcon} alt="upload files" />
|
||||
<input
|
||||
className="fileUpload"
|
||||
type="file"
|
||||
id="importQueryInputId"
|
||||
accept="text/plain"
|
||||
onChange={onFileSelected}
|
||||
/>
|
||||
</label>
|
||||
</Stack>
|
||||
</div>
|
||||
</div>
|
||||
</GenericRightPaneComponent>
|
||||
);
|
||||
};
|
|
@ -6,7 +6,6 @@ import DeleteCollectionConfirmationPaneTemplate from "./DeleteCollectionConfirma
|
|||
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
|
||||
import GraphNewVertexPaneTemplate from "./GraphNewVertexPane.html";
|
||||
import GraphStylingPaneTemplate from "./GraphStylingPane.html";
|
||||
import LoadQueryPaneTemplate from "./LoadQueryPane.html";
|
||||
import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html";
|
||||
import StringInputPaneTemplate from "./StringInputPane.html";
|
||||
import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html";
|
||||
|
@ -110,15 +109,6 @@ export class CassandraAddCollectionPaneComponent {
|
|||
}
|
||||
}
|
||||
|
||||
export class LoadQueryPaneComponent {
|
||||
constructor() {
|
||||
return {
|
||||
viewModel: PaneComponent,
|
||||
template: LoadQueryPaneTemplate,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowseQueriesPaneComponent {
|
||||
constructor() {
|
||||
return {
|
||||
|
|
|
@ -126,6 +126,17 @@
|
|||
.panelGroupSpacing > * {
|
||||
margin-bottom: @SmallSpace;
|
||||
}
|
||||
.fileUpload {
|
||||
display: none !important;
|
||||
}
|
||||
.customFileUpload {
|
||||
padding: 25px 0px 0px 10px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
}
|
||||
.fileIcon {
|
||||
align-self: center;
|
||||
}
|
||||
.panelAddIconLabel {
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
|
@ -140,4 +151,4 @@
|
|||
}
|
||||
.removeIcon {
|
||||
color: @InfoIconColor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -347,20 +347,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
|||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
BrowseQueriesPane {
|
||||
"canSaveQueries": [Function],
|
||||
"container": [Circular],
|
||||
|
@ -765,20 +751,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
|||
"isSparkEnabledForAccount": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"loadQueryPane": LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
|
@ -1389,20 +1361,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
|||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
BrowseQueriesPane {
|
||||
"canSaveQueries": [Function],
|
||||
"container": [Circular],
|
||||
|
@ -1807,20 +1765,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
|||
"isSparkEnabledForAccount": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"loadQueryPane": LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
|
|
|
@ -347,20 +347,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
|||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
BrowseQueriesPane {
|
||||
"canSaveQueries": [Function],
|
||||
"container": [Circular],
|
||||
|
@ -765,20 +751,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
|||
"isSparkEnabledForAccount": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"loadQueryPane": LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
|
|
|
@ -348,20 +348,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
|||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
BrowseQueriesPane {
|
||||
"canSaveQueries": [Function],
|
||||
"container": [Circular],
|
||||
|
@ -769,20 +755,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
|||
"isSparkEnabledForAccount": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"loadQueryPane": LoadQueryPane {
|
||||
"container": [Circular],
|
||||
"files": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "loadquerypane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onImportLinkKeyPress": [Function],
|
||||
"selectedFilesTitle": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
|
|
|
@ -184,10 +184,6 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
|
|||
await this._executeQueryDocumentsPage(0);
|
||||
};
|
||||
|
||||
public onLoadQueryClick = (): void => {
|
||||
this.collection && this.collection.container && this.collection.container.loadQueryPane.open();
|
||||
};
|
||||
|
||||
public onSaveQueryClick = (): void => {
|
||||
this.collection && this.collection.container && this.collection.container.openSaveQueryPanel();
|
||||
};
|
||||
|
|
|
@ -239,7 +239,6 @@ const App: React.FunctionComponent = () => {
|
|||
<div data-bind='component: { name: "table-column-options-pane", params: { data: tableColumnOptionsPane} }' />
|
||||
<div data-bind='component: { name: "table-query-select-pane", params: { data: querySelectPane} }' />
|
||||
<div data-bind='component: { name: "cassandra-add-collection-pane", params: { data: cassandraAddCollectionPane} }' />
|
||||
<div data-bind='component: { name: "load-query-pane", params: { data: loadQueryPane} }' />
|
||||
<div data-bind='component: { name: "browse-queries-pane", params: { data: browseQueriesPane} }' />
|
||||
<div data-bind='component: { name: "string-input-pane", params: { data: stringInputPane} }' />
|
||||
<div data-bind='component: { name: "setup-notebooks-pane", params: { data: setupNotebooksPane} }' />
|
||||
|
|
Loading…
Reference in New Issue