Remove old resource tree and cleanup (#28)

* Remove old resource tree and its various components

* Fix stored procedure, udf, trigger not always deleting when context menu option chosen

* Reformat and fix eslint warnings

* Remove CommandButtonOptions
This commit is contained in:
Laurent Nguyen 2020-06-15 12:16:52 +02:00 committed by GitHub
parent d70e30c4fc
commit 73f2c612ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 50 additions and 2307 deletions

View File

@ -117,7 +117,6 @@ export class Features {
public static readonly notebookServerUrl = "notebookserverurl";
public static readonly notebookServerToken = "notebookservertoken";
public static readonly notebookBasePath = "notebookbasepath";
public static readonly enableLegacyResourceTree = "enablelegacyresourcetree";
public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
public static readonly enableAutoPilotV2 = "enableautopilotv2";

View File

@ -5,6 +5,7 @@ import { SeverityLevel } from "@microsoft/applicationinsights-web";
// TODO: Move to a separate Diagnostics folder
export class Logger {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public static logInfo(message: string | Record<string, any>, area: string, code?: number): void {
let logMessage: string;
if (typeof message === "string") {

View File

@ -9,7 +9,6 @@ import { AccessibleVerticalList } from "../Explorer/Tree/AccessibleVerticalList"
import { ArcadiaWorkspaceItem } from "../Explorer/Controls/Arcadia/ArcadiaMenuPicker";
import { CassandraTableKey, CassandraTableKeys, TableDataClient } from "../Explorer/Tables/TableDataClient";
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
import { CommandButtonOptions } from "../Explorer/Controls/CommandButton/CommandButton";
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import { ExecuteSprocParam } from "../Explorer/Panes/ExecuteSprocParamsPane";
import { GitHubClient } from "../GitHub/GitHubClient";
@ -339,17 +338,6 @@ export interface Button {
isSelected?: ko.Computed<boolean>;
}
export interface CommandButton {
disabled: ko.Subscribable<boolean>;
visible: ko.Subscribable<boolean>;
iconSrc: string;
commandButtonLabel: string | ko.Observable<string>;
tooltipText: string | ko.Observable<string>;
children: ko.ObservableArray<CommandButtonOptions>;
commandClickCallback: () => void;
}
export interface NotificationConsole {
filteredConsoleData: ko.ObservableArray<ConsoleData>;
isConsoleExpanded: ko.Observable<boolean>;
@ -373,7 +361,6 @@ export interface TreeNode {
id: ko.Observable<string>;
database?: Database;
collection?: Collection;
contextMenu?: ContextMenu;
onNewQueryClick?(source: any, event: MouseEvent): void;
onNewStoredProcedureClick?(source: Collection, event: MouseEvent): void;
@ -538,7 +525,7 @@ export interface StoredProcedure extends TreeNode {
id: ko.Observable<string>;
body: ko.Observable<string>;
delete(source: TreeNode, event: MouseEvent | KeyboardEvent): void;
delete(): void;
open: () => void;
select(): void;
execute(params: string[], partitionKeyValue?: string): void;
@ -552,7 +539,7 @@ export interface UserDefinedFunction extends TreeNode {
id: ko.Observable<string>;
body: ko.Observable<string>;
delete(source: TreeNode, event: MouseEvent | KeyboardEvent): void;
delete(): void;
open: () => void;
select(): void;
}
@ -567,7 +554,7 @@ export interface Trigger extends TreeNode {
triggerType: ko.Observable<string>;
triggerOperation: ko.Observable<string>;
delete(source: TreeNode, event: MouseEvent | KeyboardEvent): void;
delete(): void;
open: () => void;
select(): void;
}
@ -1233,17 +1220,6 @@ export enum TerminalKind {
Cassandra = 2
}
export interface ContextMenu {
container: Explorer;
visible: ko.Observable<boolean>;
elementId: string;
options: ko.ObservableArray<CommandButtonOptions>;
tabIndex: ko.Observable<number>;
show(source: any, event: MouseEvent | KeyboardEvent): void;
hide(source: any, event: MouseEvent | KeyboardEvent): void;
}
export interface DataExplorerInputsFrame {
databaseAccount: any;
subscriptionId: string;

View File

@ -4,10 +4,6 @@ import * as ko from "knockout";
import "./ComponentRegisterer";
describe("Component Registerer", () => {
it("should register command-button component", () => {
expect(ko.components.isRegistered("command-button")).toBe(true);
});
it("should register input-typeahead component", () => {
expect(ko.components.isRegistered("input-typeahead")).toBe(true);
});
@ -80,30 +76,6 @@ describe("Component Registerer", () => {
expect(ko.components.isRegistered("mongo-shell-tab")).toBe(true);
});
it("should register resource-tree component", () => {
expect(ko.components.isRegistered("resource-tree")).toBe(true);
});
it("should register database-node component", () => {
expect(ko.components.isRegistered("database-node")).toBe(true);
});
it("should register collection-node component", () => {
expect(ko.components.isRegistered("collection-node")).toBe(true);
});
it("should register stored-procedure-node component", () => {
expect(ko.components.isRegistered("stored-procedure-node")).toBe(true);
});
it("should register trigger-node component", () => {
expect(ko.components.isRegistered("trigger-node")).toBe(true);
});
it("should register user-defined-function-node component", () => {
expect(ko.components.isRegistered("user-defined-function-node")).toBe(true);
});
it("should registeradd-collection-pane component", () => {
expect(ko.components.isRegistered("add-collection-pane")).toBe(true);
});
@ -152,10 +124,6 @@ describe("Component Registerer", () => {
expect(ko.components.isRegistered("manage-spark-cluster-pane")).toBe(true);
});
it("should register collection-node-context-menu component", () => {
expect(ko.components.isRegistered("collection-node-context-menu")).toBe(true);
});
it("should register dynamic-list component", () => {
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
});

View File

@ -1,9 +1,7 @@
import * as ko from "knockout";
import * as PaneComponents from "./Panes/PaneComponents";
import * as TabComponents from "./Tabs/TabComponents";
import * as TreeComponents from "./Tree/TreeComponents";
import { CollapsiblePanelComponent } from "./Controls/CollapsiblePanel/CollapsiblePanelComponent";
import { CommandButtonComponent } from "./Controls/CommandButton/CommandButton";
import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent";
import { DynamicListComponent } from "./Controls/DynamicList/DynamicListComponent";
import { EditorComponent } from "./Controls/Editor/EditorComponent";
@ -16,7 +14,6 @@ import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputI
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
import { ToolbarComponent } from "./Controls/Toolbar/Toolbar";
ko.components.register("command-button", CommandButtonComponent);
ko.components.register("toolbar", new ToolbarComponent());
ko.components.register("input-typeahead", new InputTypeaheadComponent());
ko.components.register("new-vertex-form", NewVertexComponent);
@ -51,14 +48,6 @@ ko.components.register("notebook-viewer-tab", new TabComponents.NotebookViewerTa
// Database Tabs
ko.components.register("database-settings-tab", new TabComponents.DatabaseSettingsTab());
// Resource Tree nodes
ko.components.register("resource-tree", new TreeComponents.ResourceTree());
ko.components.register("database-node", new TreeComponents.DatabaseTreeNode());
ko.components.register("collection-node", new TreeComponents.CollectionTreeNode());
ko.components.register("stored-procedure-node", new TreeComponents.StoredProcedureTreeNode());
ko.components.register("trigger-node", new TreeComponents.TriggerTreeNode());
ko.components.register("user-defined-function-node", new TreeComponents.UserDefinedFunctionTreeNode());
// Panes
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
@ -92,6 +81,3 @@ ko.components.register("manage-spark-cluster-pane", new PaneComponents.ManageSpa
ko.components.register("library-manage-pane", new PaneComponents.LibraryManagePaneComponent());
ko.components.register("cluster-library-pane", new PaneComponents.ClusterLibraryPaneComponent());
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
// Menus
ko.components.register("collection-node-context-menu", new TreeComponents.CollectionTreeNodeContextMenu());

View File

@ -1,6 +1,5 @@
import * as ko from "knockout";
import * as ViewModels from "../Contracts/ViewModels";
import { CommandButtonOptions } from "./Controls/CommandButton/CommandButton";
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
import AddCollectionIcon from "../../images/AddCollection.svg";
import AddSqlQueryIcon from "../../images/AddSqlQuery_16x16.svg";
@ -115,7 +114,10 @@ export class ResourceTreeContextMenuButtonFactory {
return items;
}
public static createStoreProcedureContextMenuItems(container: ViewModels.Explorer): TreeNodeMenuItem[] {
public static createStoreProcedureContextMenuItems(
container: ViewModels.Explorer,
storedProcedure: ViewModels.StoredProcedure
): TreeNodeMenuItem[] {
if (container.isPreferredApiCassandra()) {
return [];
}
@ -123,16 +125,16 @@ export class ResourceTreeContextMenuButtonFactory {
return [
{
iconSrc: DeleteSprocIcon,
onClick: () => {
const selectedStoreProcedure: ViewModels.StoredProcedure = container.findSelectedStoredProcedure();
selectedStoreProcedure && selectedStoreProcedure.delete(selectedStoreProcedure, null);
},
onClick: () => storedProcedure.delete(),
label: "Delete Store Procedure"
}
];
}
public static createTriggerContextMenuItems(container: ViewModels.Explorer): TreeNodeMenuItem[] {
public static createTriggerContextMenuItems(
container: ViewModels.Explorer,
trigger: ViewModels.Trigger
): TreeNodeMenuItem[] {
if (container.isPreferredApiCassandra()) {
return [];
}
@ -140,16 +142,16 @@ export class ResourceTreeContextMenuButtonFactory {
return [
{
iconSrc: DeleteTriggerIcon,
onClick: () => {
const selectedTrigger: ViewModels.Trigger = container.findSelectedTrigger();
selectedTrigger && selectedTrigger.delete(selectedTrigger, null);
},
onClick: () => trigger.delete(),
label: "Delete Trigger"
}
];
}
public static createUserDefinedFunctionContextMenuItems(container: ViewModels.Explorer): TreeNodeMenuItem[] {
public static createUserDefinedFunctionContextMenuItems(
container: ViewModels.Explorer,
userDefinedFunction: ViewModels.UserDefinedFunction
): TreeNodeMenuItem[] {
if (container.isPreferredApiCassandra()) {
return [];
}
@ -157,266 +159,9 @@ export class ResourceTreeContextMenuButtonFactory {
return [
{
iconSrc: DeleteUDFIcon,
onClick: () => {
const selectedUDF: ViewModels.UserDefinedFunction = container.findSelectedUDF();
selectedUDF && selectedUDF.delete(selectedUDF, null);
},
onClick: () => userDefinedFunction.delete(),
label: "Delete User Defined Function"
}
];
}
}
/**
* Current resource tree (in KO)
* TODO: Remove when switching to new resource tree
*/
export class ContextMenuButtonFactory {
public static createDatabaseContextMenuButton(
container: ViewModels.Explorer,
btnParams: DatabaseContextMenuButtonParams
): CommandButtonOptions[] {
const addCollectionId = `${btnParams.databaseId}-${container.addCollectionText()}`;
const deleteDatabaseId = `${btnParams.databaseId}-${container.deleteDatabaseText()}`;
const newCollectionButtonOptions: CommandButtonOptions = {
iconSrc: AddCollectionIcon,
id: addCollectionId,
onCommandClick: () => {
if (container.isPreferredApiCassandra()) {
container.cassandraAddCollectionPane.open();
} else {
container.addCollectionPane.open(container.selectedDatabaseId());
}
const selectedDatabase: ViewModels.Database = container.findSelectedDatabase();
selectedDatabase && selectedDatabase.contextMenu.hide(selectedDatabase, null);
},
commandButtonLabel: container.addCollectionText(),
hasPopup: true
};
const deleteDatabaseButtonOptions: CommandButtonOptions = {
iconSrc: DeleteDatabaseIcon,
id: deleteDatabaseId,
onCommandClick: () => {
const database: ViewModels.Database = container.findSelectedDatabase();
database.onDeleteDatabaseContextMenuClick(database, null);
},
commandButtonLabel: container.deleteDatabaseText(),
hasPopup: true,
disabled: ko.computed<boolean>(() => container.isNoneSelected()),
visible: ko.computed<boolean>(() => !container.isNoneSelected())
};
return [newCollectionButtonOptions, deleteDatabaseButtonOptions];
}
public static createCollectionContextMenuButton(
container: ViewModels.Explorer,
btnParams: CollectionContextMenuButtonParams
): CommandButtonOptions[] {
const newSqlQueryId = `${btnParams.databaseId}-${btnParams.collectionId}-newSqlQuery`;
const newSqlQueryForGraphId = `${btnParams.databaseId}-${btnParams.collectionId}-newSqlQueryForGraph`;
const newQueryForMongoId = `${btnParams.databaseId}-${btnParams.collectionId}-newQuery`;
const newShellForMongoId = `${btnParams.databaseId}-${btnParams.collectionId}-newShell`;
const newStoredProcedureId = `${btnParams.databaseId}-${btnParams.collectionId}-newStoredProcedure`;
const udfId = `${btnParams.databaseId}-${btnParams.collectionId}-udf`;
const newTriggerId = `${btnParams.databaseId}-${btnParams.collectionId}-newTrigger`;
const deleteCollectionId = `${btnParams.databaseId}-${btnParams.collectionId}-${container.deleteCollectionText()}`;
const newSQLQueryButtonOptions: CommandButtonOptions = {
iconSrc: AddSqlQueryIcon,
id: newSqlQueryId,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null);
},
commandButtonLabel: "New SQL Query",
hasPopup: true,
disabled: ko.computed<boolean>(
() => container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiDocumentDB()
),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiDocumentDB()
)
//TODO: Merge with add query logic below, same goes for CommandBarButtonFactory
};
const newSQLQueryButtonOptionsForGraph: CommandButtonOptions = {
iconSrc: AddSqlQueryIcon,
id: newSqlQueryForGraphId,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null);
},
commandButtonLabel: "New SQL Query",
hasPopup: true,
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiGraph()),
visible: ko.computed<boolean>(() => !container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiGraph())
};
const newMongoQueryButtonOptions: CommandButtonOptions = {
iconSrc: AddSqlQueryIcon,
id: newQueryForMongoId,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null);
},
commandButtonLabel: "New Query",
hasPopup: true,
disabled: ko.computed<boolean>(
() => container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()
),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()
)
};
const newMongoShellButtonOptions: CommandButtonOptions = {
iconSrc: HostedTerminalIcon,
id: newShellForMongoId,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewMongoShellClick();
},
commandButtonLabel: "New Shell",
hasPopup: true,
disabled: ko.computed<boolean>(
() => container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()
),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()
)
};
const newStoredProcedureButtonOptions: CommandButtonOptions = {
iconSrc: AddStoredProcedureIcon,
id: newStoredProcedureId,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
},
commandButtonLabel: "New Stored Procedure",
hasPopup: true,
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
)
};
const newUserDefinedFunctionButtonOptions: CommandButtonOptions = {
iconSrc: AddUdfIcon,
id: udfId,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
},
commandButtonLabel: "New UDF",
hasPopup: true,
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
)
};
const newTriggerButtonOptions: CommandButtonOptions = {
iconSrc: AddTriggerIcon,
id: newTriggerId,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
},
commandButtonLabel: "New Trigger",
hasPopup: true,
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
)
};
const deleteCollectionButtonOptions: CommandButtonOptions = {
iconSrc: DeleteCollectionIcon,
id: deleteCollectionId,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
},
commandButtonLabel: container.deleteCollectionText(),
hasPopup: true,
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
visible: ko.computed<boolean>(() => !container.isDatabaseNodeOrNoneSelected())
//TODO: Change to isCollectionNodeorNoneSelected and same in CommandBarButtonFactory
};
return [
newSQLQueryButtonOptions,
newSQLQueryButtonOptionsForGraph,
newMongoQueryButtonOptions,
newMongoShellButtonOptions,
newStoredProcedureButtonOptions,
newUserDefinedFunctionButtonOptions,
newTriggerButtonOptions,
deleteCollectionButtonOptions
];
}
public static createStoreProcedureContextMenuButton(container: ViewModels.Explorer): CommandButtonOptions[] {
const deleteStoredProcedureId = "Context Menu - Delete Stored Procedure";
const deleteStoreProcedureButtonOptions: CommandButtonOptions = {
iconSrc: DeleteSprocIcon,
id: deleteStoredProcedureId,
onCommandClick: () => {
const selectedStoreProcedure: ViewModels.StoredProcedure = container.findSelectedStoredProcedure();
selectedStoreProcedure && selectedStoreProcedure.delete(selectedStoreProcedure, null);
},
commandButtonLabel: "Delete Stored Procedure",
hasPopup: false,
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
)
};
return [deleteStoreProcedureButtonOptions];
}
public static createTriggerContextMenuButton(container: ViewModels.Explorer): CommandButtonOptions[] {
const deleteTriggerId = "Context Menu - Delete Trigger";
const deleteTriggerButtonOptions: CommandButtonOptions = {
iconSrc: DeleteTriggerIcon,
id: deleteTriggerId,
onCommandClick: () => {
const selectedTrigger: ViewModels.Trigger = container.findSelectedTrigger();
selectedTrigger && selectedTrigger.delete(selectedTrigger, null);
},
commandButtonLabel: "Delete Trigger",
hasPopup: false,
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
)
};
return [deleteTriggerButtonOptions];
}
public static createUserDefinedFunctionContextMenuButton(container: ViewModels.Explorer): CommandButtonOptions[] {
const deleteUserDefinedFunctionId = "Context Menu - Delete User Defined Function";
const deleteUserDefinedFunctionButtonOptions: CommandButtonOptions = {
iconSrc: DeleteUDFIcon,
id: deleteUserDefinedFunctionId,
onCommandClick: () => {
const selectedUDF: ViewModels.UserDefinedFunction = container.findSelectedUDF();
selectedUDF && selectedUDF.delete(selectedUDF, null);
},
commandButtonLabel: "Delete User Defined Function",
hasPopup: false,
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
visible: ko.computed<boolean>(
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
)
};
return [deleteUserDefinedFunctionButtonOptions];
}
}

