mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-20 01:11:25 +00:00
Move selectedNode to zustand (#903)
This commit is contained in:
@@ -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",
|
||||
|
||||
|
||||
Reference in New Issue
Block a user