Move selectedNode to zustand (#903)
This commit is contained in:
parent
a7239c7579
commit
4d2a6999d4
|
@ -89,7 +89,6 @@ export interface Database extends TreeNode {
|
|||
|
||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||
|
||||
selectDatabase(): void;
|
||||
expandDatabase(): Promise<void>;
|
||||
collapseDatabase(): void;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmat
|
|||
import StoredProcedure from "./Tree/StoredProcedure";
|
||||
import Trigger from "./Tree/Trigger";
|
||||
import UserDefinedFunction from "./Tree/UserDefinedFunction";
|
||||
import { useSelectedNode } from "./useSelectedNode";
|
||||
|
||||
export interface CollectionContextMenuButtonParams {
|
||||
databaseId: string;
|
||||
|
@ -48,10 +49,7 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
|||
onClick: () =>
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getDatabaseName(),
|
||||
<DeleteDatabaseConfirmationPanel explorer={container} selectedDatabase={container.findSelectedDatabase()} />
|
||||
),
|
||||
.openSidePanel("Delete " + getDatabaseName(), <DeleteDatabaseConfirmationPanel explorer={container} />),
|
||||
label: `Delete ${getDatabaseName()}`,
|
||||
styleClass: "deleteDatabaseMenuItem",
|
||||
});
|
||||
|
@ -82,7 +80,7 @@ export const createCollectionContextMenuButton = (
|
|||
items.push({
|
||||
iconSrc: HostedTerminalIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
if (container.isShellEnabled()) {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||
} else {
|
||||
|
@ -97,7 +95,7 @@ export const createCollectionContextMenuButton = (
|
|||
items.push({
|
||||
iconSrc: AddStoredProcedureIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, undefined);
|
||||
},
|
||||
label: "New Stored Procedure",
|
||||
|
@ -106,7 +104,7 @@ export const createCollectionContextMenuButton = (
|
|||
items.push({
|
||||
iconSrc: AddUdfIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, undefined);
|
||||
},
|
||||
label: "New UDF",
|
||||
|
@ -115,7 +113,7 @@ export const createCollectionContextMenuButton = (
|
|||
items.push({
|
||||
iconSrc: AddTriggerIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, undefined);
|
||||
},
|
||||
label: "New Trigger",
|
||||
|
|
|
@ -126,7 +126,6 @@ describe("SettingsComponent", () => {
|
|||
isDatabaseExpanded: undefined,
|
||||
isDatabaseShared: ko.computed(() => true),
|
||||
selectedSubnodeKind: undefined,
|
||||
selectDatabase: undefined,
|
||||
expandDatabase: undefined,
|
||||
collapseDatabase: undefined,
|
||||
loadCollections: undefined,
|
||||
|
|
|
@ -36,7 +36,6 @@ describe("SettingsUtils", () => {
|
|||
isDatabaseExpanded: ko.observable(false),
|
||||
isDatabaseShared: ko.computed(() => true),
|
||||
selectedSubnodeKind: ko.observable(undefined),
|
||||
selectDatabase: undefined,
|
||||
expandDatabase: undefined,
|
||||
collapseDatabase: undefined,
|
||||
loadCollections: undefined,
|
||||
|
|
|
@ -34,7 +34,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isSchemaEnabled": [Function],
|
||||
"isShellEnabled": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
|
@ -59,8 +58,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"container": [Circular],
|
||||
"parameters": [Function],
|
||||
},
|
||||
"selectedDatabaseId": [Function],
|
||||
"selectedNode": [Function],
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
|
@ -123,7 +120,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isSchemaEnabled": [Function],
|
||||
"isShellEnabled": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
|
@ -148,8 +144,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"container": [Circular],
|
||||
"parameters": [Function],
|
||||
},
|
||||
"selectedDatabaseId": [Function],
|
||||
"selectedNode": [Function],
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
|
|
|
@ -67,6 +67,7 @@ import { ResourceTreeAdapter } from "./Tree/ResourceTreeAdapter";
|
|||
import { ResourceTreeAdapterForResourceToken } from "./Tree/ResourceTreeAdapterForResourceToken";
|
||||
import StoredProcedure from "./Tree/StoredProcedure";
|
||||
import { useDatabases } from "./useDatabases";
|
||||
import { useSelectedNode } from "./useSelectedNode";
|
||||
|
||||
BindingHandlersRegisterer.registerBindingHandlers();
|
||||
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
||||
|
@ -83,14 +84,10 @@ export default class Explorer {
|
|||
public tableDataClient: TableDataClient;
|
||||
|
||||
// Resource Tree
|
||||
public selectedDatabaseId: ko.Computed<string>;
|
||||
public selectedCollectionId: ko.Computed<string>;
|
||||
public selectedNode: ko.Observable<ViewModels.TreeNode>;
|
||||
private resourceTree: ResourceTreeAdapter;
|
||||
|
||||
// Resource Token
|
||||
public resourceTokenCollection: ko.Observable<ViewModels.CollectionBase>;
|
||||
public isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
||||
public resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken;
|
||||
|
||||
// Tabs
|
||||
|
@ -163,18 +160,10 @@ export default class Explorer {
|
|||
this.resourceTokenCollection = ko.observable<ViewModels.CollectionBase>();
|
||||
this.isSchemaEnabled = ko.computed<boolean>(() => userContext.features.enableSchema);
|
||||
|
||||
this.selectedNode = ko.observable<ViewModels.TreeNode>();
|
||||
this.selectedNode.subscribe((nodeSelected: ViewModels.TreeNode) => {
|
||||
useSelectedNode.subscribe(() => {
|
||||
// Make sure switching tabs restores tabs display
|
||||
this.isTabsContentExpanded(false);
|
||||
});
|
||||
this.isResourceTokenCollectionNodeSelected = ko.computed<boolean>(() => {
|
||||
return (
|
||||
this.selectedNode() &&
|
||||
this.resourceTokenCollection() &&
|
||||
this.selectedNode().id() === this.resourceTokenCollection().id()
|
||||
);
|
||||
});
|
||||
|
||||
this.isFixedCollectionWithSharedThroughputSupported = ko.computed(() => {
|
||||
if (userContext.features.enableFixedCollectionWithSharedThroughput) {
|
||||
|
@ -187,31 +176,11 @@ export default class Explorer {
|
|||
|
||||
return isCapabilityEnabled("EnableMongo");
|
||||
});
|
||||
this.selectedDatabaseId = ko.computed<string>(() => {
|
||||
const selectedNode = this.selectedNode();
|
||||
if (!selectedNode) {
|
||||
return "";
|
||||
}
|
||||
|
||||
switch (selectedNode.nodeKind) {
|
||||
case "Collection":
|
||||
return (selectedNode as ViewModels.CollectionBase).databaseId || "";
|
||||
case "Database":
|
||||
return selectedNode.id() || "";
|
||||
case "DocumentId":
|
||||
case "StoredProcedure":
|
||||
case "Trigger":
|
||||
case "UserDefinedFunction":
|
||||
return selectedNode.collection.databaseId || "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
});
|
||||
|
||||
this.tabsManager = params?.tabsManager ?? new TabsManager();
|
||||
this.tabsManager.openedTabs.subscribe((tabs) => {
|
||||
if (tabs.length === 0) {
|
||||
this.selectedNode(undefined);
|
||||
useSelectedNode.getState().setSelectedNode(undefined);
|
||||
useCommandBar.getState().setContextButtons([]);
|
||||
}
|
||||
});
|
||||
|
@ -363,22 +332,6 @@ export default class Explorer {
|
|||
// TODO: return result
|
||||
}
|
||||
|
||||
public isDatabaseNodeOrNoneSelected(): boolean {
|
||||
return this.isNoneSelected() || this.isDatabaseNodeSelected();
|
||||
}
|
||||
|
||||
public isDatabaseNodeSelected(): boolean {
|
||||
return (this.selectedNode() && this.selectedNode().nodeKind === "Database") || false;
|
||||
}
|
||||
|
||||
public isNodeKindSelected(nodeKind: string): boolean {
|
||||
return (this.selectedNode() && this.selectedNode().nodeKind === nodeKind) || false;
|
||||
}
|
||||
|
||||
public isNoneSelected(): boolean {
|
||||
return this.selectedNode() == null;
|
||||
}
|
||||
|
||||
public refreshDatabaseForResourceToken(): Promise<void> {
|
||||
const databaseId = userContext.parsedResourceToken?.databaseId;
|
||||
const collectionId = userContext.parsedResourceToken?.collectionId;
|
||||
|
@ -388,7 +341,7 @@ export default class Explorer {
|
|||
|
||||
return readCollection(databaseId, collectionId).then((collection: DataModels.Collection) => {
|
||||
this.resourceTokenCollection(new ResourceTokenCollection(this, databaseId, collection));
|
||||
this.selectedNode(this.resourceTokenCollection());
|
||||
useSelectedNode.getState().setSelectedNode(this.resourceTokenCollection());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -414,11 +367,9 @@ export default class Explorer {
|
|||
},
|
||||
startKey
|
||||
);
|
||||
const currentlySelectedNode: ViewModels.TreeNode = this.selectedNode();
|
||||
const deltaDatabases = this.getDeltaDatabases(databases);
|
||||
this.addDatabasesToList(deltaDatabases.toAdd);
|
||||
this.deleteDatabasesFromList(deltaDatabases.toDelete);
|
||||
this.selectedNode(currentlySelectedNode);
|
||||
this.refreshAndExpandNewDatabases(deltaDatabases.toAdd).then(
|
||||
() => {
|
||||
deferred.resolve();
|
||||
|
@ -611,34 +562,6 @@ export default class Explorer {
|
|||
}
|
||||
};
|
||||
|
||||
public findSelectedDatabase(): ViewModels.Database {
|
||||
if (!this.selectedNode()) {
|
||||
return null;
|
||||
}
|
||||
if (this.selectedNode().nodeKind === "Database") {
|
||||
return _.find(
|
||||
useDatabases.getState().databases,
|
||||
(database: ViewModels.Database) => database.id() === this.selectedNode().id()
|
||||
);
|
||||
}
|
||||
return this.findSelectedCollection().database;
|
||||
}
|
||||
|
||||
public isSelectedDatabaseShared(): boolean {
|
||||
const database = this.findSelectedDatabase();
|
||||
if (!!database) {
|
||||
return database.offer && !!database.offer();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public findSelectedCollection(): ViewModels.Collection {
|
||||
return (this.selectedNode().nodeKind === "Collection"
|
||||
? this.selectedNode()
|
||||
: this.selectedNode().collection) as ViewModels.Collection;
|
||||
}
|
||||
|
||||
private refreshAndExpandNewDatabases(newDatabases: ViewModels.Database[]): Q.Promise<void> {
|
||||
// we reload collections for all databases so the resource tree reflects any collection-level changes
|
||||
// i.e addition of stored procedures, etc.
|
||||
|
@ -1331,7 +1254,7 @@ export default class Explorer {
|
|||
}
|
||||
|
||||
public openUploadItemsPanePane(): void {
|
||||
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane explorer={this} />);
|
||||
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane />);
|
||||
}
|
||||
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
|
||||
useSidePanel
|
||||
|
|
|
@ -8,9 +8,9 @@ import * as React from "react";
|
|||
import create, { UseStore } from "zustand";
|
||||
import { StyleConstants } from "../../../Common/Constants";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { useObservable } from "../../../hooks/useObservable";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
||||
import * as CommandBarUtil from "./CommandBarUtil";
|
||||
|
||||
|
@ -29,13 +29,13 @@ export const useCommandBar: UseStore<CommandBarStore> = create((set) => ({
|
|||
}));
|
||||
|
||||
export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||
useObservable(container.selectedNode);
|
||||
const selectedNodeState = useSelectedNode();
|
||||
const buttons = useCommandBar((state) => state.contextButtons);
|
||||
const backgroundColor = StyleConstants.BaseLight;
|
||||
|
||||
const staticButtons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(container);
|
||||
const staticButtons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(container, selectedNodeState);
|
||||
const contextButtons = (buttons || []).concat(
|
||||
CommandBarComponentButtonFactory.createContextCommandBarButtons(container)
|
||||
CommandBarComponentButtonFactory.createContextCommandBarButtons(container, selectedNodeState)
|
||||
);
|
||||
const controlButtons = CommandBarComponentButtonFactory.createControlCommandBarButtons(container);
|
||||
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
import * as ko from "knockout";
|
||||
import { AuthType } from "../../../AuthType";
|
||||
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
||||
import { CollectionBase } from "../../../Contracts/ViewModels";
|
||||
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
|
||||
import { updateUserContext } from "../../../UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import NotebookManager from "../../Notebook/NotebookManager";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
||||
|
||||
describe("CommandBarComponentButtonFactory tests", () => {
|
||||
let mockExplorer: Explorer;
|
||||
|
||||
afterEach(() => useSelectedNode.getState().setSelectedNode(undefined));
|
||||
|
||||
describe("Enable Azure Synapse Link Button", () => {
|
||||
const enableAzureSynapseLinkBtnLabel = "Enable Azure Synapse Link";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
|
@ -23,14 +28,12 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
});
|
||||
|
||||
it("Account is not serverless - button should be visible", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const enableAzureSynapseLinkBtn = buttons.find(
|
||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||
);
|
||||
|
@ -45,7 +48,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const enableAzureSynapseLinkBtn = buttons.find(
|
||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||
);
|
||||
|
@ -55,6 +58,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
|
||||
describe("Enable notebook button", () => {
|
||||
const enableNotebookBtnLabel = "Enable Notebooks (Preview)";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
|
@ -67,9 +71,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -82,7 +83,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||
expect(enableNotebookBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -94,7 +95,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
portalEnv: "mooncake",
|
||||
});
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||
expect(enableNotebookBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -103,7 +104,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||
expect(enableNotebookBtn).toBeDefined();
|
||||
expect(enableNotebookBtn.disabled).toBe(false);
|
||||
|
@ -114,7 +115,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||
expect(enableNotebookBtn).toBeDefined();
|
||||
expect(enableNotebookBtn.disabled).toBe(true);
|
||||
|
@ -126,6 +127,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
|
||||
describe("Open Mongo Shell button", () => {
|
||||
const openMongoShellBtnLabel = "Open Mongo Shell";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
|
@ -137,9 +139,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
|
||||
mockExplorer.isShellEnabled = ko.observable(true);
|
||||
});
|
||||
|
||||
|
@ -163,7 +162,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
updateUserContext({
|
||||
apiType: "SQL",
|
||||
});
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -173,13 +172,13 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
portalEnv: "mooncake",
|
||||
});
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
|
||||
it("Notebooks is not enabled and is unavailable - button should be hidden", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -187,7 +186,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
it("Notebooks is not enabled and is available - button should be hidden", () => {
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -195,7 +194,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeDefined();
|
||||
expect(openMongoShellBtn.disabled).toBe(false);
|
||||
|
@ -206,7 +205,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeDefined();
|
||||
expect(openMongoShellBtn.disabled).toBe(false);
|
||||
|
@ -218,7 +217,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
mockExplorer.isShellEnabled = ko.observable(false);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -226,6 +225,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
|
||||
describe("Open Cassandra Shell button", () => {
|
||||
const openCassandraShellBtnLabel = "Open Cassandra Shell";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
|
@ -237,8 +237,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -262,7 +260,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
} as DatabaseAccount,
|
||||
});
|
||||
console.log(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -272,13 +270,13 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
portalEnv: "mooncake",
|
||||
});
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeUndefined();
|
||||
});
|
||||
|
||||
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -286,7 +284,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
it("Notebooks is not enabled and is available - button should be shown and enabled", () => {
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeUndefined();
|
||||
});
|
||||
|
@ -294,7 +292,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeDefined();
|
||||
expect(openCassandraShellBtn.disabled).toBe(false);
|
||||
|
@ -305,7 +303,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeDefined();
|
||||
expect(openCassandraShellBtn.disabled).toBe(false);
|
||||
|
@ -316,6 +314,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
describe("GitHub buttons", () => {
|
||||
const connectToGitHubBtnLabel = "Connect to GitHub";
|
||||
const manageGitHubSettingsBtnLabel = "Manage GitHub settings";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
|
@ -328,7 +327,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
});
|
||||
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
|
||||
mockExplorer.notebookManager = new NotebookManager();
|
||||
|
@ -346,7 +344,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
it("Notebooks is enabled and GitHubOAuthService is not logged in - connect to github button should be visible", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
||||
expect(connectToGitHubBtn).toBeDefined();
|
||||
});
|
||||
|
@ -355,7 +353,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
mockExplorer.notebookManager.gitHubOAuthService.isLoggedIn = jest.fn().mockReturnValue(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const manageGitHubSettingsBtn = buttons.find(
|
||||
(button) => button.commandButtonLabel === manageGitHubSettingsBtnLabel
|
||||
);
|
||||
|
@ -363,7 +361,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
});
|
||||
|
||||
it("Notebooks is not enabled - connect to github and manage github settings buttons should be hidden", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
|
||||
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
||||
expect(connectToGitHubBtn).toBeUndefined();
|
||||
|
@ -376,10 +374,12 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
});
|
||||
|
||||
describe("Resource token", () => {
|
||||
const mockCollection = { id: ko.observable("test") } as CollectionBase;
|
||||
useSelectedNode.getState().setSelectedNode(mockCollection);
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isResourceTokenCollectionNodeSelected = ko.computed(() => true);
|
||||
mockExplorer.resourceTokenCollection = ko.observable(mockCollection);
|
||||
|
||||
updateUserContext({
|
||||
authType: AuthType.ResourceToken,
|
||||
|
@ -392,7 +392,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||
kind: "DocumentDB",
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
expect(buttons.length).toBe(2);
|
||||
expect(buttons[0].commandButtonLabel).toBe("New SQL Query");
|
||||
expect(buttons[0].disabled).toBe(false);
|
||||
|
|
|
@ -31,12 +31,16 @@ import Explorer from "../../Explorer";
|
|||
import { OpenFullScreen } from "../../OpenFullScreen";
|
||||
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
|
||||
import { SettingsPane } from "../../Panes/SettingsPane/SettingsPane";
|
||||
import { SelectedNodeState } from "../../useSelectedNode";
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export function createStaticCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
export function createStaticCommandBarButtons(
|
||||
container: Explorer,
|
||||
selectedNodeState: SelectedNodeState
|
||||
): CommandButtonComponentProps[] {
|
||||
if (userContext.authType === AuthType.ResourceToken) {
|
||||
return createStaticCommandBarButtonsForResourceToken(container);
|
||||
return createStaticCommandBarButtonsForResourceToken(container, selectedNodeState);
|
||||
}
|
||||
|
||||
const newCollectionBtn = createNewCollectionGroup(container);
|
||||
|
@ -71,7 +75,9 @@ export function createStaticCommandBarButtons(container: Explorer): CommandButto
|
|||
|
||||
buttons.push(createNotebookWorkspaceResetButton(container));
|
||||
if (
|
||||
(userContext.apiType === "Mongo" && container.isShellEnabled() && container.isDatabaseNodeOrNoneSelected()) ||
|
||||
(userContext.apiType === "Mongo" &&
|
||||
container.isShellEnabled() &&
|
||||
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
|
||||
userContext.apiType === "Cassandra"
|
||||
) {
|
||||
buttons.push(createDivider());
|
||||
|
@ -87,18 +93,18 @@ export function createStaticCommandBarButtons(container: Explorer): CommandButto
|
|||
}
|
||||
}
|
||||
|
||||
if (!container.isDatabaseNodeOrNoneSelected()) {
|
||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected()) {
|
||||
const isQuerySupported = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||
|
||||
if (isQuerySupported) {
|
||||
buttons.push(createDivider());
|
||||
const newSqlQueryBtn = createNewSQLQueryButton(container);
|
||||
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
||||
buttons.push(newSqlQueryBtn);
|
||||
}
|
||||
|
||||
if (isQuerySupported && container.selectedNode() && container.findSelectedCollection()) {
|
||||
if (isQuerySupported && selectedNodeState.findSelectedCollection()) {
|
||||
const openQueryBtn = createOpenQueryButton(container);
|
||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton(container)];
|
||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
||||
buttons.push(openQueryBtn);
|
||||
}
|
||||
|
||||
|
@ -108,16 +114,16 @@ export function createStaticCommandBarButtons(container: Explorer): CommandButto
|
|||
iconSrc: AddStoredProcedureIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
|
||||
newStoredProcedureBtn.children = createScriptCommandButtons(container);
|
||||
newStoredProcedureBtn.children = createScriptCommandButtons(selectedNodeState);
|
||||
buttons.push(newStoredProcedureBtn);
|
||||
}
|
||||
}
|
||||
|
@ -125,16 +131,19 @@ export function createStaticCommandBarButtons(container: Explorer): CommandButto
|
|||
return buttons;
|
||||
}
|
||||
|
||||
export function createContextCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
export function createContextCommandBarButtons(
|
||||
container: Explorer,
|
||||
selectedNodeState: SelectedNodeState
|
||||
): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
|
||||
if (!container.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
||||
const label = container.isShellEnabled() ? "Open Mongo Shell" : "New Shell";
|
||||
const newMongoShellBtn: CommandButtonComponentProps = {
|
||||
iconSrc: HostedTerminalIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
if (container.isShellEnabled()) {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||
} else {
|
||||
|
@ -144,7 +153,7 @@ export function createContextCommandBarButtons(container: Explorer): CommandButt
|
|||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo",
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo",
|
||||
};
|
||||
buttons.push(newMongoShellBtn);
|
||||
}
|
||||
|
@ -279,20 +288,20 @@ function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
|||
};
|
||||
}
|
||||
|
||||
function createNewSQLQueryButton(container: Explorer): CommandButtonComponentProps {
|
||||
function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandButtonComponentProps {
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
const label = "New SQL Query";
|
||||
return {
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
} else if (userContext.apiType === "Mongo") {
|
||||
const label = "New Query";
|
||||
|
@ -300,23 +309,24 @@ function createNewSQLQueryButton(container: Explorer): CommandButtonComponentPro
|
|||
iconSrc: AddSqlQueryIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function createScriptCommandButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
export function createScriptCommandButtons(selectedNodeState: SelectedNodeState): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
|
||||
const shouldEnableScriptsCommands: boolean = !container.isDatabaseNodeOrNoneSelected() && areScriptsSupported();
|
||||
const shouldEnableScriptsCommands: boolean =
|
||||
!selectedNodeState.isDatabaseNodeOrNoneSelected() && areScriptsSupported();
|
||||
|
||||
if (shouldEnableScriptsCommands) {
|
||||
const label = "New Stored Procedure";
|
||||
|
@ -324,13 +334,13 @@ export function createScriptCommandButtons(container: Explorer): CommandButtonCo
|
|||
iconSrc: AddStoredProcedureIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
buttons.push(newStoredProcedureBtn);
|
||||
}
|
||||
|
@ -341,13 +351,13 @@ export function createScriptCommandButtons(container: Explorer): CommandButtonCo
|
|||
iconSrc: AddUdfIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
buttons.push(newUserDefinedFunctionBtn);
|
||||
}
|
||||
|
@ -358,13 +368,13 @@ export function createScriptCommandButtons(container: Explorer): CommandButtonCo
|
|||
iconSrc: AddTriggerIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
buttons.push(newTriggerBtn);
|
||||
}
|
||||
|
@ -411,12 +421,12 @@ function createOpenQueryButton(container: Explorer): CommandButtonComponentProps
|
|||
};
|
||||
}
|
||||
|
||||
function createOpenQueryFromDiskButton(container: Explorer): CommandButtonComponentProps {
|
||||
function createOpenQueryFromDiskButton(): CommandButtonComponentProps {
|
||||
const label = "Open Query From Disk";
|
||||
return {
|
||||
iconSrc: OpenQueryFromDiskIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => useSidePanel.getState().openSidePanel("Load Query", <LoadQueryPane explorer={container} />),
|
||||
onCommandClick: () => useSidePanel.getState().openSidePanel("Load Query", <LoadQueryPane />),
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
|
@ -537,19 +547,25 @@ function createManageGitHubAccountButton(container: Explorer): CommandButtonComp
|
|||
};
|
||||
}
|
||||
|
||||
function createStaticCommandBarButtonsForResourceToken(container: Explorer): CommandButtonComponentProps[] {
|
||||
const newSqlQueryBtn = createNewSQLQueryButton(container);
|
||||
function createStaticCommandBarButtonsForResourceToken(
|
||||
container: Explorer,
|
||||
selectedNodeState: SelectedNodeState
|
||||
): CommandButtonComponentProps[] {
|
||||
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
||||
const openQueryBtn = createOpenQueryButton(container);
|
||||
|
||||
newSqlQueryBtn.disabled = !container.isResourceTokenCollectionNodeSelected();
|
||||
const isResourceTokenCollectionNodeSelected: boolean =
|
||||
container.resourceTokenCollection() &&
|
||||
container.resourceTokenCollection().id() === selectedNodeState.selectedNode?.id();
|
||||
newSqlQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
||||
newSqlQueryBtn.onCommandClick = () => {
|
||||
const resourceTokenCollection: ViewModels.CollectionBase = container.resourceTokenCollection();
|
||||
resourceTokenCollection && resourceTokenCollection.onNewQueryClick(resourceTokenCollection, undefined);
|
||||
};
|
||||
|
||||
openQueryBtn.disabled = !container.isResourceTokenCollectionNodeSelected();
|
||||
openQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
||||
if (!openQueryBtn.disabled) {
|
||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton(container)];
|
||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
||||
}
|
||||
|
||||
return [newSqlQueryBtn, openQueryBtn];
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
import Explorer from "../../Explorer";
|
||||
import { NewQueryTab } from "../../Tabs/QueryTab/QueryTab";
|
||||
import { useDatabases } from "../../useDatabases";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
|
||||
interface BrowseQueriesPaneProps {
|
||||
explorer: Explorer;
|
||||
|
@ -24,7 +25,7 @@ export const BrowseQueriesPane: FunctionComponent<BrowseQueriesPaneProps> = ({
|
|||
}: BrowseQueriesPaneProps): JSX.Element => {
|
||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||
const loadSavedQuery = (savedQuery: Query): void => {
|
||||
const selectedCollection: Collection = explorer && explorer.findSelectedCollection();
|
||||
const selectedCollection: Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
if (!selectedCollection) {
|
||||
// should never get into this state because this pane is only accessible through the query tab
|
||||
logError("No collection was selected", "BrowseQueriesPane.loadSavedQuery");
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
jest.mock("../../../Common/dataAccess/deleteCollection");
|
||||
jest.mock("../../../Shared/Telemetry/TelemetryProcessor");
|
||||
import { mount, ReactWrapper, shallow } from "enzyme";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import * as ko from "knockout";
|
||||
import React from "react";
|
||||
import { deleteCollection } from "../../../Common/dataAccess/deleteCollection";
|
||||
import DeleteFeedback from "../../../Common/DeleteFeedback";
|
||||
import { ApiKind, DatabaseAccount } from "../../../Contracts/DataModels";
|
||||
import { Collection, Database, TreeNode } from "../../../Contracts/ViewModels";
|
||||
import { Collection, Database } from "../../../Contracts/ViewModels";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { updateUserContext } from "../../../UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useDatabases } from "../../useDatabases";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import { DeleteCollectionConfirmationPane } from "./DeleteCollectionConfirmationPane";
|
||||
|
||||
describe("Delete Collection Confirmation Pane", () => {
|
||||
|
@ -54,47 +55,39 @@ describe("Delete Collection Confirmation Pane", () => {
|
|||
it("should return true if last collection and database does not have shared throughput else false", () => {
|
||||
const fakeExplorer = new Explorer();
|
||||
fakeExplorer.refreshAllDatabases = () => undefined;
|
||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
||||
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
collectionName: "container",
|
||||
};
|
||||
const wrapper = shallow(<DeleteCollectionConfirmationPane {...props} />);
|
||||
const wrapper = shallow(<DeleteCollectionConfirmationPane explorer={fakeExplorer} />);
|
||||
expect(wrapper.exists(".deleteCollectionFeedback")).toBe(false);
|
||||
|
||||
const database = { id: ko.observable("testDB") } as Database;
|
||||
database.collections = ko.observableArray<Collection>([{ id: ko.observable("testCollection") } as Collection]);
|
||||
database.nodeKind = "Database";
|
||||
database.isDatabaseShared = ko.computed(() => false);
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
wrapper.setProps(props);
|
||||
useSelectedNode.getState().setSelectedNode(database);
|
||||
wrapper.setProps({ explorer: fakeExplorer });
|
||||
expect(wrapper.exists(".deleteCollectionFeedback")).toBe(true);
|
||||
|
||||
props.explorer.isSelectedDatabaseShared = () => true;
|
||||
wrapper.setProps(props);
|
||||
database.isDatabaseShared = ko.computed(() => true);
|
||||
wrapper.setProps({ explorer: fakeExplorer });
|
||||
expect(wrapper.exists(".deleteCollectionFeedback")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("submit()", () => {
|
||||
let wrapper: ReactWrapper;
|
||||
const selectedCollectionId = "testCol";
|
||||
const databaseId = "testDatabase";
|
||||
const fakeExplorer = {} as Explorer;
|
||||
fakeExplorer.findSelectedCollection = () => {
|
||||
return {
|
||||
id: ko.observable<string>(selectedCollectionId),
|
||||
databaseId,
|
||||
rid: "test",
|
||||
} as Collection;
|
||||
};
|
||||
fakeExplorer.selectedCollectionId = ko.computed<string>(() => selectedCollectionId);
|
||||
fakeExplorer.selectedNode = ko.observable<TreeNode>();
|
||||
fakeExplorer.refreshAllDatabases = () => undefined;
|
||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
||||
const database = { id: ko.observable("testDB") } as Database;
|
||||
database.collections = ko.observableArray<Collection>([{ id: ko.observable("testCollection") } as Collection]);
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
const database = { id: ko.observable(databaseId) } as Database;
|
||||
const collection = {
|
||||
id: ko.observable(selectedCollectionId),
|
||||
nodeKind: "Collection",
|
||||
database,
|
||||
databaseId,
|
||||
} as Collection;
|
||||
database.collections = ko.observableArray<Collection>([collection]);
|
||||
database.isDatabaseShared = ko.computed(() => false);
|
||||
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
|
@ -112,15 +105,17 @@ describe("Delete Collection Confirmation Pane", () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
collectionName: "container",
|
||||
};
|
||||
wrapper = mount(<DeleteCollectionConfirmationPane {...props} />);
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
useSelectedNode.getState().setSelectedNode(collection);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
useDatabases.getState().clearDatabases();
|
||||
useSelectedNode.getState().setSelectedNode(undefined);
|
||||
});
|
||||
|
||||
it("should call delete collection", () => {
|
||||
const wrapper = mount(<DeleteCollectionConfirmationPane explorer={fakeExplorer} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
expect(wrapper.exists("#confirmCollectionId")).toBe(true);
|
||||
|
@ -137,6 +132,7 @@ describe("Delete Collection Confirmation Pane", () => {
|
|||
});
|
||||
|
||||
it("should record feedback", async () => {
|
||||
const wrapper = mount(<DeleteCollectionConfirmationPane explorer={fakeExplorer} />);
|
||||
expect(wrapper.exists("#confirmCollectionId")).toBe(true);
|
||||
wrapper
|
||||
.find("#confirmCollectionId")
|
||||
|
|
|
@ -14,6 +14,7 @@ import { getCollectionName } from "../../../Utils/APITypeUtils";
|
|||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useDatabases } from "../../useDatabases";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
|
||||
export interface DeleteCollectionConfirmationPaneProps {
|
||||
|
@ -24,19 +25,19 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
|||
explorer,
|
||||
}: DeleteCollectionConfirmationPaneProps) => {
|
||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||
const isLastCollection = useDatabases((state) => state.isLastCollection);
|
||||
const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState<string>("");
|
||||
const [inputCollectionName, setInputCollectionName] = useState<string>("");
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [isExecuting, setIsExecuting] = useState(false);
|
||||
|
||||
const shouldRecordFeedback = (): boolean => {
|
||||
return isLastCollection() && !explorer.isSelectedDatabaseShared();
|
||||
};
|
||||
const shouldRecordFeedback = (): boolean =>
|
||||
useDatabases.getState().isLastCollection() &&
|
||||
!useSelectedNode.getState().findSelectedDatabase()?.isDatabaseShared();
|
||||
|
||||
const collectionName = getCollectionName().toLocaleLowerCase();
|
||||
const paneTitle = "Delete " + collectionName;
|
||||
const onSubmit = async (): Promise<void> => {
|
||||
const collection = explorer.findSelectedCollection();
|
||||
const collection = useSelectedNode.getState().findSelectedCollection();
|
||||
if (!collection || inputCollectionName !== collection.id()) {
|
||||
const errorMessage = "Input " + collectionName + " name does not match the selected " + collectionName;
|
||||
setFormError(errorMessage);
|
||||
|
@ -61,7 +62,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
|||
await deleteCollection(collection.databaseId, collection.id());
|
||||
|
||||
setIsExecuting(false);
|
||||
explorer.selectedNode(collection.database);
|
||||
useSelectedNode.getState().setSelectedNode(collection.database);
|
||||
explorer.tabsManager?.closeTabsByComparator(
|
||||
(tab) => tab.node?.id() === collection.id() && (tab.node as Collection).databaseId === collection.databaseId
|
||||
);
|
||||
|
|
|
@ -2,15 +2,9 @@
|
|||
|
||||
exports[`Delete Collection Confirmation Pane submit() should call delete collection 1`] = `
|
||||
<DeleteCollectionConfirmationPane
|
||||
closePanel={[Function]}
|
||||
collectionName="container"
|
||||
explorer={
|
||||
Object {
|
||||
"findSelectedCollection": [Function],
|
||||
"isSelectedDatabaseShared": [Function],
|
||||
"refreshAllDatabases": [Function],
|
||||
"selectedCollectionId": [Function],
|
||||
"selectedNode": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
|
@ -372,355 +366,6 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||
</TextFieldBase>
|
||||
</StyledTextFieldBase>
|
||||
</div>
|
||||
<div
|
||||
className="deleteCollectionFeedback"
|
||||
>
|
||||
<Text
|
||||
block={true}
|
||||
variant="small"
|
||||
>
|
||||
<span
|
||||
className="css-66"
|
||||
>
|
||||
Help us improve Azure Cosmos DB!
|
||||
</span>
|
||||
</Text>
|
||||
<Text
|
||||
block={true}
|
||||
variant="small"
|
||||
>
|
||||
<span
|
||||
className="css-66"
|
||||
>
|
||||
What is the reason why you are deleting this
|
||||
container
|
||||
?
|
||||
</span>
|
||||
</Text>
|
||||
<StyledTextFieldBase
|
||||
id="deleteCollectionFeedbackInput"
|
||||
multiline={true}
|
||||
onChange={[Function]}
|
||||
rows={3}
|
||||
styles={
|
||||
Object {
|
||||
"fieldGroup": Object {
|
||||
"width": 300,
|
||||
},
|
||||
}
|
||||
}
|
||||
value=""
|
||||
>
|
||||
<TextFieldBase
|
||||
deferredValidationTime={200}
|
||||
id="deleteCollectionFeedbackInput"
|
||||
multiline={true}
|
||||
onChange={[Function]}
|
||||
resizable={true}
|
||||
rows={3}
|
||||
styles={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
"disableGlobalClassNames": false,
|
||||
"effects": Object {
|
||||
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
||||
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
||||
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
||||
"elevation8": "0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108)",
|
||||
"roundedCorner2": "2px",
|
||||
"roundedCorner4": "4px",
|
||||
"roundedCorner6": "6px",
|
||||
},
|
||||
"fonts": Object {
|
||||
"large": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "18px",
|
||||
"fontWeight": 400,
|
||||
},
|
||||
"medium": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "14px",
|
||||
"fontWeight": 400,
|
||||
},
|
||||
"mediumPlus": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "16px",
|
||||
"fontWeight": 400,
|
||||
},
|
||||
"mega": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "68px",
|
||||
"fontWeight": 600,
|
||||
},
|
||||
"small": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "12px",
|
||||
"fontWeight": 400,
|
||||
},
|
||||
"smallPlus": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "12px",
|
||||
"fontWeight": 400,
|
||||
},
|
||||
"superLarge": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "42px",
|
||||
"fontWeight": 600,
|
||||
},
|
||||
"tiny": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "10px",
|
||||
"fontWeight": 400,
|
||||
},
|
||||
"xLarge": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "20px",
|
||||
"fontWeight": 600,
|
||||
},
|
||||
"xLargePlus": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "24px",
|
||||
"fontWeight": 600,
|
||||
},
|
||||
"xSmall": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "10px",
|
||||
"fontWeight": 400,
|
||||
},
|
||||
"xxLarge": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "28px",
|
||||
"fontWeight": 600,
|
||||
},
|
||||
"xxLargePlus": Object {
|
||||
"MozOsxFontSmoothing": "grayscale",
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||
"fontSize": "32px",
|
||||
"fontWeight": 600,
|
||||
},
|
||||
},
|
||||
"isInverted": false,
|
||||
"palette": Object {
|
||||
"accent": "#0078d4",
|
||||
"black": "#000000",
|
||||
"blackTranslucent40": "rgba(0,0,0,.4)",
|
||||
"blue": "#0078d4",
|
||||
"blueDark": "#002050",
|
||||
"blueLight": "#00bcf2",
|
||||
"blueMid": "#00188f",
|
||||
"green": "#107c10",
|
||||
"greenDark": "#004b1c",
|
||||
"greenLight": "#bad80a",
|
||||
"magenta": "#b4009e",
|
||||
"magentaDark": "#5c005c",
|
||||
"magentaLight": "#e3008c",
|
||||
"neutralDark": "#201f1e",
|
||||
"neutralLight": "#edebe9",
|
||||
"neutralLighter": "#f3f2f1",
|
||||
"neutralLighterAlt": "#faf9f8",
|
||||
"neutralPrimary": "#323130",
|
||||
"neutralPrimaryAlt": "#3b3a39",
|
||||
"neutralQuaternary": "#d2d0ce",
|
||||
"neutralQuaternaryAlt": "#e1dfdd",
|
||||
"neutralSecondary": "#605e5c",
|
||||
"neutralSecondaryAlt": "#8a8886",
|
||||
"neutralTertiary": "#a19f9d",
|
||||
"neutralTertiaryAlt": "#c8c6c4",
|
||||
"orange": "#d83b01",
|
||||
"orangeLight": "#ea4300",
|
||||
"orangeLighter": "#ff8c00",
|
||||
"purple": "#5c2d91",
|
||||
"purpleDark": "#32145a",
|
||||
"purpleLight": "#b4a0ff",
|
||||
"red": "#e81123",
|
||||
"redDark": "#a4262c",
|
||||
"teal": "#008272",
|
||||
"tealDark": "#004b50",
|
||||
"tealLight": "#00b294",
|
||||
"themeDark": "#005a9e",
|
||||
"themeDarkAlt": "#106ebe",
|
||||
"themeDarker": "#004578",
|
||||
"themeLight": "#c7e0f4",
|
||||
"themeLighter": "#deecf9",
|
||||
"themeLighterAlt": "#eff6fc",
|
||||
"themePrimary": "#0078d4",
|
||||
"themeSecondary": "#2b88d8",
|
||||
"themeTertiary": "#71afe5",
|
||||
"white": "#ffffff",
|
||||
"whiteTranslucent40": "rgba(255,255,255,.4)",
|
||||
"yellow": "#ffb900",
|
||||
"yellowDark": "#d29200",
|
||||
"yellowLight": "#fff100",
|
||||
},
|
||||
"rtl": undefined,
|
||||
"semanticColors": Object {
|
||||
"accentButtonBackground": "#0078d4",
|
||||
"accentButtonText": "#ffffff",
|
||||
"actionLink": "#323130",
|
||||
"actionLinkHovered": "#201f1e",
|
||||
"blockingBackground": "#FDE7E9",
|
||||
"blockingIcon": "#FDE7E9",
|
||||
"bodyBackground": "#ffffff",
|
||||
"bodyBackgroundChecked": "#edebe9",
|
||||
"bodyBackgroundHovered": "#f3f2f1",
|
||||
"bodyDivider": "#edebe9",
|
||||
"bodyFrameBackground": "#ffffff",
|
||||
"bodyFrameDivider": "#edebe9",
|
||||
"bodyStandoutBackground": "#faf9f8",
|
||||
"bodySubtext": "#605e5c",
|
||||
"bodyText": "#323130",
|
||||
"bodyTextChecked": "#000000",
|
||||
"buttonBackground": "#ffffff",
|
||||
"buttonBackgroundChecked": "#c8c6c4",
|
||||
"buttonBackgroundCheckedHovered": "#edebe9",
|
||||
"buttonBackgroundDisabled": "#f3f2f1",
|
||||
"buttonBackgroundHovered": "#f3f2f1",
|
||||
"buttonBackgroundPressed": "#edebe9",
|
||||
"buttonBorder": "#8a8886",
|
||||
"buttonBorderDisabled": "#f3f2f1",
|
||||
"buttonText": "#323130",
|
||||
"buttonTextChecked": "#201f1e",
|
||||
"buttonTextCheckedHovered": "#000000",
|
||||
"buttonTextDisabled": "#a19f9d",
|
||||
"buttonTextHovered": "#201f1e",
|
||||
"buttonTextPressed": "#201f1e",
|
||||
"cardShadow": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
||||
"cardShadowHovered": "0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108)",
|
||||
"cardStandoutBackground": "#ffffff",
|
||||
"defaultStateBackground": "#faf9f8",
|
||||
"disabledBackground": "#f3f2f1",
|
||||
"disabledBodySubtext": "#c8c6c4",
|
||||
"disabledBodyText": "#a19f9d",
|
||||
"disabledBorder": "#c8c6c4",
|
||||
"disabledSubtext": "#d2d0ce",
|
||||
"disabledText": "#a19f9d",
|
||||
"errorBackground": "#FDE7E9",
|
||||
"errorIcon": "#A80000",
|
||||
"errorText": "#a4262c",
|
||||
"focusBorder": "#605e5c",
|
||||
"infoBackground": "#f3f2f1",
|
||||
"infoIcon": "#605e5c",
|
||||
"inputBackground": "#ffffff",
|
||||
"inputBackgroundChecked": "#0078d4",
|
||||
"inputBackgroundCheckedHovered": "#005a9e",
|
||||
"inputBorder": "#605e5c",
|
||||
"inputBorderHovered": "#323130",
|
||||
"inputFocusBorderAlt": "#0078d4",
|
||||
"inputForegroundChecked": "#ffffff",
|
||||
"inputIcon": "#0078d4",
|
||||
"inputIconDisabled": "#a19f9d",
|
||||
"inputIconHovered": "#005a9e",
|
||||
"inputPlaceholderBackgroundChecked": "#deecf9",
|
||||
"inputPlaceholderText": "#605e5c",
|
||||
"inputText": "#323130",
|
||||
"inputTextHovered": "#201f1e",
|
||||
"link": "#0078d4",
|
||||
"linkHovered": "#004578",
|
||||
"listBackground": "#ffffff",
|
||||
"listHeaderBackgroundHovered": "#f3f2f1",
|
||||
"listHeaderBackgroundPressed": "#edebe9",
|
||||
"listItemBackgroundChecked": "#edebe9",
|
||||
"listItemBackgroundCheckedHovered": "#e1dfdd",
|
||||
"listItemBackgroundHovered": "#f3f2f1",
|
||||
"listText": "#323130",
|
||||
"listTextColor": "#323130",
|
||||
"menuBackground": "#ffffff",
|
||||
"menuDivider": "#c8c6c4",
|
||||
"menuHeader": "#0078d4",
|
||||
"menuIcon": "#0078d4",
|
||||
"menuItemBackgroundChecked": "#edebe9",
|
||||
"menuItemBackgroundHovered": "#f3f2f1",
|
||||
"menuItemBackgroundPressed": "#edebe9",
|
||||
"menuItemText": "#323130",
|
||||
"menuItemTextHovered": "#201f1e",
|
||||
"messageLink": "#005A9E",
|
||||
"messageLinkHovered": "#004578",
|
||||
"messageText": "#323130",
|
||||
"primaryButtonBackground": "#0078d4",
|
||||
"primaryButtonBackgroundDisabled": "#f3f2f1",
|
||||
"primaryButtonBackgroundHovered": "#106ebe",
|
||||
"primaryButtonBackgroundPressed": "#005a9e",
|
||||
"primaryButtonBorder": "transparent",
|
||||
"primaryButtonText": "#ffffff",
|
||||
"primaryButtonTextDisabled": "#d2d0ce",
|
||||
"primaryButtonTextHovered": "#ffffff",
|
||||
"primaryButtonTextPressed": "#ffffff",
|
||||
"severeWarningBackground": "#FED9CC",
|
||||
"severeWarningIcon": "#D83B01",
|
||||
"smallInputBorder": "#605e5c",
|
||||
"successBackground": "#DFF6DD",
|
||||
"successIcon": "#107C10",
|
||||
"successText": "#107C10",
|
||||
"variantBorder": "#edebe9",
|
||||
"variantBorderHovered": "#a19f9d",
|
||||
"warningBackground": "#FFF4CE",
|
||||
"warningHighlight": "#ffb900",
|
||||
"warningIcon": "#797775",
|
||||
"warningText": "#323130",
|
||||
},
|
||||
"spacing": Object {
|
||||
"l1": "20px",
|
||||
"l2": "32px",
|
||||
"m": "16px",
|
||||
"s1": "8px",
|
||||
"s2": "4px",
|
||||
},
|
||||
}
|
||||
}
|
||||
validateOnLoad={true}
|
||||
value=""
|
||||
>
|
||||
<div
|
||||
className="ms-TextField ms-TextField--multiline root-55"
|
||||
>
|
||||
<div
|
||||
className="ms-TextField-wrapper"
|
||||
>
|
||||
<div
|
||||
className="ms-TextField-fieldGroup fieldGroup-67"
|
||||
>
|
||||
<textarea
|
||||
aria-invalid={false}
|
||||
className="ms-TextField-field field-68"
|
||||
id="deleteCollectionFeedbackInput"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
onInput={[Function]}
|
||||
rows={3}
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TextFieldBase>
|
||||
</StyledTextFieldBase>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PanelFooterComponent
|
||||
|
@ -2433,7 +2078,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||
>
|
||||
<button
|
||||
aria-label="OK"
|
||||
className="ms-Button ms-Button--primary root-70"
|
||||
className="ms-Button ms-Button--primary root-66"
|
||||
data-is-focusable={true}
|
||||
id="sidePanelOkButton"
|
||||
onClick={[Function]}
|
||||
|
@ -2445,16 +2090,16 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||
type="submit"
|
||||
>
|
||||
<span
|
||||
className="ms-Button-flexContainer flexContainer-71"
|
||||
className="ms-Button-flexContainer flexContainer-67"
|
||||
data-automationid="splitbuttonprimary"
|
||||
>
|
||||
<span
|
||||
className="ms-Button-textContainer textContainer-72"
|
||||
className="ms-Button-textContainer textContainer-68"
|
||||
>
|
||||
<span
|
||||
className="ms-Button-label label-74"
|
||||
id="id__6"
|
||||
key="id__6"
|
||||
className="ms-Button-label label-70"
|
||||
id="id__3"
|
||||
key="id__3"
|
||||
>
|
||||
OK
|
||||
</span>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
jest.mock("../../Common/dataAccess/deleteDatabase");
|
||||
jest.mock("../../Shared/Telemetry/TelemetryProcessor");
|
||||
import { mount, ReactWrapper, shallow } from "enzyme";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import * as ko from "knockout";
|
||||
import React from "react";
|
||||
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
|
||||
|
@ -13,121 +13,102 @@ import { updateUserContext } from "../../UserContext";
|
|||
import Explorer from "../Explorer";
|
||||
import { TabsManager } from "../Tabs/TabsManager";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { DeleteDatabaseConfirmationPanel } from "./DeleteDatabaseConfirmationPanel";
|
||||
|
||||
describe("Delete Database Confirmation Pane", () => {
|
||||
describe("shouldRecordFeedback()", () => {
|
||||
it("should return true if last non empty database or is last database that has shared throughput, else false", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
fakeExplorer.refreshAllDatabases = () => undefined;
|
||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
||||
const selectedDatabaseId = "testDatabase";
|
||||
let fakeExplorer: Explorer;
|
||||
let database: Database;
|
||||
|
||||
const database = {} as Database;
|
||||
database.collections = ko.observableArray<Collection>([{ id: ko.observable("testCollection") } as Collection]);
|
||||
database.id = ko.observable<string>("testDatabase");
|
||||
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
openNotificationConsole: (): void => undefined,
|
||||
selectedDatabase: database,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<DeleteDatabaseConfirmationPanel {...props} />);
|
||||
expect(wrapper.exists(".deleteDatabaseFeedback")).toBe(false);
|
||||
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
|
||||
wrapper.setProps(props);
|
||||
expect(wrapper.exists(".deleteDatabaseFeedback")).toBe(true);
|
||||
useDatabases.getState().clearDatabases();
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
name: "testDatabaseAccountName",
|
||||
properties: {
|
||||
cassandraEndpoint: "testEndpoint",
|
||||
},
|
||||
id: "testDatabaseAccountId",
|
||||
} as DatabaseAccount,
|
||||
apiType: "SQL",
|
||||
});
|
||||
(deleteDatabase as jest.Mock).mockResolvedValue(undefined);
|
||||
(TelemetryProcessor.trace as jest.Mock).mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
describe("submit()", () => {
|
||||
const selectedDatabaseId = "testDatabse";
|
||||
const database = { id: ko.observable("testDatabase") } as Database;
|
||||
beforeEach(() => {
|
||||
fakeExplorer = {} as Explorer;
|
||||
fakeExplorer.refreshAllDatabases = () => undefined;
|
||||
fakeExplorer.tabsManager = new TabsManager();
|
||||
|
||||
database = {} as Database;
|
||||
database.collections = ko.observableArray<Collection>([{ id: ko.observable("testCollection") } as Collection]);
|
||||
database.id = ko.observable<string>(selectedDatabaseId);
|
||||
const fakeExplorer = {} as Explorer;
|
||||
fakeExplorer.refreshAllDatabases = () => undefined;
|
||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
||||
fakeExplorer.tabsManager = new TabsManager();
|
||||
fakeExplorer.selectedNode = ko.observable();
|
||||
database.nodeKind = "Database";
|
||||
|
||||
let wrapper: ReactWrapper;
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
name: "testDatabaseAccountName",
|
||||
properties: {
|
||||
cassandraEndpoint: "testEndpoint",
|
||||
},
|
||||
id: "testDatabaseAccountId",
|
||||
} as DatabaseAccount,
|
||||
apiType: "SQL",
|
||||
});
|
||||
(deleteDatabase as jest.Mock).mockResolvedValue(undefined);
|
||||
(TelemetryProcessor.trace as jest.Mock).mockReturnValue(undefined);
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
openNotificationConsole: (): void => undefined,
|
||||
selectedDatabase: database,
|
||||
};
|
||||
|
||||
wrapper = mount(<DeleteDatabaseConfirmationPanel {...props} />);
|
||||
});
|
||||
|
||||
afterAll(() => useDatabases.getState().clearDatabases());
|
||||
|
||||
it("Should call delete database", () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.exists("#confirmDatabaseId")).toBe(true);
|
||||
|
||||
wrapper
|
||||
.find("#confirmDatabaseId")
|
||||
.hostNodes()
|
||||
.simulate("change", { target: { value: selectedDatabaseId } });
|
||||
expect(wrapper.exists("button")).toBe(true);
|
||||
wrapper.find("button").hostNodes().simulate("submit");
|
||||
expect(deleteDatabase).toHaveBeenCalledWith(selectedDatabaseId);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it("should record feedback", async () => {
|
||||
expect(wrapper.exists("#confirmDatabaseId")).toBe(true);
|
||||
wrapper
|
||||
.find("#confirmDatabaseId")
|
||||
.hostNodes()
|
||||
.simulate("change", { target: { value: selectedDatabaseId } });
|
||||
|
||||
expect(wrapper.exists("#deleteDatabaseFeedbackInput")).toBe(true);
|
||||
const feedbackText = "Test delete Database feedback text";
|
||||
wrapper
|
||||
.find("#deleteDatabaseFeedbackInput")
|
||||
.hostNodes()
|
||||
.simulate("change", { target: { value: feedbackText } });
|
||||
|
||||
expect(wrapper.exists("#sidePanelOkButton")).toBe(true);
|
||||
wrapper.find("#sidePanelOkButton").hostNodes().simulate("submit");
|
||||
expect(deleteDatabase).toHaveBeenCalledWith(selectedDatabaseId);
|
||||
|
||||
const deleteFeedback = new DeleteFeedback(
|
||||
"testDatabaseAccountId",
|
||||
"testDatabaseAccountName",
|
||||
ApiKind.SQL,
|
||||
feedbackText
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(TelemetryProcessor.trace).toHaveBeenCalledWith(Action.DeleteDatabase, ActionModifiers.Mark, {
|
||||
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback)),
|
||||
});
|
||||
wrapper.unmount();
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
useSelectedNode.getState().setSelectedNode(database);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
useDatabases.getState().clearDatabases();
|
||||
useSelectedNode.getState().setSelectedNode(undefined);
|
||||
});
|
||||
|
||||
it("shouldRecordFeedback() should return true if last non empty database or is last database that has shared throughput", () => {
|
||||
const wrapper = shallow(<DeleteDatabaseConfirmationPanel explorer={fakeExplorer} />);
|
||||
expect(wrapper.exists(".deleteDatabaseFeedback")).toBe(true);
|
||||
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
wrapper.setProps({ explorer: fakeExplorer });
|
||||
expect(wrapper.exists(".deleteDatabaseFeedback")).toBe(false);
|
||||
useDatabases.getState().clearDatabases();
|
||||
});
|
||||
|
||||
it("Should call delete database", () => {
|
||||
const wrapper = mount(<DeleteDatabaseConfirmationPanel explorer={fakeExplorer} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.exists("#confirmDatabaseId")).toBe(true);
|
||||
|
||||
wrapper
|
||||
.find("#confirmDatabaseId")
|
||||
.hostNodes()
|
||||
.simulate("change", { target: { value: selectedDatabaseId } });
|
||||
expect(wrapper.exists("button")).toBe(true);
|
||||
wrapper.find("button").hostNodes().simulate("submit");
|
||||
expect(deleteDatabase).toHaveBeenCalledWith(selectedDatabaseId);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it("should record feedback", async () => {
|
||||
const wrapper = mount(<DeleteDatabaseConfirmationPanel explorer={fakeExplorer} />);
|
||||
expect(wrapper.exists("#confirmDatabaseId")).toBe(true);
|
||||
wrapper
|
||||
.find("#confirmDatabaseId")
|
||||
.hostNodes()
|
||||
.simulate("change", { target: { value: selectedDatabaseId } });
|
||||
|
||||
expect(wrapper.exists("#deleteDatabaseFeedbackInput")).toBe(true);
|
||||
const feedbackText = "Test delete Database feedback text";
|
||||
wrapper
|
||||
.find("#deleteDatabaseFeedbackInput")
|
||||
.hostNodes()
|
||||
.simulate("change", { target: { value: feedbackText } });
|
||||
|
||||
expect(wrapper.exists("#sidePanelOkButton")).toBe(true);
|
||||
wrapper.find("#sidePanelOkButton").hostNodes().simulate("submit");
|
||||
expect(deleteDatabase).toHaveBeenCalledWith(selectedDatabaseId);
|
||||
|
||||
const deleteFeedback = new DeleteFeedback(
|
||||
"testDatabaseAccountId",
|
||||
"testDatabaseAccountName",
|
||||
ApiKind.SQL,
|
||||
feedbackText
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(TelemetryProcessor.trace).toHaveBeenCalledWith(Action.DeleteDatabase, ActionModifiers.Mark, {
|
||||
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback)),
|
||||
});
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,17 +14,16 @@ import { userContext } from "../../UserContext";
|
|||
import { logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../Explorer";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { PanelInfoErrorComponent, PanelInfoErrorProps } from "./PanelInfoErrorComponent";
|
||||
import { RightPaneForm, RightPaneFormProps } from "./RightPaneForm/RightPaneForm";
|
||||
|
||||
interface DeleteDatabaseConfirmationPanelProps {
|
||||
explorer: Explorer;
|
||||
selectedDatabase: Database;
|
||||
}
|
||||
|
||||
export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseConfirmationPanelProps> = ({
|
||||
explorer,
|
||||
selectedDatabase,
|
||||
}: DeleteDatabaseConfirmationPanelProps): JSX.Element => {
|
||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||
const isLastNonEmptyDatabase = useDatabases((state) => state.isLastNonEmptyDatabase);
|
||||
|
@ -33,6 +32,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
|||
const [formError, setFormError] = useState<string>("");
|
||||
const [databaseInput, setDatabaseInput] = useState<string>("");
|
||||
const [databaseFeedbackInput, setDatabaseFeedbackInput] = useState<string>("");
|
||||
const selectedDatabase: Database = useSelectedNode.getState().findSelectedDatabase();
|
||||
|
||||
const submit = async (): Promise<void> => {
|
||||
if (selectedDatabase?.id() && databaseInput !== selectedDatabase.id()) {
|
||||
|
@ -54,7 +54,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
|||
closeSidePanel();
|
||||
explorer.refreshAllDatabases();
|
||||
explorer.tabsManager.closeTabsByComparator((tab) => tab.node?.id() === selectedDatabase.id());
|
||||
explorer.selectedNode(undefined);
|
||||
useSelectedNode.getState().setSelectedNode(undefined);
|
||||
selectedDatabase
|
||||
.collections()
|
||||
.forEach((collection: Collection) =>
|
||||
|
|
|
@ -23,7 +23,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
|||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isSchemaEnabled": [Function],
|
||||
"isShellEnabled": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
|
@ -48,8 +47,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
|||
"container": [Circular],
|
||||
"parameters": [Function],
|
||||
},
|
||||
"selectedDatabaseId": [Function],
|
||||
"selectedNode": [Function],
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import { LoadQueryPane } from "./LoadQueryPane";
|
||||
|
||||
describe("Load Query Pane", () => {
|
||||
it("should render Default properly", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<LoadQueryPane {...props} />);
|
||||
const wrapper = shallow(<LoadQueryPane />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,14 +7,10 @@ import { Collection } from "../../../Contracts/ViewModels";
|
|||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
|
||||
interface LoadQueryPaneProps {
|
||||
explorer: Explorer;
|
||||
}
|
||||
|
||||
export const LoadQueryPane: FunctionComponent<LoadQueryPaneProps> = ({ explorer }: LoadQueryPaneProps): JSX.Element => {
|
||||
export const LoadQueryPane: FunctionComponent = (): JSX.Element => {
|
||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
|
@ -58,7 +54,7 @@ export const LoadQueryPane: FunctionComponent<LoadQueryPaneProps> = ({ explorer
|
|||
};
|
||||
|
||||
const loadQueryFromFile = async (file: File): Promise<void> => {
|
||||
const selectedCollection: Collection = explorer?.findSelectedCollection();
|
||||
const selectedCollection: Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
const reader = new FileReader();
|
||||
let fileData: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
@ -13,7 +13,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isSchemaEnabled": [Function],
|
||||
"isShellEnabled": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
|
@ -38,8 +37,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||
"container": [Circular],
|
||||
"parameters": [Function],
|
||||
},
|
||||
"selectedDatabaseId": [Function],
|
||||
"selectedNode": [Function],
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import { UploadItemsPane } from "./UploadItemsPane";
|
||||
const props = {
|
||||
explorer: new Explorer(),
|
||||
};
|
||||
|
||||
describe("Upload Items Pane", () => {
|
||||
it("should render Default properly", () => {
|
||||
const wrapper = shallow(<UploadItemsPane {...props} />);
|
||||
const wrapper = shallow(<UploadItemsPane />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,15 +3,11 @@ import React, { ChangeEvent, FunctionComponent, useState } from "react";
|
|||
import { Upload } from "../../../Common/Upload/Upload";
|
||||
import { UploadDetailsRecord } from "../../../Contracts/ViewModels";
|
||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import { getErrorMessage } from "../../Tables/Utilities";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
|
||||
export interface UploadItemsPaneProps {
|
||||
explorer: Explorer;
|
||||
}
|
||||
|
||||
export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ explorer }: UploadItemsPaneProps) => {
|
||||
export const UploadItemsPane: FunctionComponent = () => {
|
||||
const [files, setFiles] = useState<FileList>();
|
||||
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
|
@ -25,7 +21,7 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ explo
|
|||
return;
|
||||
}
|
||||
|
||||
const selectedCollection = explorer.findSelectedCollection();
|
||||
const selectedCollection = useSelectedNode.getState().findSelectedCollection();
|
||||
setIsExecuting(true);
|
||||
|
||||
selectedCollection
|
||||
|
|
|
@ -1,26 +1,16 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Delete Database Confirmation Pane submit() Should call delete database 1`] = `
|
||||
exports[`Delete Database Confirmation Pane Should call delete database 1`] = `
|
||||
<DeleteDatabaseConfirmationPanel
|
||||
closePanel={[Function]}
|
||||
explorer={
|
||||
Object {
|
||||
"isSelectedDatabaseShared": [Function],
|
||||
"refreshAllDatabases": [Function],
|
||||
"selectedNode": [Function],
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
},
|
||||
}
|
||||
}
|
||||
openNotificationConsole={[Function]}
|
||||
selectedDatabase={
|
||||
Object {
|
||||
"collections": [Function],
|
||||
"id": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<RightPaneForm
|
||||
formError=""
|
||||
|
|
|
@ -7,7 +7,6 @@ jest.mock("../Explorer");
|
|||
|
||||
const createExplorer = () => {
|
||||
const mock = new Explorer();
|
||||
mock.selectedNode = ko.observable();
|
||||
mock.isNotebookEnabled = ko.observable(false);
|
||||
mock.tabsManager = new TabsManager();
|
||||
return mock as jest.Mocked<Explorer>;
|
||||
|
|
|
@ -23,6 +23,7 @@ import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
|||
import Explorer from "../Explorer";
|
||||
import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
||||
export interface SplashScreenItem {
|
||||
iconSrc: string;
|
||||
|
@ -60,7 +61,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
|
||||
public componentDidMount() {
|
||||
this.subscriptions.push(
|
||||
this.container.selectedNode.subscribe(() => this.setState({})),
|
||||
{ dispose: useSelectedNode.subscribe(() => this.setState({})) },
|
||||
this.container.isNotebookEnabled.subscribe(() => this.setState({}))
|
||||
);
|
||||
}
|
||||
|
@ -228,12 +229,12 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
return items;
|
||||
}
|
||||
|
||||
if (!this.container.isDatabaseNodeOrNoneSelected()) {
|
||||
if (!useSelectedNode.getState().isDatabaseNodeOrNoneSelected()) {
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
items.push({
|
||||
iconSrc: NewQueryIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = this.container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null);
|
||||
},
|
||||
title: "New SQL Query",
|
||||
|
@ -243,7 +244,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
items.push({
|
||||
iconSrc: NewQueryIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = this.container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null);
|
||||
},
|
||||
title: "New Query",
|
||||
|
@ -266,20 +267,14 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
title: "New Stored Procedure",
|
||||
description: null,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = this.container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/* Scale & Settings */
|
||||
let isShared = false;
|
||||
if (this.container.isDatabaseNodeSelected()) {
|
||||
isShared = this.container.findSelectedDatabase().isDatabaseShared();
|
||||
} else if (this.container.isNodeKindSelected("Collection")) {
|
||||
const database: ViewModels.Database = this.container.findSelectedCollection().getDatabase();
|
||||
isShared = database && database.isDatabaseShared();
|
||||
}
|
||||
const isShared = useSelectedNode.getState().findSelectedDatabase()?.isDatabaseShared();
|
||||
|
||||
const label = isShared ? "Settings" : "Scale & Settings";
|
||||
items.push({
|
||||
|
@ -287,7 +282,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
title: label,
|
||||
description: null,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = this.container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onSettingsClick();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -28,6 +28,7 @@ import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandBu
|
|||
import Explorer from "../Explorer";
|
||||
import { AccessibleVerticalList } from "../Tree/AccessibleVerticalList";
|
||||
import DocumentId from "../Tree/DocumentId";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import template from "./DocumentsTab.html";
|
||||
import TabsBase from "./TabsBase";
|
||||
|
||||
|
@ -911,13 +912,13 @@ export default class DocumentsTab extends TabsBase {
|
|||
iconSrc: UploadIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && container.openUploadItemsPanePane();
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: useSelectedNode.getState().isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { EditorReact } from "../../Controls/Editor/EditorReact";
|
|||
import Explorer from "../../Explorer";
|
||||
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import ScriptTabBase from "../ScriptTabBase";
|
||||
|
||||
export interface IStorProcTabComponentAccessor {
|
||||
|
@ -298,12 +299,13 @@ export default class StoredProcedureTabComponent extends React.Component<
|
|||
}
|
||||
|
||||
const database: ViewModels.Database = this.props.collectionBase.getDatabase();
|
||||
const setSelectedNode = useSelectedNode.getState().setSelectedNode;
|
||||
if (!database.isDatabaseExpanded()) {
|
||||
this.props.collectionBase.container.selectedNode(database);
|
||||
setSelectedNode(database);
|
||||
} else if (!this.props.collectionBase.isCollectionExpanded() || !this.collection.isStoredProceduresExpanded()) {
|
||||
this.props.collectionBase.container.selectedNode(this.props.collectionBase);
|
||||
setSelectedNode(this.props.collectionBase);
|
||||
} else {
|
||||
this.props.collectionBase.container.selectedNode(this.node);
|
||||
setSelectedNode(this.node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
|||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../Explorer";
|
||||
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
|
||||
import { TabsManager } from "./TabsManager";
|
||||
// TODO: Use specific actions for logging telemetry data
|
||||
|
@ -74,12 +75,13 @@ export default class TabsBase extends WaitsForTemplateViewModel {
|
|||
|
||||
protected updateSelectedNode(): void {
|
||||
const relatedDatabase = (this.collection && this.collection.getDatabase()) || this.database;
|
||||
const setSelectedNode = useSelectedNode.getState().setSelectedNode;
|
||||
if (relatedDatabase && !relatedDatabase.isDatabaseExpanded()) {
|
||||
this.getContainer().selectedNode(relatedDatabase);
|
||||
setSelectedNode(relatedDatabase);
|
||||
} else if (this.collection && !this.collection.isCollectionExpanded()) {
|
||||
this.getContainer().selectedNode(this.collection);
|
||||
setSelectedNode(this.collection);
|
||||
} else {
|
||||
this.getContainer().selectedNode(this.node);
|
||||
setSelectedNode(this.node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,6 @@ describe("Collection", () => {
|
|||
function generateMockCollectionWithDataModel(data: DataModels.Collection): Collection {
|
||||
const mockContainer = {} as Explorer;
|
||||
|
||||
mockContainer.isDatabaseNodeOrNoneSelected = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
return generateCollection(mockContainer, "abc", data, {} as DataModels.Offer);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import { NewQueryTab } from "../Tabs/QueryTab/QueryTab";
|
|||
import QueryTablesTab from "../Tabs/QueryTablesTab";
|
||||
import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import ConflictId from "./ConflictId";
|
||||
import DocumentId from "./DocumentId";
|
||||
import StoredProcedure from "./StoredProcedure";
|
||||
|
@ -223,7 +224,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
}
|
||||
|
||||
public expandCollapseCollection() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Collection node",
|
||||
|
||||
|
@ -276,7 +277,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
}
|
||||
|
||||
public onDocumentDBDocumentsClick() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Documents node",
|
||||
|
@ -321,7 +322,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
}
|
||||
|
||||
public onConflictsClick() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Conflicts);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Conflicts node",
|
||||
|
@ -366,7 +367,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
}
|
||||
|
||||
public onTableEntitiesClick() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.QueryTables);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Entities node",
|
||||
|
@ -419,7 +420,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
}
|
||||
|
||||
public onGraphDocumentsClick() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Graph);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Documents node",
|
||||
|
@ -470,7 +471,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
}
|
||||
|
||||
public onMongoDBDocumentsClick = () => {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Documents node",
|
||||
|
@ -514,7 +515,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
};
|
||||
|
||||
public onSchemaAnalyzerClick = async () => {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.SchemaAnalyzer);
|
||||
const SchemaAnalyzerTab = await (await import("../Tabs/SchemaAnalyzerTab")).default;
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
|
@ -557,7 +558,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
};
|
||||
|
||||
public onSettingsClick = async (): Promise<void> => {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Settings node",
|
||||
|
@ -744,21 +745,21 @@ export default class Collection implements ViewModels.Collection {
|
|||
|
||||
public createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure {
|
||||
const node = new StoredProcedure(this.container, this, data);
|
||||
this.container.selectedNode(node);
|
||||
useSelectedNode.getState().setSelectedNode(node);
|
||||
this.children.push(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction {
|
||||
const node = new UserDefinedFunction(this.container, this, data);
|
||||
this.container.selectedNode(node);
|
||||
useSelectedNode.getState().setSelectedNode(node);
|
||||
this.children.push(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public createTriggerNode(data: TriggerDefinition & Resource): Trigger {
|
||||
const node = new Trigger(this.container, this, data);
|
||||
this.container.selectedNode(node);
|
||||
useSelectedNode.getState().setSelectedNode(node);
|
||||
this.children.push(node);
|
||||
return node;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import { logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
|||
import Explorer from "../Explorer";
|
||||
import { DatabaseSettingsTabV2 } from "../Tabs/SettingsTabV2";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import Collection from "./Collection";
|
||||
|
||||
export default class Database implements ViewModels.Database {
|
||||
|
@ -53,7 +54,7 @@ export default class Database implements ViewModels.Database {
|
|||
}
|
||||
|
||||
public onSettingsClick = () => {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.DatabaseSettings);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Settings node",
|
||||
|
@ -121,25 +122,6 @@ export default class Database implements ViewModels.Database {
|
|||
}
|
||||
};
|
||||
|
||||
public isDatabaseNodeSelected(): boolean {
|
||||
return (
|
||||
!this.isDatabaseExpanded() &&
|
||||
this.container.selectedNode &&
|
||||
this.container.selectedNode() &&
|
||||
this.container.selectedNode().nodeKind === "Database" &&
|
||||
this.container.selectedNode().id() === this.id()
|
||||
);
|
||||
}
|
||||
|
||||
public selectDatabase() {
|
||||
this.container.selectedNode(this);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Database node",
|
||||
|
||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||
});
|
||||
}
|
||||
|
||||
public async expandDatabase() {
|
||||
if (this.isDatabaseExpanded()) {
|
||||
return;
|
||||
|
|
|
@ -10,6 +10,7 @@ import DocumentsTab from "../Tabs/DocumentsTab";
|
|||
import { NewQueryTab } from "../Tabs/QueryTab/QueryTab";
|
||||
import TabsBase from "../Tabs/TabsBase";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import DocumentId from "./DocumentId";
|
||||
|
||||
export default class ResourceTokenCollection implements ViewModels.CollectionBase {
|
||||
|
@ -104,7 +105,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||
}
|
||||
|
||||
public onDocumentDBDocumentsClick() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Documents node",
|
||||
|
|
|
@ -1,114 +1,101 @@
|
|||
import * as ko from "knockout";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import Explorer from "../Explorer";
|
||||
import TabsBase from "../Tabs/TabsBase";
|
||||
import { ResourceTreeAdapter } from "./ResourceTreeAdapter";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
||||
describe("ResourceTreeAdapter", () => {
|
||||
const mockContainer = (): Explorer =>
|
||||
(({
|
||||
selectedNode: ko.observable<ViewModels.TreeNode>({
|
||||
nodeKind: "nodeKind",
|
||||
rid: "rid",
|
||||
id: ko.observable<string>("id"),
|
||||
}),
|
||||
tabsManager: {
|
||||
activeTab: ko.observable<TabsBase>({
|
||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||
} as TabsBase),
|
||||
},
|
||||
isNotebookEnabled: ko.observable<boolean>(true),
|
||||
databases: ko.observable<ViewModels.Database[]>([]),
|
||||
} as unknown) as Explorer);
|
||||
describe("useSelectedNode.getState()", () => {
|
||||
const mockTab = {
|
||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||
} as TabsBase;
|
||||
|
||||
// TODO isDataNodeSelected needs a better design and refactor, but for now, we protect some of the code paths
|
||||
describe("isDataNodeSelected", () => {
|
||||
afterEach(() => useSelectedNode.getState().setSelectedNode(undefined));
|
||||
it("it should not select if no selected node", () => {
|
||||
const explorer = mockContainer();
|
||||
explorer.selectedNode(undefined);
|
||||
const resourceTreeAdapter = new ResourceTreeAdapter(explorer);
|
||||
const isDataNodeSelected = resourceTreeAdapter.isDataNodeSelected("foo", "bar", undefined);
|
||||
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected(mockTab, "foo", "bar", undefined);
|
||||
expect(isDataNodeSelected).toBeFalsy();
|
||||
});
|
||||
|
||||
it("it should not select incorrect subnodekinds", () => {
|
||||
const resourceTreeAdapter = new ResourceTreeAdapter(mockContainer());
|
||||
const isDataNodeSelected = resourceTreeAdapter.isDataNodeSelected("foo", "bar", undefined);
|
||||
useSelectedNode.getState().setSelectedNode({
|
||||
nodeKind: "nodeKind",
|
||||
rid: "rid",
|
||||
id: ko.observable<string>("id"),
|
||||
});
|
||||
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected(mockTab, "foo", "bar", undefined);
|
||||
expect(isDataNodeSelected).toBeFalsy();
|
||||
});
|
||||
|
||||
it("it should not select if no active tab", () => {
|
||||
const explorer = mockContainer();
|
||||
explorer.tabsManager.activeTab(undefined);
|
||||
const resourceTreeAdapter = new ResourceTreeAdapter(explorer);
|
||||
const isDataNodeSelected = resourceTreeAdapter.isDataNodeSelected("foo", "bar", undefined);
|
||||
useSelectedNode.getState().setSelectedNode({
|
||||
nodeKind: "nodeKind",
|
||||
rid: "rid",
|
||||
id: ko.observable<string>("id"),
|
||||
});
|
||||
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected(undefined, "foo", "bar", undefined);
|
||||
expect(isDataNodeSelected).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should select if correct database node regardless of subnodekinds", () => {
|
||||
const subNodeKind = ViewModels.CollectionTabKind.Documents;
|
||||
const explorer = mockContainer();
|
||||
explorer.selectedNode(({
|
||||
useSelectedNode.getState().setSelectedNode({
|
||||
nodeKind: "Database",
|
||||
rid: "dbrid",
|
||||
id: ko.observable<string>("dbid"),
|
||||
selectedSubnodeKind: ko.observable<ViewModels.CollectionTabKind>(subNodeKind),
|
||||
} as unknown) as ViewModels.TreeNode);
|
||||
const resourceTreeAdapter = new ResourceTreeAdapter(explorer);
|
||||
const isDataNodeSelected = resourceTreeAdapter.isDataNodeSelected("dbid", undefined, [
|
||||
ViewModels.CollectionTabKind.Documents,
|
||||
]);
|
||||
} as ViewModels.TreeNode);
|
||||
const isDataNodeSelected = useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(mockTab, "dbid", undefined, [ViewModels.CollectionTabKind.Documents]);
|
||||
expect(isDataNodeSelected).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should select correct collection node (documents or graph node)", () => {
|
||||
let subNodeKind = ViewModels.CollectionTabKind.Documents;
|
||||
const explorer = mockContainer();
|
||||
explorer.tabsManager.activeTab({
|
||||
let activeTab = {
|
||||
tabKind: subNodeKind,
|
||||
} as TabsBase);
|
||||
explorer.selectedNode(({
|
||||
} as TabsBase;
|
||||
useSelectedNode.getState().setSelectedNode({
|
||||
nodeKind: "Collection",
|
||||
rid: "collrid",
|
||||
databaseId: "dbid",
|
||||
id: ko.observable<string>("collid"),
|
||||
selectedSubnodeKind: ko.observable<ViewModels.CollectionTabKind>(subNodeKind),
|
||||
} as unknown) as ViewModels.TreeNode);
|
||||
const resourceTreeAdapter = new ResourceTreeAdapter(explorer);
|
||||
let isDataNodeSelected = resourceTreeAdapter.isDataNodeSelected("dbid", "collid", [subNodeKind]);
|
||||
} as ViewModels.TreeNode);
|
||||
let isDataNodeSelected = useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(activeTab, "dbid", "collid", [subNodeKind]);
|
||||
expect(isDataNodeSelected).toBeTruthy();
|
||||
|
||||
subNodeKind = ViewModels.CollectionTabKind.Graph;
|
||||
explorer.tabsManager.activeTab({
|
||||
activeTab = {
|
||||
tabKind: subNodeKind,
|
||||
} as TabsBase);
|
||||
explorer.selectedNode(({
|
||||
} as TabsBase;
|
||||
useSelectedNode.getState().setSelectedNode({
|
||||
nodeKind: "Collection",
|
||||
rid: "collrid",
|
||||
databaseId: "dbid",
|
||||
id: ko.observable<string>("collid"),
|
||||
selectedSubnodeKind: ko.observable<ViewModels.CollectionTabKind>(subNodeKind),
|
||||
} as unknown) as ViewModels.TreeNode);
|
||||
isDataNodeSelected = resourceTreeAdapter.isDataNodeSelected("dbid", "collid", [subNodeKind]);
|
||||
} as ViewModels.TreeNode);
|
||||
isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected(activeTab, "dbid", "collid", [subNodeKind]);
|
||||
expect(isDataNodeSelected).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should not select incorrect collection node (e.g. Settings)", () => {
|
||||
const explorer = mockContainer();
|
||||
explorer.selectedNode(({
|
||||
useSelectedNode.getState().setSelectedNode({
|
||||
nodeKind: "Collection",
|
||||
rid: "collrid",
|
||||
databaseId: "dbid",
|
||||
id: ko.observable<string>("collid"),
|
||||
selectedSubnodeKind: ko.observable<ViewModels.CollectionTabKind>(ViewModels.CollectionTabKind.Documents),
|
||||
} as unknown) as ViewModels.TreeNode);
|
||||
explorer.tabsManager.activeTab({
|
||||
} as ViewModels.TreeNode);
|
||||
const activeTab = {
|
||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||
} as TabsBase);
|
||||
const resourceTreeAdapter = new ResourceTreeAdapter(explorer);
|
||||
const isDataNodeSelected = resourceTreeAdapter.isDataNodeSelected("dbid", "collid", [
|
||||
ViewModels.CollectionTabKind.Settings,
|
||||
]);
|
||||
} as TabsBase;
|
||||
const isDataNodeSelected = useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(activeTab, "dbid", "collid", [ViewModels.CollectionTabKind.Settings]);
|
||||
expect(isDataNodeSelected).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { shallow } from "enzyme";
|
||||
import * as ko from "knockout";
|
||||
import React from "react";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
|
@ -208,13 +207,6 @@ const schema: DataModels.ISchema = {
|
|||
],
|
||||
};
|
||||
|
||||
const createMockContainer = (): Explorer => {
|
||||
const mockContainer = new Explorer();
|
||||
mockContainer.selectedNode = ko.observable<ViewModels.TreeNode>();
|
||||
|
||||
return mockContainer;
|
||||
};
|
||||
|
||||
const createMockCollection = (): ViewModels.Collection => {
|
||||
const mockCollection = {} as DataModels.Collection;
|
||||
mockCollection._rid = "fakeRid";
|
||||
|
@ -223,17 +215,13 @@ const createMockCollection = (): ViewModels.Collection => {
|
|||
mockCollection.analyticalStorageTtl = 0;
|
||||
mockCollection.schema = schema;
|
||||
|
||||
const mockCollectionVM: ViewModels.Collection = new Collection(
|
||||
createMockContainer(),
|
||||
"fakeDatabaseId",
|
||||
mockCollection
|
||||
);
|
||||
const mockCollectionVM: ViewModels.Collection = new Collection(new Explorer(), "fakeDatabaseId", mockCollection);
|
||||
|
||||
return mockCollectionVM;
|
||||
};
|
||||
|
||||
describe("Resource tree for schema", () => {
|
||||
const mockContainer: Explorer = createMockContainer();
|
||||
const mockContainer = new Explorer();
|
||||
const resourceTree = new ResourceTreeAdapter(mockContainer);
|
||||
|
||||
it("should render", () => {
|
||||
|
|
|
@ -33,6 +33,7 @@ import { NotebookContentItem, NotebookContentItemType } from "../Notebook/Notebo
|
|||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||
import TabsBase from "../Tabs/TabsBase";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import StoredProcedure from "./StoredProcedure";
|
||||
import Trigger from "./Trigger";
|
||||
import UserDefinedFunction from "./UserDefinedFunction";
|
||||
|
@ -54,7 +55,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
public constructor(private container: Explorer) {
|
||||
this.parameters = ko.observable(Date.now());
|
||||
|
||||
this.container.selectedNode.subscribe((newValue: any) => this.triggerRender());
|
||||
useSelectedNode.subscribe(() => this.triggerRender());
|
||||
this.container.tabsManager.activeTab.subscribe((newValue: TabsBase) => this.triggerRender());
|
||||
this.container.isNotebookEnabled.subscribe((newValue) => this.triggerRender());
|
||||
|
||||
|
@ -183,7 +184,8 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
isExpanded: false,
|
||||
className: "databaseHeader",
|
||||
children: [],
|
||||
isSelected: () => this.isDataNodeSelected(database.id()),
|
||||
isSelected: () =>
|
||||
useSelectedNode.getState().isDataNodeSelected(this.container.tabsManager.activeTab(), database.id()),
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createDatabaseContextMenu(this.container, database.id()),
|
||||
onClick: async (isExpanded) => {
|
||||
// Rewritten version of expandCollapseDatabase():
|
||||
|
@ -196,18 +198,22 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
await database.expandDatabase();
|
||||
}
|
||||
databaseNode.isLoading = false;
|
||||
database.selectDatabase();
|
||||
useSelectedNode.getState().setSelectedNode(database);
|
||||
useCommandBar.getState().setContextButtons([]);
|
||||
this.container.tabsManager.refreshActiveTab((tab: TabsBase) => tab.collection?.databaseId === database.id());
|
||||
},
|
||||
onContextMenuOpen: () => this.container.selectedNode(database),
|
||||
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(database),
|
||||
};
|
||||
|
||||
if (database.isDatabaseShared()) {
|
||||
databaseNode.children.push({
|
||||
label: "Scale",
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(database.id(), undefined, [ViewModels.CollectionTabKind.DatabaseSettings]),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), database.id(), undefined, [
|
||||
ViewModels.CollectionTabKind.DatabaseSettings,
|
||||
]),
|
||||
onClick: database.onSettingsClick.bind(database),
|
||||
});
|
||||
}
|
||||
|
@ -253,10 +259,12 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
mostRecentActivity.collectionWasOpened(userContext.databaseAccount?.id, collection);
|
||||
},
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.Documents,
|
||||
ViewModels.CollectionTabKind.Graph,
|
||||
]),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.Documents,
|
||||
ViewModels.CollectionTabKind.Graph,
|
||||
]),
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(this.container, collection),
|
||||
});
|
||||
|
||||
|
@ -265,9 +273,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
label: "Schema (Preview)",
|
||||
onClick: collection.onSchemaAnalyzerClick.bind(collection),
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.SchemaAnalyzer,
|
||||
]),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.SchemaAnalyzer,
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -276,7 +286,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
label: database.isDatabaseShared() || isServerlessAccount() ? "Settings" : "Scale & Settings",
|
||||
onClick: collection.onSettingsClick.bind(collection),
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(collection.databaseId, collection.id(), [ViewModels.CollectionTabKind.Settings]),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.Settings,
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -302,7 +316,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
label: "Conflicts",
|
||||
onClick: collection.onConflictsClick.bind(collection),
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(collection.databaseId, collection.id(), [ViewModels.CollectionTabKind.Conflicts]),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.Conflicts,
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -315,7 +333,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(this.container, collection),
|
||||
onClick: () => {
|
||||
// Rewritten version of expandCollapseCollection
|
||||
this.container.selectedNode(collection);
|
||||
useSelectedNode.getState().setSelectedNode(collection);
|
||||
useCommandBar.getState().setContextButtons([]);
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: TabsBase) =>
|
||||
|
@ -329,8 +347,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
collection.loadTriggers();
|
||||
}
|
||||
},
|
||||
isSelected: () => this.isDataNodeSelected(collection.databaseId, collection.id()),
|
||||
onContextMenuOpen: () => this.container.selectedNode(collection),
|
||||
isSelected: () =>
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id()),
|
||||
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(collection),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -341,9 +362,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
label: sp.id(),
|
||||
onClick: sp.open.bind(sp),
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.StoredProcedures,
|
||||
]),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.StoredProcedures,
|
||||
]),
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createStoreProcedureContextMenuItems(this.container, sp),
|
||||
})),
|
||||
onClick: () => {
|
||||
|
@ -363,9 +386,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
label: udf.id(),
|
||||
onClick: udf.open.bind(udf),
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.UserDefinedFunctions,
|
||||
]),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.UserDefinedFunctions,
|
||||
]),
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createUserDefinedFunctionContextMenuItems(
|
||||
this.container,
|
||||
udf
|
||||
|
@ -388,7 +413,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
label: trigger.id(),
|
||||
onClick: trigger.open.bind(trigger),
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(collection.databaseId, collection.id(), [ViewModels.CollectionTabKind.Triggers]),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.Triggers,
|
||||
]),
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createTriggerContextMenuItems(this.container, trigger),
|
||||
})),
|
||||
onClick: () => {
|
||||
|
@ -818,44 +847,4 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
public triggerRender() {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
|
||||
/**
|
||||
* public for testing purposes
|
||||
* @param databaseId
|
||||
* @param collectionId
|
||||
* @param subnodeKinds
|
||||
*/
|
||||
public isDataNodeSelected(
|
||||
databaseId: string,
|
||||
collectionId?: string,
|
||||
subnodeKinds?: ViewModels.CollectionTabKind[]
|
||||
): boolean {
|
||||
if (!this.container.selectedNode || !this.container.selectedNode()) {
|
||||
return false;
|
||||
}
|
||||
const selectedNode = this.container.selectedNode();
|
||||
const isNodeSelected = collectionId
|
||||
? (selectedNode as ViewModels.Collection).databaseId === databaseId && selectedNode.id() === collectionId
|
||||
: selectedNode.id() === databaseId;
|
||||
|
||||
if (!isNodeSelected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (subnodeKinds === undefined || !Array.isArray(subnodeKinds)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const activeTab = this.container.tabsManager.activeTab();
|
||||
const selectedSubnodeKind = collectionId
|
||||
? (selectedNode as ViewModels.Collection).selectedSubnodeKind()
|
||||
: (selectedNode as ViewModels.Database).selectedSubnodeKind();
|
||||
|
||||
return (
|
||||
activeTab &&
|
||||
subnodeKinds.includes(activeTab.tabKind) &&
|
||||
selectedSubnodeKind !== undefined &&
|
||||
subnodeKinds.includes(selectedSubnodeKind)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import { ResourceTreeAdapterForResourceToken } from "./ResourceTreeAdapterForRes
|
|||
const createMockContainer = (): Explorer => {
|
||||
let mockContainer = {} as Explorer;
|
||||
mockContainer.resourceTokenCollection = createMockCollection(mockContainer);
|
||||
mockContainer.selectedNode = ko.observable<ViewModels.TreeNode>();
|
||||
|
||||
return mockContainer;
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ import Explorer from "../Explorer";
|
|||
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
||||
import { NotebookContentItem } from "../Notebook/NotebookContentItem";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
||||
export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
||||
public parameters: ko.Observable<number>;
|
||||
|
@ -18,7 +19,7 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||
this.parameters = ko.observable(Date.now());
|
||||
|
||||
this.container.resourceTokenCollection.subscribe(() => this.triggerRender());
|
||||
this.container.selectedNode.subscribe((newValue: any) => this.triggerRender());
|
||||
useSelectedNode.subscribe(() => this.triggerRender());
|
||||
this.container.tabsManager && this.container.tabsManager.activeTab.subscribe(() => this.triggerRender());
|
||||
|
||||
this.triggerRender();
|
||||
|
@ -48,7 +49,11 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||
mostRecentActivity.collectionWasOpened(userContext.databaseAccount?.id, collection);
|
||||
},
|
||||
isSelected: () =>
|
||||
this.isDataNodeSelected(collection.databaseId, collection.id(), ViewModels.CollectionTabKind.Documents),
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
|
||||
ViewModels.CollectionTabKind.Documents,
|
||||
]),
|
||||
});
|
||||
|
||||
const collectionNode: TreeNode = {
|
||||
|
@ -59,13 +64,16 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||
className: "collectionHeader",
|
||||
onClick: () => {
|
||||
// Rewritten version of expandCollapseCollection
|
||||
this.container.selectedNode(collection);
|
||||
useSelectedNode.getState().setSelectedNode(collection);
|
||||
useCommandBar.getState().setContextButtons([]);
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab) => tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
|
||||
);
|
||||
},
|
||||
isSelected: () => this.isDataNodeSelected(collection.databaseId, collection.id()),
|
||||
isSelected: () =>
|
||||
useSelectedNode
|
||||
.getState()
|
||||
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id()),
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -75,35 +83,6 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||
};
|
||||
}
|
||||
|
||||
public isDataNodeSelected(
|
||||
databaseId: string,
|
||||
collectionId?: string,
|
||||
subnodeKind?: ViewModels.CollectionTabKind
|
||||
): boolean {
|
||||
if (!this.container.selectedNode || !this.container.selectedNode()) {
|
||||
return false;
|
||||
}
|
||||
const selectedNode = this.container.selectedNode();
|
||||
const isNodeSelected = collectionId
|
||||
? (selectedNode as ViewModels.Collection).databaseId === databaseId && selectedNode.id() === collectionId
|
||||
: selectedNode.id() === databaseId;
|
||||
|
||||
if (!isNodeSelected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subnodeKind) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const activeTab = this.container.tabsManager.activeTab();
|
||||
const selectedSubnodeKind = collectionId
|
||||
? (selectedNode as ViewModels.Collection).selectedSubnodeKind()
|
||||
: (selectedNode as ViewModels.Database).selectedSubnodeKind();
|
||||
|
||||
return activeTab && activeTab.tabKind === subnodeKind && selectedSubnodeKind === subnodeKind;
|
||||
}
|
||||
|
||||
public triggerRender() {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import Explorer from "../Explorer";
|
|||
import { getErrorMessage } from "../Tables/Utilities";
|
||||
import { NewStoredProcedureTab } from "../Tabs/StoredProcedureTab/StoredProcedureTab";
|
||||
import TabsBase from "../Tabs/TabsBase";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
||||
const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE
|
||||
function sample(prefix) {
|
||||
|
@ -87,7 +88,7 @@ export default class StoredProcedure {
|
|||
}
|
||||
|
||||
public select() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Stored procedure node",
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
|
|||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import Explorer from "../Explorer";
|
||||
import TriggerTab from "../Tabs/TriggerTab";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
||||
export default class Trigger {
|
||||
public nodeKind: string;
|
||||
|
@ -32,7 +33,7 @@ export default class Trigger {
|
|||
}
|
||||
|
||||
public select() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "Trigger node",
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
|
|||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import Explorer from "../Explorer";
|
||||
import UserDefinedFunctionTab from "../Tabs/UserDefinedFunctionTab";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
||||
export default class UserDefinedFunction {
|
||||
public nodeKind: string;
|
||||
|
@ -82,7 +83,7 @@ export default class UserDefinedFunction {
|
|||
};
|
||||
|
||||
public select() {
|
||||
this.container.selectedNode(this);
|
||||
useSelectedNode.getState().setSelectedNode(this);
|
||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||
description: "UDF item node",
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import _ from "underscore";
|
||||
import create, { UseStore } from "zustand";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import TabsBase from "./Tabs/TabsBase";
|
||||
import { useDatabases } from "./useDatabases";
|
||||
|
||||
export interface SelectedNodeState {
|
||||
selectedNode: ViewModels.TreeNode;
|
||||
setSelectedNode: (node: ViewModels.TreeNode) => void;
|
||||
isDatabaseNodeOrNoneSelected: () => boolean;
|
||||
findSelectedDatabase: () => ViewModels.Database;
|
||||
findSelectedCollection: () => ViewModels.Collection;
|
||||
isDataNodeSelected: (
|
||||
activeTab: TabsBase,
|
||||
databaseId: string,
|
||||
collectionId?: string,
|
||||
subnodeKinds?: ViewModels.CollectionTabKind[]
|
||||
) => boolean;
|
||||
}
|
||||
|
||||
export const useSelectedNode: UseStore<SelectedNodeState> = create((set, get) => ({
|
||||
selectedNode: undefined,
|
||||
setSelectedNode: (node: ViewModels.TreeNode) => set({ selectedNode: node }),
|
||||
isDatabaseNodeOrNoneSelected: (): boolean => {
|
||||
const selectedNode = get().selectedNode;
|
||||
return !selectedNode || selectedNode.nodeKind === "Database";
|
||||
},
|
||||
findSelectedDatabase: (): ViewModels.Database => {
|
||||
const selectedNode = get().selectedNode;
|
||||
if (!selectedNode) {
|
||||
return undefined;
|
||||
}
|
||||
if (selectedNode.nodeKind === "Database") {
|
||||
return _.find(
|
||||
useDatabases.getState().databases,
|
||||
(database: ViewModels.Database) => database.id() === selectedNode.id()
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedNode.nodeKind === "Collection") {
|
||||
return selectedNode.database;
|
||||
}
|
||||
|
||||
return selectedNode.collection?.database;
|
||||
},
|
||||
findSelectedCollection: (): ViewModels.Collection => {
|
||||
const selectedNode = get().selectedNode;
|
||||
return (selectedNode.nodeKind === "Collection" ? selectedNode : selectedNode.collection) as ViewModels.Collection;
|
||||
},
|
||||
isDataNodeSelected: (
|
||||
activeTab: TabsBase,
|
||||
databaseId: string,
|
||||
collectionId?: string,
|
||||
subnodeKinds?: ViewModels.CollectionTabKind[]
|
||||
): boolean => {
|
||||
const selectedNode = get().selectedNode;
|
||||
if (!selectedNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isNodeSelected = collectionId
|
||||
? (selectedNode as ViewModels.Collection).databaseId === databaseId && selectedNode.id() === collectionId
|
||||
: selectedNode.id() === databaseId;
|
||||
|
||||
if (!isNodeSelected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (subnodeKinds === undefined || !Array.isArray(subnodeKinds)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const selectedSubnodeKind = collectionId
|
||||
? (selectedNode as ViewModels.Collection).selectedSubnodeKind()
|
||||
: (selectedNode as ViewModels.Database).selectedSubnodeKind();
|
||||
|
||||
return (
|
||||
activeTab &&
|
||||
subnodeKinds.includes(activeTab.tabKind) &&
|
||||
selectedSubnodeKind !== undefined &&
|
||||
subnodeKinds.includes(selectedSubnodeKind)
|
||||
);
|
||||
},
|
||||
}));
|
Loading…
Reference in New Issue