View File

@ -1,200 +0,0 @@
@import "../../../../less/Common/Constants";
@ButtonIconSize: 18px;
.commandBar {
padding-left: @DefaultSpace;
border-bottom: @ButtonBorderWidth solid @BaseMedium;
display: flex;
overflow: hidden;
height: @topcommandbarheight;
.staticCommands {
list-style: none;
margin: 0px;
padding: 0px;
display: flex;
flex: 0 0 auto;
}
.overflowCommands {
display:flex;
flex: 1 0 auto;
.visibleCommands {
display: inline-flex;
list-style: none;
margin: 0px;
padding: 0px;
}
.partialSplitterContainer {
padding: @SmallSpace @DefaultSpace @SmallSpace @SmallSpace;
.flex-display();
}
}
.commandExpand {
border: none;
padding: 0px;
direction: rtl;
&:hover {
.hover();
cursor: pointer;
& > .commandDropdownContainer {
display: block !important; // TODO: Remove after reusing KO mouseover and mouseout event handlers
}
}
&:focus {
.focus();
}
.commandDropdownLauncher {
direction: ltr;
padding-top: @SmallSpace;
.commandIcon {
vertical-align: text-top;
}
.commandBarEllipses {
font-weight: bold;
font-size: 20px;
}
}
}
.hiddenCommandsContainer > .commandDropdownLauncher {
padding: 0px @DefaultSpace;
}
.commandDropdownContainer {
display: none;
z-index: 1000;
direction: ltr;
position: absolute;
width: fit-content;
padding: 0px;
background-color: @BaseLight;
box-shadow: 1px 2px 6px @BaseMediumHigh, -2px 2px 6px @BaseMediumHigh;
.commandDropdown {
display: flex;
flex-direction: column;
padding: 0px;
margin: 0px;
}
}
.feedbackButton {
margin-right: @LargeSpace;
white-space: nowrap;
}
}
command-button,
.commandButtonReact {
display: inline-flex;
.commandButtonComponent {
width: 100%;
color: @BaseHigh;
background-color: transparent;
text-decoration: none;
border: @ButtonBorderWidth solid transparent;
.flex-display();
&:hover:not(.commandDisabled) {
cursor: pointer;
.hover();
}
&:active:not(.commandDisabled) {
border: @ButtonBorderWidth dashed @AccentMedium;
.active();
}
&:focus:not(.commandDisabled) {
border: @ButtonBorderWidth dashed @AccentMedium;
}
.commandContent {
padding: @DefaultSpace @DefaultSpace @DefaultSpace;
flex: 0 0 auto;
.commandIcon {
margin: 0 @SmallSpace 0 0;
vertical-align: text-top;
width: @ButtonIconSize;
height: @ButtonIconSize;
}
.commandLabel {
padding: 0px;
}
}
.commandContent .hasHiddenItems {
padding-right: @SmallSpace;
}
}
.commandButtonComponent.commandDisabled {
color: @BaseMediumHigh;
opacity: 0.5;
}
.commandExpand {
padding-top: @SmallSpace;
padding-bottom: @SmallSpace;
&:hover {
.hover();
& > .commandDropdownContainer {
display: block !important; // TODO: Remove after reusing KO mouseover and mouseout event handlers
}
}
&:focus {
.focus();
}
.commandDropdownLauncher {
cursor: pointer;
display: inline-flex;
.commandButtonComponent {
padding: 0px;
}
}
.expandDropdown {
padding: @SmallSpace;
img {
vertical-align: top;
}
}
.partialSplitter {
margin: @SmallSpace 0px 6px;
}
}
.commandButtonComponent[tabindex]:focus {
outline: none;
}
.selectedButton {
background-color: @AccentLow;
outline: none
}
}
.partialSplitter {
border-left: @ButtonBorderWidth solid @BaseMediumHigh;
}
.commandDropdown .commandButtonComponent {
padding-left: 0px;
}

