diff --git a/src/Contracts/ViewModels.ts b/src/Contracts/ViewModels.ts index 7dcea3024..210d34075 100644 --- a/src/Contracts/ViewModels.ts +++ b/src/Contracts/ViewModels.ts @@ -89,7 +89,6 @@ export interface Database extends TreeNode { selectedSubnodeKind: ko.Observable; - selectDatabase(): void; expandDatabase(): Promise; collapseDatabase(): void; diff --git a/src/Explorer/ContextMenuButtonFactory.tsx b/src/Explorer/ContextMenuButtonFactory.tsx index fa86a70bd..4b1b52054 100644 --- a/src/Explorer/ContextMenuButtonFactory.tsx +++ b/src/Explorer/ContextMenuButtonFactory.tsx @@ -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(), - - ), + .openSidePanel("Delete " + getDatabaseName(), ), 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", diff --git a/src/Explorer/Controls/Settings/SettingsComponent.test.tsx b/src/Explorer/Controls/Settings/SettingsComponent.test.tsx index 0082db7e9..d14fe1cc4 100644 --- a/src/Explorer/Controls/Settings/SettingsComponent.test.tsx +++ b/src/Explorer/Controls/Settings/SettingsComponent.test.tsx @@ -126,7 +126,6 @@ describe("SettingsComponent", () => { isDatabaseExpanded: undefined, isDatabaseShared: ko.computed(() => true), selectedSubnodeKind: undefined, - selectDatabase: undefined, expandDatabase: undefined, collapseDatabase: undefined, loadCollections: undefined, diff --git a/src/Explorer/Controls/Settings/SettingsUtils.test.tsx b/src/Explorer/Controls/Settings/SettingsUtils.test.tsx index 81c025c1c..1a7e4d79b 100644 --- a/src/Explorer/Controls/Settings/SettingsUtils.test.tsx +++ b/src/Explorer/Controls/Settings/SettingsUtils.test.tsx @@ -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, diff --git a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap index a79ed9285..e34b56fb7 100644 --- a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap +++ b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap @@ -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], diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index 87122177d..a4a53017f 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -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; - public selectedCollectionId: ko.Computed; - public selectedNode: ko.Observable; private resourceTree: ResourceTreeAdapter; // Resource Token public resourceTokenCollection: ko.Observable; - public isResourceTokenCollectionNodeSelected: ko.Computed; public resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken; // Tabs @@ -163,18 +160,10 @@ export default class Explorer { this.resourceTokenCollection = ko.observable(); this.isSchemaEnabled = ko.computed(() => userContext.features.enableSchema); - this.selectedNode = ko.observable(); - this.selectedNode.subscribe((nodeSelected: ViewModels.TreeNode) => { + useSelectedNode.subscribe(() => { // Make sure switching tabs restores tabs display this.isTabsContentExpanded(false); }); - this.isResourceTokenCollectionNodeSelected = ko.computed(() => { - 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(() => { - 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 { 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 { // 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(), ); + useSidePanel.getState().openSidePanel("Upload " + getUploadName(), ); } public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void { useSidePanel diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx b/src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx index efaa5a3dd..03a3c1ef8 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx @@ -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 = create((set) => ({ })); export const CommandBar: React.FC = ({ 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); diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.test.ts b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.test.ts index b0e2feeec..3e8ae6b04 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.test.ts +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.test.ts @@ -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); diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx index 36b1513af..35f2ee82d 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx @@ -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", ), + onCommandClick: () => useSidePanel.getState().openSidePanel("Load Query", ), 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]; diff --git a/src/Explorer/Panes/BrowseQueriesPane/BrowseQueriesPane.tsx b/src/Explorer/Panes/BrowseQueriesPane/BrowseQueriesPane.tsx index 9624b4539..ce23077be 100644 --- a/src/Explorer/Panes/BrowseQueriesPane/BrowseQueriesPane.tsx +++ b/src/Explorer/Panes/BrowseQueriesPane/BrowseQueriesPane.tsx @@ -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): 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"); diff --git a/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.test.tsx b/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.test.tsx index 62aeade73..1ad1ddf12 100644 --- a/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.test.tsx +++ b/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.test.tsx @@ -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(); + const wrapper = shallow(); expect(wrapper.exists(".deleteCollectionFeedback")).toBe(false); const database = { id: ko.observable("testDB") } as Database; database.collections = ko.observableArray([{ 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(selectedCollectionId), - databaseId, - rid: "test", - } as Collection; - }; - fakeExplorer.selectedCollectionId = ko.computed(() => selectedCollectionId); - fakeExplorer.selectedNode = ko.observable(); fakeExplorer.refreshAllDatabases = () => undefined; - fakeExplorer.isSelectedDatabaseShared = () => false; - const database = { id: ko.observable("testDB") } as Database; - database.collections = ko.observableArray([{ 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]); + 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(); + useDatabases.getState().addDatabases([database]); + useSelectedNode.getState().setSelectedNode(collection); + }); + + afterEach(() => { + useDatabases.getState().clearDatabases(); + useSelectedNode.getState().setSelectedNode(undefined); }); it("should call delete collection", () => { + const wrapper = mount(); 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(); expect(wrapper.exists("#confirmCollectionId")).toBe(true); wrapper .find("#confirmCollectionId") diff --git a/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx b/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx index bec5db8a0..6effe0b01 100644 --- a/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx +++ b/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx @@ -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 { const closeSidePanel = useSidePanel((state) => state.closeSidePanel); - const isLastCollection = useDatabases((state) => state.isLastCollection); const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState(""); const [inputCollectionName, setInputCollectionName] = useState(""); const [formError, setFormError] = useState(""); 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 => { - 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 tab.node?.id() === collection.id() && (tab.node as Collection).databaseId === collection.databaseId ); diff --git a/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap b/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap index af75f4fe6..6b3104666 100644 --- a/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap +++ b/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap @@ -2,15 +2,9 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collection 1`] = ` @@ -372,355 +366,6 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect -
- - - Help us improve Azure Cosmos DB! - - - - - What is the reason why you are deleting this - container - ? - - - - -
-
-
-