View File

@ -1,139 +0,0 @@
import * as ko from "knockout";
import { CommandButtonComponent, CommandButtonOptions } from "./CommandButton";
const mockLabel = "Some Label";
const id = "Some id";
function buildComponent(buttonOptions: any) {
document.body.innerHTML = CommandButtonComponent.template as any;
const vm = new CommandButtonComponent.viewModel(buttonOptions);
ko.applyBindings(vm);
}
describe("Command Button Component", () => {
function buildButtonOptions(
onClick: () => void,
id?: string,
label?: string,
disabled?: ko.Observable<boolean>,
visible?: ko.Observable<boolean>,
tooltipText?: string
): { buttonProps: CommandButtonOptions } {
return {
buttonProps: {
iconSrc: "images/AddCollection.svg",
id: id,
commandButtonLabel: label || mockLabel,
disabled: disabled,
visible: visible,
tooltipText: tooltipText,
hasPopup: false,
onCommandClick: onClick
}
};
}
function buildSplitterButtonOptions(
onClick: () => void,
id?: string,
label?: string,
disabled?: ko.Observable<boolean>,
visible?: ko.Observable<boolean>,
tooltipText?: string
): { buttonProps: CommandButtonOptions } {
const child: CommandButtonOptions = {
iconSrc: "images/settings_15x15.svg",
id: id,
commandButtonLabel: label || mockLabel,
disabled: disabled,
visible: visible,
tooltipText: tooltipText,
hasPopup: false,
onCommandClick: onClick
};
return {
buttonProps: {
iconSrc: "images/AddCollection.svg",
id: id,
commandButtonLabel: label || mockLabel,
disabled: disabled,
visible: visible,
tooltipText: tooltipText,
hasPopup: false,
onCommandClick: onClick,
children: [child]
}
};
}
afterEach(() => {
ko.cleanNode(document);
document.body.innerHTML = "";
});
describe("Rendering", () => {
it("should display button label", () => {
const buttonOptions = buildButtonOptions(() => {
/** do nothing **/
}, mockLabel);
buildComponent(buttonOptions);
expect(document.getElementsByClassName("commandButtonComponent").item(0).textContent).toContain(mockLabel);
});
it("should display button icon", () => {
const buttonOptions = buildButtonOptions(() => {
/** do nothing **/
});
buildComponent(buttonOptions);
expect(
document
.getElementsByTagName("img")
.item(0)
.getAttribute("src")
).toBeDefined();
});
});
describe("Behavior", () => {
let clickSpy: jasmine.Spy;
beforeEach(() => {
clickSpy = jasmine.createSpy("Command button click spy");
});
it("should trigger the click handler when the command button is clicked", () => {
const buttonOptions = buildButtonOptions(() => clickSpy());
buildComponent(buttonOptions);
document
.getElementsByClassName("commandButtonComponent")
.item(0)
.dispatchEvent(new Event("click"));
expect(clickSpy).toHaveBeenCalled();
});
it("should not trigger the click handler when command button is disabled", () => {
const buttonOptions = buildButtonOptions(() => clickSpy(), id, mockLabel, ko.observable(true));
buildComponent(buttonOptions);
document
.getElementsByClassName("commandButtonComponent")
.item(0)
.dispatchEvent(new Event("click"));
expect(clickSpy).not.toHaveBeenCalled();
});
it("should not have a dropdown if it has no child", () => {
const buttonOptions = buildButtonOptions(() => clickSpy(), id, mockLabel, ko.observable(true));
buildComponent(buttonOptions);
const dropdownSize = document.getElementsByClassName("commandExpand").length;
expect(dropdownSize).toBe(0);
});
it("should have a dropdown if it has a child", () => {
const buttonOptions = buildSplitterButtonOptions(() => clickSpy(), id, mockLabel, ko.observable(true));
buildComponent(buttonOptions);
const dropdownSize = document.getElementsByClassName("commandExpand").length;
expect(dropdownSize).toBe(1);
});
});
});

View File

@ -1,191 +0,0 @@
/**
* How to use this component:
*
* In your html markup, use:
* <command-button params="{
* iconSrc: '/icon/example/src/',
* onCommandClick: () => { doSomething },
* commandButtonLabel: 'Some Label'
* disabled: true/false
* }">
* </command-button>
*
*/
import * as ko from "knockout";
import * as ViewModels from "../../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
import { KeyCodes } from "../../../Common/Constants";
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import template from "./command-button.html";
/**
* Options for this component
*/
export interface CommandButtonOptions {
/**
* image source for the button icon
*/
iconSrc: string;
/**
* Id for the button icon
*/
id: string;
/**
* Click handler for command button click
*/
onCommandClick: () => void;
/**
* Label for the button
*/
commandButtonLabel: string | ko.Observable<string>;
/**
* True if this button opens a tab or pane, false otherwise.
*/
hasPopup: boolean;
/**
* Enabled/disabled state of command button
*/
disabled?: ko.Subscribable<boolean>;
/**
* Visibility/Invisibility of the button
*/
visible?: ko.Subscribable<boolean>;
/**
* Whether or not the button should have the 'selectedButton' styling
*/
isSelected?: ko.Observable<boolean>;
/**
* Text to displayed in the tooltip on hover
*/
tooltipText?: string | ko.Observable<string>;
/**
* Callback triggered when the template is bound to the component
*/
onTemplateReady?: () => void;
/**
* tabindex for the command button
*/
tabIndex?: ko.Observable<number>;
/**
* Childrens command buttons to hide in the dropdown
*/
children?: CommandButtonOptions[];
}
export class CommandButtonViewModel extends WaitsForTemplateViewModel implements ViewModels.CommandButton {
public commandClickCallback: () => void;
public commandButtonId: string;
public disabled: ko.Subscribable<boolean>;
public visible: ko.Subscribable<boolean>;
public isSelected: ko.Observable<boolean>;
public iconSrc: string;
public commandButtonLabel: ko.Observable<string>;
public tooltipText: ko.Observable<string>;
public tabIndex: ko.Observable<number>;
public isTemplateReady: ko.Observable<boolean>;
public hasPopup: boolean;
public children: ko.ObservableArray<CommandButtonOptions>;
public constructor(options: { buttonProps: CommandButtonOptions }) {
super();
const props = options.buttonProps;
const commandButtonLabel = props.commandButtonLabel;
const tooltipText = props.tooltipText;
this.commandButtonLabel =
typeof commandButtonLabel === "string" ? ko.observable<string>(commandButtonLabel) : commandButtonLabel;
this.commandButtonId = props.id;
this.disabled = props.disabled || ko.observable(false);
this.visible = props.visible || ko.observable(true);
this.isSelected = props.isSelected || ko.observable(false);
this.iconSrc = props.iconSrc;
this.tabIndex = props.tabIndex || ko.observable(0);
this.hasPopup = props.hasPopup;
this.children = ko.observableArray(props.children);
super.onTemplateReady((isTemplateReady: boolean) => {
if (isTemplateReady && props.onTemplateReady) {
props.onTemplateReady();
}
});
if (tooltipText && typeof tooltipText === "string") {
this.tooltipText = ko.observable<string>(tooltipText);
} else if (tooltipText && typeof tooltipText === "function") {
this.tooltipText = tooltipText;
} else {
this.tooltipText = this.commandButtonLabel;
}
this.commandClickCallback = () => {
if (this.disabled()) {
return;
}
const el = document.querySelector(".commandDropdownContainer") as HTMLElement;
if (el) {
el.style.display = "none";
}
props.onCommandClick();
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
commandButtonClicked: this.commandButtonLabel
});
};
}
public onKeyPress(source: any, event: KeyboardEvent): boolean {
if (event.keyCode === KeyCodes.Space || event.keyCode === KeyCodes.Enter) {
this.commandClickCallback && this.commandClickCallback();
event.stopPropagation();
return false;
}
return true;
}
public onLauncherKeyDown(source: any, event: KeyboardEvent): boolean {
// TODO: Convert JQuery code into Knockout
if (event.keyCode === KeyCodes.DownArrow) {
$(event.target)
.parent()
.siblings()
.children(".commandExpand")
.children(".commandDropdownContainer")
.hide();
$(event.target)
.children(".commandDropdownContainer")
.show()
.focus();
event.stopPropagation();
return false;
}
if (event.keyCode === KeyCodes.UpArrow) {
$(event.target)
.children(".commandDropdownContainer")
.hide();
event.stopPropagation();
return false;
}
return true;
}
}
/**
* Helper class for ko component registration
*/
export const CommandButtonComponent = {
viewModel: CommandButtonViewModel,
template
};

View File

@ -1,40 +0,0 @@
<span
class="commandButtonComponent"
role="menuitem"
tabindex="0"
data-bind="setTemplateReady: true,
css: {
commandDisabled: disabled,
selectedButton: isSelected
},
event: {
keypress: onKeyPress
},
attr: {
title: tooltipText,
id: commandButtonId,
tabindex: tabIndex ,
'aria-disabled': disabled,
'aria-haspopup': hasPopup
},
click: commandClickCallback,
visible: visible"
>
<div class="commandContent" data-bind="css: { hasHiddenItems: children().length > 0 }">
<img class="commandIcon" data-bind="attr: {src: iconSrc, alt: commandButtonLabel}" />
<span class="commandLabel" data-bind="text: commandButtonLabel"></span>
</div>
</span>
<!-- ko if: children().length > 0 -->
<div class="commandExpand" tabindex="0" data-bind="visible: visible, event: { keydown: onLauncherKeyDown }">
<div class="commandDropdownLauncher">
<span class="partialSplitter"></span>
<span class="expandDropdown"> <img src="/QueryBuilder/CollapseChevronDown_16x.png" /> </span>
</div>
<div class="commandDropdownContainer">
<div class="commandDropdown" data-bind="foreach: children">
<command-button params="{buttonProps: $data}"></command-button>
</div>
</div>
</div>
<!-- /ko -->

View File

@ -10,7 +10,6 @@ import {
GalleryViewerComponent,
GalleryViewerComponentProps
} from "./GalleryViewerComponent";
import * as DataModels from "../../../Contracts/DataModels";
describe("GalleryCardsComponent", () => {
it("renders", () => {
@ -18,14 +17,8 @@ describe("GalleryCardsComponent", () => {
const props: GalleryCardsComponentProps = {
data: [],
userMetadata: undefined,
onNotebookMetadataChange: (officialSamplesIndex: number, notebookMetadata: DataModels.NotebookMetadata) =>
Promise.resolve(),
onClick: (
url: string,
notebookMetadata: DataModels.NotebookMetadata,
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
isLikedNotebook: boolean
) => Promise.resolve()
onNotebookMetadataChange: () => Promise.resolve(),
onClick: () => Promise.resolve()
};
const wrapper = shallow(<GalleryCardsComponent {...props} />);
@ -39,12 +32,7 @@ describe("FullWidthTabs", () => {
officialSamplesContent: [],
likedNotebooksContent: [],
userMetadata: undefined,
onClick: (
url: string,
notebookMetadata: DataModels.NotebookMetadata,
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
isLikedNotebook: boolean
) => Promise.resolve()
onClick: () => Promise.resolve()
};
const wrapper = shallow(<FullWidthTabs {...props} />);

View File

@ -39,7 +39,7 @@ export class GalleryCardsComponent extends React.Component<GalleryCardsComponent
public render(): JSX.Element {
return (
<Stack horizontal wrap tokens={this.sectionStackTokens}>
{this.props.data.map((githubInfo: DataModels.GitHubInfoJunoResponse, index: any) => {
{this.props.data.map((githubInfo: DataModels.GitHubInfoJunoResponse) => {
const name = githubInfo.name;
const url = githubInfo.downloadUrl;
const notebookMetadata = githubInfo.metadata || {
@ -56,7 +56,7 @@ export class GalleryCardsComponent extends React.Component<GalleryCardsComponent
const officialSamplesIndex = githubInfo.officialSamplesIndex;
const isLikedNotebook = githubInfo.isLikedNotebook;
const updateTabsStatePerNotebook = this.props.onNotebookMetadataChange
? (notebookMetadata: DataModels.NotebookMetadata) =>
? (notebookMetadata: DataModels.NotebookMetadata): Promise<void> =>
this.props.onNotebookMetadataChange(officialSamplesIndex, notebookMetadata)
: undefined;
@ -68,7 +68,9 @@ export class GalleryCardsComponent extends React.Component<GalleryCardsComponent
name={name}
url={url}
notebookMetadata={notebookMetadata}
onClick={() => this.props.onClick(url, notebookMetadata, updateTabsStatePerNotebook, isLikedNotebook)}
onClick={(): Promise<void> =>
this.props.onClick(url, notebookMetadata, updateTabsStatePerNotebook, isLikedNotebook)
}
/>
)
);
@ -115,7 +117,7 @@ export class FullWidthTabs extends React.Component<FullWidthTabsProps, FullWidth
title: "Official Samples",
content: {
className: "",
render: () => (
render: (): JSX.Element => (
<GalleryCardsComponent
data={this.state.officialSamplesContent}
onClick={this.props.onClick}
@ -124,13 +126,13 @@ export class FullWidthTabs extends React.Component<FullWidthTabsProps, FullWidth
/>
)
},
isVisible: () => true
isVisible: (): boolean => true
},
{
title: "Liked Notebooks",
content: {
className: "",
render: () => (
render: (): JSX.Element => (
<GalleryCardsComponent
data={this.state.likedNotebooksContent}
onClick={this.props.onClick}
@ -139,12 +141,15 @@ export class FullWidthTabs extends React.Component<FullWidthTabsProps, FullWidth
/>
)
},
isVisible: () => true
isVisible: (): boolean => true
}
];
}
public updateTabsState = async (officialSamplesIndex: number, notebookMetadata: DataModels.NotebookMetadata) => {
public updateTabsState = async (
officialSamplesIndex: number,
notebookMetadata: DataModels.NotebookMetadata
): Promise<void> => {
let currentLikedNotebooksContent = [...this.state.likedNotebooksContent];
let currentUserMetadata = { ...this.state.userMetadata };
let currentLikedNotebooks = [...currentUserMetadata.likedNotebooks];
@ -187,7 +192,7 @@ export class FullWidthTabs extends React.Component<FullWidthTabsProps, FullWidth
});
JunoUtils.updateNotebookMetadata(this.authorizationToken, notebookMetadata).then(
async returnedNotebookMetadata => {
async () => {
if (metadataLikesUpdates !== 0) {
JunoUtils.updateUserMetadata(this.authorizationToken, currentUserMetadata);
// TODO: update state here?
@ -203,9 +208,9 @@ export class FullWidthTabs extends React.Component<FullWidthTabsProps, FullWidth
);
};
private onTabIndexChange = (activeTabIndex: number) => this.setState({ activeTabIndex });
private onTabIndexChange = (activeTabIndex: number): void => this.setState({ activeTabIndex });
public render() {
public render(): JSX.Element {
return (
<TabComponent.TabComponent
tabs={this.appTabs}
@ -238,7 +243,7 @@ export class GalleryViewerContainerComponent extends React.Component<
};
}
componentDidMount() {
componentDidMount(): void {
const authToken = CosmosClient.authorizationToken();
JunoUtils.getOfficialSampleNotebooks(authToken).then(
(data1: DataModels.GitHubInfoJunoResponse[]) => {
@ -341,7 +346,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
notebookMetadata: DataModels.NotebookMetadata,
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
isLikedNotebook: boolean
) => {
): Promise<void> => {
if (!this.props.container) {
SessionStorageUtility.setEntryString(
StorageKey.NotebookMetadata,

View File

@ -1,7 +1,6 @@
import React from "react";
import { shallow } from "enzyme";
import { NotebookMetadataComponentProps, NotebookMetadataComponent } from "./NotebookMetadataComponent";
import { NotebookMetadata } from "../../../Contracts/DataModels";
describe("NotebookMetadataComponent", () => {
it("renders un-liked notebook", () => {
@ -10,7 +9,7 @@ describe("NotebookMetadataComponent", () => {
container: undefined,
notebookMetadata: undefined,
notebookContent: {},
onNotebookMetadataChange: (newNotebookMetadata: NotebookMetadata) => Promise.resolve(),
onNotebookMetadataChange: () => Promise.resolve(),
isLikedNotebook: false
};
@ -24,7 +23,7 @@ describe("NotebookMetadataComponent", () => {
container: undefined,
notebookMetadata: undefined,
notebookContent: {},
onNotebookMetadataChange: (newNotebookMetadata: NotebookMetadata) => Promise.resolve(),
onNotebookMetadataChange: () => Promise.resolve(),
isLikedNotebook: true
};

View File

@ -154,7 +154,6 @@ export default class Explorer implements ViewModels.Explorer {
public selectedNode: ko.Observable<ViewModels.TreeNode>;
public isRefreshingExplorer: ko.Observable<boolean>;
private resourceTree: ResourceTreeAdapter;
private enableLegacyResourceTree: ko.Observable<boolean>;
// Resource Token
public resourceTokenDatabaseId: ko.Observable<string>;
@ -382,7 +381,6 @@ export default class Explorer implements ViewModels.Explorer {
this.armEndpoint = ko.observable<string>(undefined);
this.queriesClient = new QueriesClient(this);
this.isTryCosmosDBSubscription = ko.observable<boolean>(false);
this.enableLegacyResourceTree = ko.observable<boolean>(false);
this.resourceTokenDatabaseId = ko.observable<string>();
this.resourceTokenCollectionId = ko.observable<string>();
@ -1103,8 +1101,6 @@ export default class Explorer implements ViewModels.Explorer {
this.sparkClusterConnectionInfo.valueHasMutated();
}
this.enableLegacyResourceTree(this.isFeatureEnabled(Constants.Features.enableLegacyResourceTree));
featureSubcription.dispose();
});

View File

@ -1,43 +0,0 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import { CommandButtonOptions } from "./../Controls/CommandButton/CommandButton";
export default class ContextMenu implements ViewModels.ContextMenu {
public container: ViewModels.Explorer;
public visible: ko.Observable<boolean>;
public elementId: string;
public options: ko.ObservableArray<CommandButtonOptions>;
public tabIndex: ko.Observable<number>;
constructor(container: ViewModels.Explorer, rid: string) {
this.container = container;
this.visible = ko.observable<boolean>(false);
this.elementId = `contextMenu${rid}`;
this.options = ko.observableArray<CommandButtonOptions>([]);
this.tabIndex = ko.observable<number>(0);
}
public show(source: ViewModels.TreeNode, event: MouseEvent) {
if (source && source.contextMenu && source.contextMenu.visible && source.contextMenu.visible()) {
return;
}
this.container.selectedNode(source);
const elementId = source.contextMenu.elementId;
const htmlElement = document.getElementById(elementId);
htmlElement.style.left = `${event.clientX}px`;
htmlElement.style.top = `${event.clientY}px`;
!!source.contextMenu && source.contextMenu.visible(true);
source.contextMenu.tabIndex(0);
htmlElement.focus();
}
public hide(source: ViewModels.TreeNode, event: MouseEvent) {
if (!source || !source.contextMenu || !source.contextMenu.visible || !source.contextMenu.visible()) {
return;
}
source.contextMenu.tabIndex(-1);
source.contextMenu.visible(false);
}
}

View File

@ -449,7 +449,6 @@ export class DatabaseStub implements ViewModels.Database {
public collections: ko.ObservableArray<ViewModels.Collection>;
public isDatabaseExpanded: ko.Observable<boolean>;
public isDatabaseShared: ko.Computed<boolean>;
public contextMenu: ViewModels.ContextMenu;
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
public offer: ko.Observable<DataModels.Offer>;
@ -461,7 +460,6 @@ export class DatabaseStub implements ViewModels.Database {
this.id = options.id;
this.collections = options.collections;
this.isDatabaseExpanded = options.isDatabaseExpanded;
this.contextMenu = options.contextMenu;
this.offer = options.offer;
this.selectedSubnodeKind = options.selectedSubnodeKind;
}
@ -564,8 +562,6 @@ export class CollectionStub implements ViewModels.Collection {
public storedProceduresFocused: ko.Observable<boolean>;
public userDefinedFunctionsFocused: ko.Observable<boolean>;
public triggersFocused: ko.Observable<boolean>;
public contextMenu: ViewModels.ContextMenu;
public documentsContextMenu: ViewModels.ContextMenu;
public conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
public changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
public geospatialConfig: ko.Observable<DataModels.GeospatialConfig>;
@ -610,69 +606,8 @@ export class CollectionStub implements ViewModels.Collection {
this.storedProceduresFocused = options.storedProceduresFocused;
this.userDefinedFunctionsFocused = options.userDefinedFunctionsFocused;
this.triggersFocused = options.triggersFocused;
this.contextMenu = options.contextMenu;
this.documentsContextMenu = options.documentsContextMenu;
}
public onKeyPress = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onKeyDown = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onMenuKeyDown = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onDocumentDBDocumentsKeyDown = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onDocumentDBDocumentsKeyPress = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onMongoDBDocumentsKeyDown = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onMongoDBDocumentsKeyPress = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onSettingsKeyDown = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onSettingsKeyPress = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onStoredProceduresKeyDown = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onStoredProceduresKeyPress = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onUserDefinedFunctionsKeyDown = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onUserDefinedFunctionsKeyPress = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onTriggersKeyDown = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public onTriggersKeyPress = (source: any, event: KeyboardEvent): boolean => {
throw new Error("Not implemented");
};
public expandCollapseCollection() {
throw new Error("Not implemented");
}

View File

@ -14,8 +14,6 @@ import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
import { OfferUtils } from "../../Utils/OfferUtils";
import { StartUploadMessageParams, UploadDetails, UploadDetailsRecord } from "../../workers/upload/definitions";
import { ContextMenuButtonFactory } from "../ContextMenuButtonFactory";
import ContextMenu from "../Menus/ContextMenu";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient";
import { ConflictsTab } from "../Tabs/ConflictsTab";
@ -86,9 +84,6 @@ export default class Collection implements ViewModels.Collection {
public userDefinedFunctionsFocused: ko.Observable<boolean>;
public triggersFocused: ko.Observable<boolean>;
public contextMenu: ViewModels.ContextMenu;
public documentsContextMenu: ViewModels.ContextMenu;
constructor(
container: ViewModels.Explorer,
databaseId: string,
@ -216,217 +211,8 @@ export default class Collection implements ViewModels.Collection {
this.isStoredProceduresExpanded = ko.observable<boolean>(false);
this.isUserDefinedFunctionsExpanded = ko.observable<boolean>(false);
this.isTriggersExpanded = ko.observable<boolean>(false);
this.contextMenu = new ContextMenu(this.container, this.rid);
this.contextMenu.options(
ContextMenuButtonFactory.createCollectionContextMenuButton(container, {
databaseId: this.databaseId,
collectionId: this.id()
})
);
this.documentsContextMenu = new ContextMenu(this.container, `${this.rid}/documents`);
}
public onKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.expandCollapseCollection();
return false;
}
return true;
};
public onKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Delete") {
this.onDeleteCollectionContextMenuClick(source, event);
return false;
}
if (event.key === "ArrowRight" && !this.isCollectionExpanded()) {
this.expandCollection();
return false;
}
if (event.key === "ArrowDown" && !this.isCollectionExpanded()) {
this.expandCollection();
return false;
}
if (event.key === "ArrowLeft" && this.isCollectionExpanded()) {
this.collapseCollection();
return false;
}
return true;
};
public onMenuKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Escape") {
this.contextMenu.hide(source, event);
return false;
}
return true;
};
public onTableEntitiesKeyDown = (source: any, event: KeyboardEvent): boolean => {
return true;
};
public onTableEntitiesKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.onTableEntitiesClick();
event.stopPropagation();
return false;
}
return true;
};
public onGraphDocumentsKeyDown = (source: any, event: KeyboardEvent): boolean => {
return true;
};
public onGraphDocumentsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.onGraphDocumentsClick();
event.stopPropagation();
return false;
}
return true;
};
public onDocumentDBDocumentsKeyDown = (source: any, event: KeyboardEvent): boolean => {
return true;
};
public onDocumentDBDocumentsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.onDocumentDBDocumentsClick();
return false;
}
return true;
};
public onConflictsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.onConflictsClick();
return false;
}
return true;
};
public onMongoDBDocumentsKeyDown = (source: any, event: KeyboardEvent): boolean => {
return true;
};
public onMongoDBDocumentsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.onMongoDBDocumentsClick();
return false;
}
return true;
};
public onSettingsKeyDown = (source: any, event: KeyboardEvent): boolean => {
return true;
};
public onSettingsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.onSettingsClick();
return false;
}
return true;
};
public onStoredProceduresKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "ArrowRight" && !this.isStoredProceduresExpanded()) {
this.expandStoredProcedures();
return false;
}
if (event.key === "ArrowDown" && !this.isStoredProceduresExpanded()) {
this.expandStoredProcedures();
return false;
}
if (event.key === "ArrowLeft" && this.isStoredProceduresExpanded()) {
this.collapseStoredProcedures();
return false;
}
return true;
};
public onStoredProceduresKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.expandCollapseStoredProcedures();
return false;
}
return true;
};
public onUserDefinedFunctionsKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "ArrowRight" && !this.isUserDefinedFunctionsExpanded()) {
this.expandUserDefinedFunctions();
return false;
}
if (event.key === "ArrowDown" && !this.isUserDefinedFunctionsExpanded()) {
this.expandUserDefinedFunctions();
return false;
}
if (event.key === "ArrowLeft" && this.isUserDefinedFunctionsExpanded()) {
this.collapseUserDefinedFunctions();
return false;
}
return true;
};
public onUserDefinedFunctionsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.expandCollapseUserDefinedFunctions();
return false;
}
return true;
};
public onTriggersKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "ArrowRight" && !this.isTriggersExpanded()) {
this.expandTriggers();
return false;
}
if (event.key === "ArrowDown" && !this.isTriggersExpanded()) {
this.expandTriggers();
return false;
}
if (event.key === "ArrowLeft" && this.isTriggersExpanded()) {
this.collapseTriggers();
return false;
}
return true;
};
public onTriggersKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.expandCollapseTriggers();
return false;
}
return true;
};
public expandCollapseCollection() {
this.container.selectedNode(this);
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
@ -985,9 +771,6 @@ export default class Collection implements ViewModels.Collection {
// Activate
queryTab.onTabClick();
// Hide Context Menu (if necessary)
collection.contextMenu.hide(this, null);
}
public onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string) {
@ -1025,9 +808,6 @@ export default class Collection implements ViewModels.Collection {
// Activate
queryTab.onTabClick();
// Hide Context Menu (if necessary)
collection.contextMenu.hide(this, null);
}
public onNewGraphClick() {
@ -1036,8 +816,6 @@ export default class Collection implements ViewModels.Collection {
this.container.openedTabs.push(graphTab);
// Activate
graphTab.onTabClick();
// Hide Context Menu (if necessary)
this.contextMenu.hide(this, null);
}
public onNewMongoShellClick() {
@ -1060,9 +838,6 @@ export default class Collection implements ViewModels.Collection {
// Activate
mongoShellTab.onTabClick();
// Hide Context Menu (if necessary)
this.contextMenu.hide(this, null);
}
public onNewStoredProcedureClick(source: ViewModels.Collection, event: MouseEvent) {
@ -1357,7 +1132,6 @@ export default class Collection implements ViewModels.Collection {
}
public onDeleteCollectionContextMenuClick(source: ViewModels.Collection, event: MouseEvent | KeyboardEvent) {
this._onContextMenuClick(source, event);
this.container.deleteCollectionConfirmationPane.open();
}
@ -1605,11 +1379,6 @@ export default class Collection implements ViewModels.Collection {
});
}
private _onContextMenuClick(source: ViewModels.Collection, event: MouseEvent | KeyboardEvent) {
this.container.selectedNode(this);
this.contextMenu.hide(source, event);
}
protected _getOfferForCollection(offers: DataModels.Offer[], collection: DataModels.Collection): DataModels.Offer {
return _.find(offers, (offer: DataModels.Offer) => offer.resource.indexOf(collection._rid) >= 0);
}

View File

@ -1,425 +0,0 @@
<div class="pointerCursor">
<div
role="treeitem"
data-test="collectionList"
tabindex="0"
class="collectionMenu treeHovermargin highlight"
data-bind="
click: $data.expandCollapseCollection,
clickBubble: false,
contextmenuBubble: false,
css:{
collectionNodeSelected: isCollectionNodeSelected(),
contextmenushowing: $data.contextMenu.visible
},
event: {
keydown: onKeyDown,
keypress: onKeyPress,
contextmenu: $data.contextMenu.show,
drop: $data.onDrop,
dragover: $data.onDragOver
},
attr:{
'aria-expanded': $data.isCollectionExpanded,
'aria-selected': isCollectionNodeSelected()
}"
>
<span
class="collectionList databaseCollChildTextOverflow"
data-bind="
attr: {
title: $data.id()
}"
>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-right.svg"
alt="Show collection properties"
data-bind="visible: !$data.isCollectionExpanded()"
/>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-down.svg"
alt="Hide collection properties"
data-bind="visible: $data.isCollectionExpanded()"
/>
<img
src="/tree-collection.svg"
data-bind="
attr: {
alt: container.addCollectionText
}"
/>
<!--ko text: $data.id-->
<!--/ko-->
</span>
<span
class="menuEllipsis"
data-test="collectionEllipsisMenu"
name="context menu"
role="button"
data-bind="click: $data.contextMenu.show, clickBubble: false"
>&hellip;</span
>
</div>
<!-- Collection node children - Start -->
<div
class="collectionChildList"
role="group"
data-bind="
visible: $data.isCollectionExpanded,
clickBubble: false"
>
<!-- Documents Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
class="documentsMenu"
data-bind="
visible: $root.isPreferredApiDocumentDB(),
click: $data.onDocumentDBDocumentsClick,
event: {
keydown: onDocumentDBDocumentsKeyDown,
keypress: onDocumentDBDocumentsKeyPress
},
clickBubble: false,
css: {
highlight: true,
collectionNodeSelected: isSubNodeSelected(0)
},
attr:{
'aria-selected': isSubNodeSelected(0)
}"
>
<span class="databaseDocuments">Items</span>
</div>
</div>
<!-- Documents Node - End -->
<!-- Entitites Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
class="documentsMenu"
data-bind="
visible: $root.isPreferredApiTable(),
click: $data.onTableEntitiesClick,
event: {
keydown: onTableEntitiesKeyDown,
keypress: onTableEntitiesKeyPress
},
clickBubble: false,
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 9
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 9
}"
>
<span class="databaseDocuments">Entities</span>
</div>
</div>
<!-- Entitites Node - End -->
<!-- Rows Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
class="documentsMenu"
data-bind="
visible: $root.isPreferredApiCassandra(),
click: $data.onTableEntitiesClick,
event: {
keydown: onTableEntitiesKeyDown,
keypress: onTableEntitiesKeyPress
},
clickBubble: false,
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 9
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 9
}"
>
<span class="databaseDocuments">Rows</span>
</div>
</div>
<!-- Rows Node - End -->
<!-- Graph Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
class="documentsMenu"
data-bind="
visible: $root.isPreferredApiGraph,
click: $data.onGraphDocumentsClick,
event: {
keydown: onGraphDocumentsKeyDown,
keypress: onGraphDocumentsKeyPress
},
clickBubble: false,
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 6
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 6
}"
>
<span class="databaseDocuments">Graph</span>
</div>
</div>
<!-- Graph Node - End -->
<!-- MongoDB Documents Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
class="documentsMenu"
data-bind="
visible: $root.isPreferredApiMongoDB,
click: $data.onMongoDBDocumentsClick,
event: {
keydown: onMongoDBDocumentsKeyDown,
keypress: onMongoDBDocumentsKeyPress
},
clickBubble: false,
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 0
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 0
}"
>
<span class="databaseDocuments">Documents</span>
</div>
</div>
<!-- MongoDB Documents Node - End -->
<!-- Scale & Setings Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
data-bind="
click: $data.onSettingsClick,
event: {
keydown: onSettingsKeyDown,
keypress: onSettingsKeyPress
},
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 1
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 1
}"
>
<span class="databaseDocuments">
<!-- ko if: !$data.database.isDatabaseShared() -->
Scale & Settings
<!-- /ko -->
<!-- ko if: $data.database.isDatabaseShared() -->
Settings
<!-- /ko -->
</span>
</div>
</div>
<!-- Scale & Setings Node - End -->
<!-- Stored Procedures Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
class="storedProcedureMenu highlight"
data-bind="
click: $data.expandCollapseStoredProcedures,
event: {
keydown: onStoredProceduresKeyDown,
keypress: onStoredProceduresKeyPress
},
css: {
collectionNodeSelected: !isStoredProceduresExpanded() && isSubNodeSelected(2)
},
attr:{
'aria-expanded': $data.isStoredProceduresExpanded(),
'aria-selected': !isStoredProceduresExpanded() && isSubNodeSelected(2)
},
visible: showStoredProcedures"
>
<span
class="collectionMenuChildren"
data-bind="
attr: {
title: $data.id()
}"
>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-right.svg"
alt="Show storedprocedures properties"
data-bind="visible: !$data.isStoredProceduresExpanded()"
/>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-down.svg"
alt="Hide storedprocedures properties"
data-bind="visible: $data.isStoredProceduresExpanded()"
/>
Stored Procedures
</span>
</div>
<div
class="storedUdfTriggerMenu"
data-bind=" visible: $data.isStoredProceduresExpanded(), foreach: $data.storedProcedures"
>
<stored-procedure-node params="{data: $data}"></stored-procedure-node>
</div>
</div>
<!-- Stored Procedures Node - End -->
<!-- User Defined Functions Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
class="userDefinedMenu highlight"
data-bind="
click: $data.expandCollapseUserDefinedFunctions,
event: {
keydown: onUserDefinedFunctionsKeyDown,
keypress: onUserDefinedFunctionsKeyPress
},
css: {
collectionNodeSelected: !isUserDefinedFunctionsExpanded() && isSubNodeSelected(3)
},
attr:{
'aria-expanded': $data.isUserDefinedFunctionsExpanded(),
'aria-selected': !isUserDefinedFunctionsExpanded() && isSubNodeSelected(3)
},
visible: showUserDefinedFunctions"
>
<div>
<span
class="collectionMenuChildren"
data-bind="
attr: {
title: $data.id()
}"
>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-right.svg"
alt="Show userdefinedfunctions properties"
data-bind="visible: !$data.isUserDefinedFunctionsExpanded()"
/>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-down.svg"
alt="Hide userdefinedfunctions properties"
data-bind="visible: $data.isUserDefinedFunctionsExpanded()"
/>
User Defined Functions
</span>
</div>
</div>
<div
class="storedUdfTriggerMenu"
data-bind="visible: $data.isUserDefinedFunctionsExpanded(), foreach: $data.userDefinedFunctions"
>
<user-defined-function-node params="{data: $data}"></user-defined-function-node>
</div>
</div>
<!-- User Defined Functions Node - End -->
<!-- Triggers Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
class="triggersMenu highlight"
data-bind="
click: $data.expandCollapseTriggers,
event: {
keydown: onTriggersKeyDown,
keypress: onTriggersKeyPress
},
css: {
collectionNodeSelected: !isTriggersExpanded() && isSubNodeSelected(4)
},
attr:{
'aria-expanded': $data.isTriggersExpanded(),
'aria-selected': !isTriggersExpanded() && isSubNodeSelected(4)
},
visible: showTriggers"
>
<div>
<span
class="collectionMenuChildren"
data-bind="
attr: {
title: $data.id()
}"
>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-right.svg"
alt="Show Triggers properties"
data-bind="visible: !$data.isTriggersExpanded()"
/>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-down.svg"
alt="Hide Triggers properties"
data-bind="visible: $data.isTriggersExpanded()"
/>
Triggers
</span>
</div>
</div>
<div class="storedUdfTriggerMenu" data-bind="visible: $data.isTriggersExpanded(), foreach: $data.triggers">
<trigger-node params="{data: $data}"></trigger-node>
</div>
</div>
<!-- Triggers Node - End -->
<!-- Conflicts Node - Start -->
<div>
<div
role="treeitem"
tabindex="0"
data-bind="
click: $data.onConflictsClick,
event: {
keypress: onConflictsKeyPress
},
css: {
highlight: true,
collectionNodeSelected: isSubNodeSelected(12)
},
attr:{
'aria-selected': isSubNodeSelected(12)
},
visible: showConflicts"
>
<span class=" databaseDocuments"> Conflicts </span>
</div>
</div>
<!-- Conflicts Node - End -->
</div>
<!-- Collection node children - End -->
</div>

View File

@ -1,16 +0,0 @@
<div data-bind="event: { keydown: onMenuKeyDown }">
<div
class="context-menu-background"
data-bind="
visible: $data.contextMenu.visible,
click: $data.contextMenu.hide"
></div>
<div
class="context-menu"
data-test="collectionContextMenu"
data-bind="attr:{ tabindex: $data.contextMenu.tabIndex, id: $data.contextMenu.elementId }, visible: $data.contextMenu.visible, foreach: $data.contextMenu.options"
>
<command-button class="context-menu-option" params="{buttonProps: $data}"></command-button>
</div>
</div>

View File

@ -7,11 +7,9 @@ import * as DataModels from "../../Contracts/DataModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import DatabaseSettingsTab from "../Tabs/DatabaseSettingsTab";
import Collection from "./Collection";
import ContextMenu from "../Menus/ContextMenu";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { ContextMenuButtonFactory } from "../ContextMenuButtonFactory";
import { Logger } from "../../Common/Logger";
export default class Database implements ViewModels.Database {
@ -25,7 +23,6 @@ export default class Database implements ViewModels.Database {
public isDatabaseExpanded: ko.Observable<boolean>;
public isDatabaseShared: ko.Computed<boolean>;
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
public contextMenu: ViewModels.ContextMenu;
constructor(container: ViewModels.Explorer, data: any, offer: DataModels.Offer) {
this.nodeKind = "Database";
@ -36,71 +33,12 @@ export default class Database implements ViewModels.Database {
this.offer = ko.observable(offer);
this.collections = ko.observableArray<Collection>();
this.isDatabaseExpanded = ko.observable<boolean>(false);
this.contextMenu = new ContextMenu(this.container, this.rid);
this.contextMenu.options(
ContextMenuButtonFactory.createDatabaseContextMenuButton(container, { databaseId: this.id() })
);
this.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
this.isDatabaseShared = ko.pureComputed(() => {
return this.offer && !!this.offer();
});
}
public onKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.key === " " || event.key === "Enter") {
this.expandCollapseDatabase();
return false;
}
return true;
};
public onKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Delete") {
this.onDeleteDatabaseContextMenuClick(source, event);
return false;
}
if (event.key === "ArrowRight") {
this.expandDatabase();
return false;
}
if (event.key === "ArrowLeft") {
this.collapseDatabase();
return false;
}
if (event.key === "Enter") {
this.expandCollapseDatabase();
return false;
}
return true;
};
public onMenuKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Escape") {
this.contextMenu.hide(source, event);
return false;
}
return true;
};
public onSettingsKeyDown = (source: any, event: KeyboardEvent): boolean => {
return true;
};
public onSettingsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.key === " " || event.key === "Enter") {
this.onSettingsClick();
return false;
}
return true;
};
public onSettingsClick = () => {
this.container.selectedNode(this);
this.selectedSubnodeKind(ViewModels.CollectionTabKind.DatabaseSettings);
@ -262,8 +200,6 @@ export default class Database implements ViewModels.Database {
}
public onDeleteDatabaseContextMenuClick(source: ViewModels.Database, event: MouseEvent | KeyboardEvent) {
source.container.selectedNode(source);
source.contextMenu.hide(source, event);
this.container.deleteDatabaseConfirmationPane.open();
}
@ -346,7 +282,6 @@ export default class Database implements ViewModels.Database {
}
public openAddCollection(database: Database, event: MouseEvent) {
database.contextMenu.hide(database, event);
database.container.addCollectionPane.databaseId(database.id());
database.container.addCollectionPane.open();
}

View File

@ -1,110 +0,0 @@
<div class="pointerCursor">
<div
role="treeitem"
tabindex="0"
data-test="databaseMenu"
class="databaseMenu treeHovermargin highlight"
data-bind="
click: $data.expandCollapseDatabase,
event: {
keydown: onKeyDown,
keypress: onKeyPress,
contextmenu: $data.contextMenu.show
},
clickBubble: false,
contextmenuBubble: false,
css:{
contextmenushowing: $data.contextMenu.visible,
highlight: true,
databaseNodeSelected: isDatabaseNodeSelected()
},
attr:{
'aria-expanded': $data.isDatabaseExpanded,
'aria-selected': isDatabaseNodeSelected()
}"
>
<span
class="databaseId databaseCollChildTextOverflow"
data-bind="
attr: {
title: $data.id()
}"
>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-right.svg"
alt="Show database properties"
data-bind="visible: !$data.isDatabaseExpanded()"
/>
<img
class="imgiconwidth collectionsTreeCollapseExpand"
src="/Triangle-down.svg"
alt="Hide database properties"
data-bind="visible: $data.isDatabaseExpanded()"
/>
<img src="/Azure-Cosmos-DB.svg" alt="Database" />
<!--ko text: $data.id--><!--/ko-->
</span>
<span
class="menuEllipsis"
data-test="databaseEllipsisMenu"
name="context menu"
role="button"
data-bind="
click: $data.contextMenu.show,
clickBubble: false
"
>&hellip;</span
>
</div>
<div class="databaseList" data-test="databaseList" data-bind="visible: $data.isDatabaseExpanded">
<!-- Scale & Setings Node - Start -->
<div data-bind="visible: $data.isDatabaseShared">
<div
role="treeitem"
class="databaseCollChildTextOverflow treeHovermargin highlight"
tabindex="0"
data-bind="
click: $data.onSettingsClick,
event: {
keydown: onSettingsKeyDown,
keypress: onSettingsKeyPress
},
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 11
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid && $data.selectedSubnodeKind() === 11
}"
>
<span class="databaseDocuments"> Scale </span>
</div>
</div>
<!-- Scale & Setings Node - End -->
<div data-bind="foreach: $data.collections">
<collection-node params="{data: $data}"></collection-node>
<collection-node-context-menu params="{data: $data}"></collection-node-context-menu>
</div>
</div>
<!-- Database Context Menu - Start -->
<div data-bind="event: { keydown: onMenuKeyDown }">
<div
class="context-menu-background"
data-bind="
visible: $data.contextMenu.visible,
click: $data.contextMenu.hide"
></div>
<div
class="context-menu"
data-test="databaseContextMenu"
data-bind="attr:{ tabindex: $data.contextMenu.tabIndex, id: $data.contextMenu.elementId }, visible: $data.contextMenu.visible, foreach: $data.contextMenu.options"
>
<command-button class="context-menu-option" params="{buttonProps: $data}"></command-button>
</div>
</div>
<!-- Database Context Menu - End -->
</div>

View File

@ -1,11 +0,0 @@
<div
class="collectionstree"
data-test="resoureTree-collectionsTree"
tabindex="0"
role="tree"
data-bind="attr: { 'aria-label': collectionTitle }"
>
<div class="databaseList" data-bind="foreach: nonSystemDatabases">
<database-node params="{data: $data}"></database-node>
</div>
</div>

View File

@ -301,7 +301,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
onClick: sp.open.bind(sp),
isSelected: () =>
this.isDataNodeSelected(collection.rid, "Collection", ViewModels.CollectionTabKind.StoredProcedures),
contextMenu: ResourceTreeContextMenuButtonFactory.createStoreProcedureContextMenuItems(this.container)
contextMenu: ResourceTreeContextMenuButtonFactory.createStoreProcedureContextMenuItems(this.container, sp)
})),
onClick: () => {
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.StoredProcedures);
@ -318,7 +318,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
onClick: udf.open.bind(udf),
isSelected: () =>
this.isDataNodeSelected(collection.rid, "Collection", ViewModels.CollectionTabKind.UserDefinedFunctions),
contextMenu: ResourceTreeContextMenuButtonFactory.createUserDefinedFunctionContextMenuItems(this.container)
contextMenu: ResourceTreeContextMenuButtonFactory.createUserDefinedFunctionContextMenuItems(this.container, udf)
})),
onClick: () => {
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.UserDefinedFunctions);
@ -334,7 +334,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
label: trigger.id(),
onClick: trigger.open.bind(trigger),
isSelected: () => this.isDataNodeSelected(collection.rid, "Collection", ViewModels.CollectionTabKind.Triggers),
contextMenu: ResourceTreeContextMenuButtonFactory.createTriggerContextMenuItems(this.container)
contextMenu: ResourceTreeContextMenuButtonFactory.createTriggerContextMenuItems(this.container, trigger)
})),
onClick: () => {
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Triggers);

View File

@ -5,9 +5,7 @@ import * as DataModels from "../../Contracts/DataModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import StoredProcedureTab from "../Tabs/StoredProcedureTab";
import ContextMenu from "../Menus/ContextMenu";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { ContextMenuButtonFactory } from "../ContextMenuButtonFactory";
const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE
function sample(prefix) {
@ -44,7 +42,6 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
public rid: string;
public id: ko.Observable<string>;
public body: ko.Observable<string>;
public contextMenu: ViewModels.ContextMenu;
public isExecuteEnabled: boolean;
constructor(container: ViewModels.Explorer, collection: ViewModels.Collection, data: DataModels.StoredProcedure) {
@ -56,9 +53,6 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
this.id = ko.observable(data.id);
this.body = ko.observable(data.body);
this.isExecuteEnabled = this.container.isFeatureEnabled(Constants.Features.executeSproc);
this.contextMenu = new ContextMenu(this.container, this.rid);
this.contextMenu.options(ContextMenuButtonFactory.createStoreProcedureContextMenuButton(container));
}
public static create(source: ViewModels.Collection, event: MouseEvent) {
@ -89,9 +83,6 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
// Activate
storedProcedureTab.onTabClick();
// Hide Context Menu (if necessary)
source.contextMenu.hide(source, event);
}
public select() {
@ -148,10 +139,7 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
storedProcedureTab.onTabClick();
};
public delete(source: ViewModels.Collection, event: MouseEvent | KeyboardEvent) {
// Hide Context Menu (if necessary)
this.contextMenu.hide(source, event);
public delete() {
if (!window.confirm("Are you sure you want to delete the stored procedure?")) {
return;
}
@ -192,33 +180,6 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
});
}
public onKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Delete") {
this.delete(source, event);
return false;
}
return true;
};
public onMenuKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Escape") {
this.contextMenu.hide(source, event);
return false;
}
return true;
};
public onKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.key === " " || event.key === "Enter") {
this.open();
return false;
}
return true;
};
public onFocusAfterExecute(): void {
const focusElement = document.getElementById("execute-storedproc-toggles");
focusElement && focusElement.focus();

View File

@ -1,63 +0,0 @@
<div
role="treeitem"
tabindex="0"
class="pointerCursor"
data-bind="
click: $data.open,
event: {
keydown: onKeyDown,
keypress: onKeyPress,
contextmenu: $data.contextMenu.show
},
clickBubble: false,
contextmenuBubble: false,
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid,
contextmenushowing: $data.contextMenu.visible
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid
}"
>
<div class="storedChildMenu treeChildMenu">
<div
class="childMenu"
data-bind="
attr: {
title: $data.id()
}"
>
<!--ko text: $data.id-->
<!--/ko-->
</div>
<span
class="menuEllipsis"
name="context menu"
role="button"
data-bind="
click: $data.contextMenu.show,
clickBubble: false
"
>&hellip;</span
>
</div>
</div>
<!-- Stored Procedure Node Context Menu - Start -->
<div data-bind="event: { keydown: onMenuKeyDown }">
<div
class="context-menu-background"
data-bind="
visible: $data.contextMenu.visible,
click: $data.contextMenu.hide"
></div>
<div
class="context-menu"
data-bind="attr:{ tabindex: $data.contextMenu.tabIndex, id: $data.contextMenu.elementId }, visible: $data.contextMenu.visible, foreach: $data.contextMenu.options"
>
<command-button class="context-menu-option" params="{buttonProps: $data}"></command-button>
</div>
</div>
<!-- Stored Procedure Node Context Menu - End -->

View File

@ -1,76 +0,0 @@
import resourceTreeTemplate from "./ResourceTree.html";
import databaseTreeNoteTemplate from "./DatabaseTreeNode.html";
import collectionTreeNodeTemplate from "./CollectionTreeNode.html";
import storedProcedureTreeNodeTemplate from "./StoredProcedureTreeNode.html";
import userDefinedFunctionTreeNodeTemplate from "./UserDefinedFunctionTreeNode.html";
import triggerTreeNodeTemplate from "./TriggerTreeNode.html";
import collectionTreeNodeContextMenuTemplate from "./CollectionTreeNodeContextMenu.html";
export class TreeNodeComponent {
constructor(data: any) {
return data.data;
}
}
export class ResourceTree {
constructor() {
return {
viewModel: TreeNodeComponent,
template: resourceTreeTemplate
};
}
}
export class DatabaseTreeNode {
constructor() {
return {
viewModel: TreeNodeComponent,
template: databaseTreeNoteTemplate
};
}
}
export class CollectionTreeNode {
constructor() {
return {
viewModel: TreeNodeComponent,
template: collectionTreeNodeTemplate
};
}
}
export class StoredProcedureTreeNode {
constructor() {
return {
viewModel: TreeNodeComponent,
template: storedProcedureTreeNodeTemplate
};
}
}
export class UserDefinedFunctionTreeNode {
constructor() {
return {
viewModel: TreeNodeComponent,
template: userDefinedFunctionTreeNodeTemplate
};
}
}
export class TriggerTreeNode {
constructor() {
return {
viewModel: TreeNodeComponent,
template: triggerTreeNodeTemplate
};
}
}
export class CollectionTreeNodeContextMenu {
constructor() {
return {
viewModel: TreeNodeComponent,
template: collectionTreeNodeContextMenuTemplate
};
}
}

View File

@ -5,9 +5,7 @@ import * as DataModels from "../../Contracts/DataModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import Collection from "./Collection";
import TriggerTab from "../Tabs/TriggerTab";
import ContextMenu from "../Menus/ContextMenu";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { ContextMenuButtonFactory } from "../ContextMenuButtonFactory";
export default class Trigger implements ViewModels.Trigger {
public nodeKind: string;
@ -19,7 +17,6 @@ export default class Trigger implements ViewModels.Trigger {
public body: ko.Observable<string>;
public triggerType: ko.Observable<string>;
public triggerOperation: ko.Observable<string>;
public contextMenu: ViewModels.ContextMenu;
constructor(container: ViewModels.Explorer, collection: ViewModels.Collection, data: any) {
this.nodeKind = "Trigger";
@ -31,9 +28,6 @@ export default class Trigger implements ViewModels.Trigger {
this.body = ko.observable(data.body);
this.triggerOperation = ko.observable(data.triggerOperation);
this.triggerType = ko.observable(data.triggerType);
this.contextMenu = new ContextMenu(this.container, this.rid);
this.contextMenu.options(ContextMenuButtonFactory.createTriggerContextMenuButton(container));
}
public select() {
@ -78,9 +72,6 @@ export default class Trigger implements ViewModels.Trigger {
// Activate
triggerTab.onTabClick();
// Hide Context Menu (if necessary)
source.contextMenu.hide(source, event);
}
public open = () => {
@ -125,10 +116,7 @@ export default class Trigger implements ViewModels.Trigger {
triggerTab.onTabClick();
};
public delete(source: Collection, event: MouseEvent | KeyboardEvent) {
// Hide Context Menu (if necessary)
this.contextMenu.hide(source, event);
public delete() {
if (!window.confirm("Are you sure you want to delete the trigger?")) {
return;
}
@ -150,31 +138,4 @@ export default class Trigger implements ViewModels.Trigger {
reason => {}
);
}
public onKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Delete") {
this.delete(source, event);
return false;
}
return true;
};
public onMenuKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Escape") {
this.contextMenu.hide(source, event);
return false;
}
return true;
};
public onKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.key === " " || event.key === "Enter") {
this.open();
return false;
}
return true;
};
}

View File

@ -1,62 +0,0 @@
<div
role="treeitem"
tabindex="0"
class="pointerCursor"
data-bind="
click: $data.open,
event: {
keydown: onKeyDown,
keypress: onKeyPress,
contextmenu: $data.contextMenu.show
},
clickBubble: false,
contextmenuBubble: false,
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid,
contextmenushowing: $data.contextMenu.visible
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid
}"
>
<div class="triggersChildMenu treeChildMenu">
<div
class="childMenu"
data-bind="
attr: {
title: $data.id()
}"
>
<!--ko text: $data.id-->
<!--/ko-->
</div>
<span
class="menuEllipsis"
name="context menu"
role="button"
data-bind="
click: $data.contextMenu.show,
clickBubble: false
"
>&hellip;</span
>
</div>
</div>
<!-- Trigger Node Context Menu - Start -->
<div data-bind="event: { keydown: onMenuKeyDown }">
<div
class="context-menu-background"
data-bind="
visible: $data.contextMenu.visible,
click: $data.contextMenu.hide"
></div>
<div
class="context-menu"
data-bind="attr:{ tabindex: $data.contextMenu.tabIndex, id: $data.contextMenu.elementId }, visible: $data.contextMenu.visible, foreach: $data.contextMenu.options"
>
<command-button class="context-menu-option" params="{buttonProps: $data}"></command-button>
</div>
</div>

View File

@ -4,9 +4,7 @@ import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import UserDefinedFunctionTab from "../Tabs/UserDefinedFunctionTab";
import ContextMenu from "../Menus/ContextMenu";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { ContextMenuButtonFactory } from "../ContextMenuButtonFactory";
import Collection from "./Collection";
export default class UserDefinedFunction implements ViewModels.UserDefinedFunction {
@ -17,7 +15,6 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
public rid: string;
public id: ko.Observable<string>;
public body: ko.Observable<string>;
public contextMenu: ViewModels.ContextMenu;
constructor(container: ViewModels.Explorer, collection: ViewModels.Collection, data: DataModels.UserDefinedFunction) {
this.nodeKind = "UserDefinedFunction";
@ -28,9 +25,6 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
this.rid = data._rid;
this.id = ko.observable(data.id);
this.body = ko.observable(data.body);
this.contextMenu = new ContextMenu(this.container, this.rid);
this.contextMenu.options(ContextMenuButtonFactory.createUserDefinedFunctionContextMenuButton(container));
}
public static create(source: ViewModels.Collection, event: MouseEvent) {
@ -61,9 +55,6 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
// Activate
userDefinedFunctionTab.onTabClick();
// Hide Context Menu (if necessary)
source.contextMenu.hide(source, event);
}
public open = () => {
@ -115,10 +106,7 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
});
}
public delete(source: Collection, event: MouseEvent | KeyboardEvent) {
// Hide Context Menu (if necessary)
this.contextMenu.hide(source, event);
public delete() {
if (!window.confirm("Are you sure you want to delete the user defined function?")) {
return;
}
@ -137,31 +125,4 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
reason => {}
);
}
public onKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Delete") {
this.delete(source, event);
return false;
}
return true;
};
public onMenuKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.key === "Escape") {
this.contextMenu.hide(source, event);
return false;
}
return true;
};
public onKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.key === " " || event.key === "Enter") {
this.open();
return false;
}
return true;
};
}

View File

@ -1,62 +0,0 @@
<div
role="treeitem"
tabindex="0"
class="pointerCursor"
data-bind="
click: $data.open,
event: {
keydown: onKeyDown,
keypress: onKeyPress,
contextmenu: $data.contextMenu.show
},
clickBubble: false,
contextmenuBubble: false,
css: {
highlight: true,
collectionNodeSelected: $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid,
contextmenushowing: $data.contextMenu.visible
},
attr:{
'aria-selected': $root.selectedNode && $root.selectedNode() && $root.selectedNode().rid === $data.rid
}"
>
<div class="userDefinedchildMenu treeChildMenu">
<div
class="childMenu"
data-bind="
attr: {
title: $data.id()
}"
>
<!--ko text: $data.id-->
<!--/ko-->
</div>
<span
class="menuEllipsis"
name="context menu"
role="button"
data-bind="
click: $data.contextMenu.show,
clickBubble: false
"
>&hellip;</span
>
</div>
</div>
<!-- User Defined Function Node Context Menu - Start -->
<div data-bind="event: { keydown: onMenuKeyDown }">
<div
class="context-menu-background"
data-bind="
visible: $data.contextMenu.visible,
click: $data.contextMenu.hide"
></div>
<div
class="context-menu"
data-bind="attr:{ tabindex: $data.contextMenu.tabIndex, id: $data.contextMenu.elementId }, visible: $data.contextMenu.visible, foreach: $data.contextMenu.options"
>
<command-button class="context-menu-option" params="{buttonProps: $data}"></command-button>
</div>
</div>
<!-- User Defined Function Node Context Menu - End -->

View File

@ -7,7 +7,6 @@ import "../less/menus.less";
import "../less/infobox.less";
import "../less/messagebox.less";
import "./Explorer/Controls/InputTypeahead/InputTypeahead.less";
import "./Explorer/Controls/CommandButton/CommandButton.less";
import "./Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.less";
import "./Explorer/Menus/NotificationConsole/NotificationConsole.less";
import "./Explorer/Menus/CommandBar/CommandBarComponent.less";

View File

@ -128,15 +128,12 @@
</div>
</div>
<!-- Collections Window Title/Command Bar - End -->
<!-- ko if: !enableLegacyResourceTree() && !isAuthWithResourceToken() -->
<!-- ko if: !isAuthWithResourceToken() -->
<div style="overflow-y: auto" data-bind="react:resourceTree"></div>
<!-- /ko -->
<!-- ko if: !enableLegacyResourceTree() && isAuthWithResourceToken() -->
<!-- ko if: isAuthWithResourceToken() -->
<div style="overflow-y: auto" data-bind="react:resourceTreeForResourceToken"></div>
<!-- /ko -->
<!-- ko if: enableLegacyResourceTree -->
<resource-tree class="resourceTreeScroll" params="{data: $data}"></resource-tree>
<!-- /ko -->
</div>
<!-- Collections Window - End -->
</div>