mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-08 03:57:31 +00:00
use npm version 6
This commit is contained in:
@@ -119,14 +119,10 @@ src/Explorer/Panes/ContextualPaneBase.ts
|
|||||||
src/Explorer/Panes/DeleteDatabaseConfirmationPane.test.ts
|
src/Explorer/Panes/DeleteDatabaseConfirmationPane.test.ts
|
||||||
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
||||||
src/Explorer/Panes/GraphStylingPane.ts
|
src/Explorer/Panes/GraphStylingPane.ts
|
||||||
# src/Explorer/Panes/NewVertexPane.ts
|
|
||||||
src/Explorer/Panes/PaneComponents.ts
|
src/Explorer/Panes/PaneComponents.ts
|
||||||
src/Explorer/Panes/RenewAdHocAccessPane.ts
|
src/Explorer/Panes/RenewAdHocAccessPane.ts
|
||||||
src/Explorer/Panes/SetupNotebooksPane.ts
|
src/Explorer/Panes/SetupNotebooksPane.ts
|
||||||
src/Explorer/Panes/SwitchDirectoryPane.ts
|
src/Explorer/Panes/SwitchDirectoryPane.ts
|
||||||
src/Explorer/Panes/Tables/EditTableEntityPane.ts
|
|
||||||
src/Explorer/Panes/Tables/EntityPropertyViewModel.ts
|
|
||||||
src/Explorer/Panes/Tables/TableEntityPane.ts
|
|
||||||
src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts
|
src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts
|
||||||
src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
|
src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
|
||||||
src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts
|
src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts
|
||||||
|
|||||||
@@ -188,7 +188,3 @@ Cosmos Explorer has been under constant development for over 5 years. As a resul
|
|||||||
✅ DO
|
✅ DO
|
||||||
|
|
||||||
- Support all [browsers supported by the Azure Portal](https://docs.microsoft.com/en-us/azure/azure-portal/azure-portal-supported-browsers-devices)
|
- Support all [browsers supported by the Azure Portal](https://docs.microsoft.com/en-us/azure/azure-portal/azure-portal-supported-browsers-devices)
|
||||||
- Support IE11
|
|
||||||
- In practice, this should not need to be considered as part of a normal development workflow
|
|
||||||
- Polyfills and transpilation are already provided by our engineering systems.
|
|
||||||
- This requirement will be removed on March 30th, 2021 when Azure drops IE11 support.
|
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"],
|
presets: [
|
||||||
|
["@babel/preset-env", { targets: { node: "current" } }],
|
||||||
|
[
|
||||||
|
"@babel/preset-react",
|
||||||
|
{
|
||||||
|
runtime: "automatic",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"@babel/preset-typescript",
|
||||||
|
],
|
||||||
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
|
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
|
||||||
};
|
};
|
||||||
|
|||||||
33178
package-lock.json
generated
33178
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -11,7 +11,7 @@
|
|||||||
"@azure/ms-rest-nodeauth": "3.0.7",
|
"@azure/ms-rest-nodeauth": "3.0.7",
|
||||||
"@babel/plugin-proposal-class-properties": "7.13.0",
|
"@babel/plugin-proposal-class-properties": "7.13.0",
|
||||||
"@babel/plugin-proposal-decorators": "7.13.15",
|
"@babel/plugin-proposal-decorators": "7.13.15",
|
||||||
"@fluentui/react": "8.10.1",
|
"@fluentui/react": "8.14.4",
|
||||||
"@jupyterlab/services": "6.0.2",
|
"@jupyterlab/services": "6.0.2",
|
||||||
"@jupyterlab/terminal": "3.0.3",
|
"@jupyterlab/terminal": "3.0.3",
|
||||||
"@microsoft/applicationinsights-web": "2.6.1",
|
"@microsoft/applicationinsights-web": "2.6.1",
|
||||||
@@ -40,7 +40,6 @@
|
|||||||
"@nteract/transform-vega": "7.0.6",
|
"@nteract/transform-vega": "7.0.6",
|
||||||
"@octokit/rest": "17.9.2",
|
"@octokit/rest": "17.9.2",
|
||||||
"@phosphor/widgets": "1.9.3",
|
"@phosphor/widgets": "1.9.3",
|
||||||
"@testing-library/jest-dom": "5.12.0",
|
|
||||||
"@types/mkdirp": "1.0.1",
|
"@types/mkdirp": "1.0.1",
|
||||||
"@types/node-fetch": "2.5.7",
|
"@types/node-fetch": "2.5.7",
|
||||||
"@uifabric/react-cards": "0.109.110",
|
"@uifabric/react-cards": "0.109.110",
|
||||||
@@ -55,15 +54,15 @@
|
|||||||
"datatables.net-colreorder-dt": "1.5.1",
|
"datatables.net-colreorder-dt": "1.5.1",
|
||||||
"datatables.net-dt": "1.10.19",
|
"datatables.net-dt": "1.10.19",
|
||||||
"date-fns": "1.29.0",
|
"date-fns": "1.29.0",
|
||||||
"dayjs": "1.8.19",
|
"dayjs": "1.10.4",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "9.0.0",
|
||||||
"eslint-plugin-jest": "23.13.2",
|
"eslint-plugin-jest": "24.3.6",
|
||||||
"eslint-plugin-react": "7.23.2",
|
"eslint-plugin-react": "7.23.2",
|
||||||
"hasher": "1.2.0",
|
"hasher": "1.2.0",
|
||||||
"html2canvas": "1.0.0-rc.5",
|
"html2canvas": "1.0.0-rc.7",
|
||||||
"i18next": "19.8.4",
|
"i18next": "20.2.2",
|
||||||
"i18next-browser-languagedetector": "6.0.1",
|
"i18next-browser-languagedetector": "6.1.0",
|
||||||
"i18next-http-backend": "1.0.23",
|
"i18next-http-backend": "1.2.2",
|
||||||
"iframe-resizer-react": "1.1.0",
|
"iframe-resizer-react": "1.1.0",
|
||||||
"immutable": "4.0.0-rc.12",
|
"immutable": "4.0.0-rc.12",
|
||||||
"is-ci": "2.0.0",
|
"is-ci": "2.0.0",
|
||||||
@@ -85,7 +84,7 @@
|
|||||||
"react-dnd-html5-backend": "14.0.0",
|
"react-dnd-html5-backend": "14.0.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-hotkeys": "2.0.0",
|
"react-hotkeys": "2.0.0",
|
||||||
"react-i18next": "11.8.5",
|
"react-i18next": "11.8.15",
|
||||||
"react-notification-system": "0.2.17",
|
"react-notification-system": "0.2.17",
|
||||||
"react-redux": "7.2.4",
|
"react-redux": "7.2.4",
|
||||||
"redux": "4.1.0",
|
"redux": "4.1.0",
|
||||||
@@ -93,7 +92,7 @@
|
|||||||
"rx-jupyter": "5.5.12",
|
"rx-jupyter": "5.5.12",
|
||||||
"rxjs": "6.6.3",
|
"rxjs": "6.6.3",
|
||||||
"sanitize-html": "2.3.3",
|
"sanitize-html": "2.3.3",
|
||||||
"styled-components": "4.3.2",
|
"styled-components": "5.3.0",
|
||||||
"swr": "0.4.0",
|
"swr": "0.4.0",
|
||||||
"underscore": "1.9.1",
|
"underscore": "1.9.1",
|
||||||
"utility-types": "3.10.0"
|
"utility-types": "3.10.0"
|
||||||
@@ -104,6 +103,7 @@
|
|||||||
"@babel/preset-react": "7.13.13",
|
"@babel/preset-react": "7.13.13",
|
||||||
"@babel/preset-typescript": "7.13.0",
|
"@babel/preset-typescript": "7.13.0",
|
||||||
"@svgr/webpack": "5.5.0",
|
"@svgr/webpack": "5.5.0",
|
||||||
|
"@testing-library/jest-dom": "5.12.0",
|
||||||
"@testing-library/react": "11.2.6",
|
"@testing-library/react": "11.2.6",
|
||||||
"@types/applicationinsights-js": "1.0.7",
|
"@types/applicationinsights-js": "1.0.7",
|
||||||
"@types/codemirror": "0.0.56",
|
"@types/codemirror": "0.0.56",
|
||||||
@@ -117,15 +117,15 @@
|
|||||||
"@types/post-robot": "10.0.1",
|
"@types/post-robot": "10.0.1",
|
||||||
"@types/promise.prototype.finally": "2.0.3",
|
"@types/promise.prototype.finally": "2.0.3",
|
||||||
"@types/q": "1.5.1",
|
"@types/q": "1.5.1",
|
||||||
"@types/react": "17.0.3",
|
"@types/react": "17.0.5",
|
||||||
"@types/react-dom": "17.0.3",
|
"@types/react-dom": "17.0.3",
|
||||||
"@types/react-notification-system": "0.2.39",
|
"@types/react-notification-system": "0.2.39",
|
||||||
"@types/react-redux": "7.1.16",
|
"@types/react-redux": "7.1.16",
|
||||||
"@types/sanitize-html": "1.27.2",
|
"@types/sanitize-html": "1.27.2",
|
||||||
"@types/sinon": "2.3.3",
|
"@types/sinon": "2.3.3",
|
||||||
"@types/styled-components": "5.1.1",
|
"@types/styled-components": "5.1.9",
|
||||||
"@types/underscore": "1.7.36",
|
"@types/underscore": "1.7.36",
|
||||||
"@typescript-eslint/eslint-plugin": "4.22.0",
|
"@typescript-eslint/eslint-plugin": "4.22.1",
|
||||||
"@typescript-eslint/parser": "4.22.1",
|
"@typescript-eslint/parser": "4.22.1",
|
||||||
"@wojtekmaj/enzyme-adapter-react-17": "0.6.1",
|
"@wojtekmaj/enzyme-adapter-react-17": "0.6.1",
|
||||||
"babel-jest": "26.6.3",
|
"babel-jest": "26.6.3",
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"wait-on": "4.0.2",
|
"wait-on": "4.0.2",
|
||||||
"webpack": "5.36.2",
|
"webpack": "5.36.2",
|
||||||
"webpack-bundle-analyzer": "4.4.1",
|
"webpack-bundle-analyzer": "4.4.1",
|
||||||
"webpack-cli": "4.6.0",
|
"webpack-cli": "4.7.0",
|
||||||
"webpack-dev-server": "3.11.2"
|
"webpack-dev-server": "3.11.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ITooltipHostStyles, TooltipHost } from "@fluentui/react";
|
import { ITooltipHostStyles, TooltipHost } from "@fluentui/react";
|
||||||
import { useId } from "@fluentui/react-hooks";
|
import { useId } from "@fluentui/react-hooks";
|
||||||
import { ReactComponent as InfoBubble } from "images/info-bubble.svg";
|
import { ReactComponent as InfoBubble } from "images/info-bubble.svg";
|
||||||
import * as React from "react";
|
import React, { FunctionComponent } from "react";
|
||||||
|
|
||||||
const calloutProps = { gapSpace: 0 };
|
const calloutProps = { gapSpace: 0 };
|
||||||
const hostStyles: Partial<ITooltipHostStyles> = { root: { display: "inline-block" } };
|
const hostStyles: Partial<ITooltipHostStyles> = { root: { display: "inline-block" } };
|
||||||
@@ -9,7 +9,7 @@ const hostStyles: Partial<ITooltipHostStyles> = { root: { display: "inline-block
|
|||||||
export interface TooltipProps {
|
export interface TooltipProps {
|
||||||
children: string;
|
children: string;
|
||||||
}
|
}
|
||||||
export const Tooltip: React.FunctionComponent = ({ children }: TooltipProps) => {
|
export const Tooltip: FunctionComponent = ({ children }: TooltipProps) => {
|
||||||
const tooltipId = useId("tooltip");
|
const tooltipId = useId("tooltip");
|
||||||
|
|
||||||
return children ? (
|
return children ? (
|
||||||
|
|||||||
@@ -22,6 +22,4 @@ ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponent
|
|||||||
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
||||||
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
||||||
ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPaneComponent());
|
ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPaneComponent());
|
||||||
ko.components.register("table-add-entity-pane", new PaneComponents.TableAddEntityPaneComponent());
|
|
||||||
ko.components.register("table-edit-entity-pane", new PaneComponents.TableEditEntityPaneComponent());
|
|
||||||
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
||||||
|
|||||||
@@ -1959,7 +1959,7 @@ exports[`test render renders with filters 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</DefaultButton>
|
</DefaultButton>
|
||||||
</CustomizedDefaultButton>
|
</CustomizedDefaultButton>
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
|
|||||||
>
|
>
|
||||||
capacity calculator
|
capacity calculator
|
||||||
|
|
||||||
<Component
|
<FontIcon
|
||||||
iconName="NavigateExternalInline"
|
iconName="NavigateExternalInline"
|
||||||
/>
|
/>
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
@@ -526,7 +526,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
|
|||||||
>
|
>
|
||||||
capacity calculator
|
capacity calculator
|
||||||
|
|
||||||
<Component
|
<FontIcon
|
||||||
iconName="NavigateExternalInline"
|
iconName="NavigateExternalInline"
|
||||||
/>
|
/>
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -253,6 +254,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -1379,6 +1381,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -1529,6 +1532,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -2668,6 +2672,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -2818,6 +2823,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -3944,6 +3950,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -4094,6 +4101,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import { IChoiceGroupProps } from "@fluentui/react";
|
import { IChoiceGroupProps } from "@fluentui/react";
|
||||||
import * as path from "path";
|
import * as ko from "knockout";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
@@ -1843,39 +1842,6 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openNotebookViewer(notebookUrl: string) {
|
|
||||||
const title = path.basename(notebookUrl);
|
|
||||||
const hashLocation = notebookUrl;
|
|
||||||
const NotebookViewerTab = await (
|
|
||||||
await import(/* webpackChunkName: "NotebookViewerTab" */ "./Tabs/NotebookViewerTab")
|
|
||||||
).default;
|
|
||||||
|
|
||||||
const notebookViewerTab = this.tabsManager.getTabs(ViewModels.CollectionTabKind.NotebookV2).find((tab) => {
|
|
||||||
return tab.hashLocation() == hashLocation && tab instanceof NotebookViewerTab && tab.notebookUrl === notebookUrl;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (notebookViewerTab) {
|
|
||||||
this.tabsManager.activateNewTab(notebookViewerTab);
|
|
||||||
} else {
|
|
||||||
const notebookViewerTab = new NotebookViewerTab({
|
|
||||||
account: userContext.databaseAccount,
|
|
||||||
tabKind: ViewModels.CollectionTabKind.NotebookViewer,
|
|
||||||
node: null,
|
|
||||||
title: title,
|
|
||||||
tabPath: title,
|
|
||||||
collection: null,
|
|
||||||
hashLocation: hashLocation,
|
|
||||||
isTabsContentExpanded: ko.observable(true),
|
|
||||||
onLoadStartKey: null,
|
|
||||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
|
||||||
container: this,
|
|
||||||
notebookUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tabsManager.activateNewTab(notebookViewerTab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public onNewCollectionClicked(databaseId?: string): void {
|
public onNewCollectionClicked(databaseId?: string): void {
|
||||||
if (userContext.apiType === "Cassandra") {
|
if (userContext.apiType === "Cassandra") {
|
||||||
this.cassandraAddCollectionPane.open();
|
this.cassandraAddCollectionPane.open();
|
||||||
|
|||||||
@@ -733,15 +733,16 @@ export class D3ForceGraph implements GraphRenderer {
|
|||||||
.attr("aria-label", (d: D3Node) => {
|
.attr("aria-label", (d: D3Node) => {
|
||||||
return this.retrieveNodeCaption(d);
|
return this.retrieveNodeCaption(d);
|
||||||
})
|
})
|
||||||
.on("dblclick", function (_: MouseEvent, d: D3Node) {
|
.on("dblclick", function (this: Element, _: MouseEvent, d: D3Node) {
|
||||||
|
// https://stackoverflow.com/a/41945742 ('this' implicitly has type 'any' because it does not have a type annotation)
|
||||||
// this is the <g> element
|
// this is the <g> element
|
||||||
self.onNodeClicked(this.parentNode, d);
|
self.onNodeClicked(this.parentNode, d);
|
||||||
})
|
})
|
||||||
.on("click", function (_: MouseEvent, d: D3Node) {
|
.on("click", function (this: Element, _: MouseEvent, d: D3Node) {
|
||||||
// this is the <g> element
|
// this is the <g> element
|
||||||
self.onNodeClicked(this.parentNode, d);
|
self.onNodeClicked(this.parentNode, d);
|
||||||
})
|
})
|
||||||
.on("keypress", function (event: KeyboardEvent, d: D3Node) {
|
.on("keypress", function (this: Element, event: KeyboardEvent, d: D3Node) {
|
||||||
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
|
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
// this is the <g> element
|
// this is the <g> element
|
||||||
|
|||||||
@@ -123,8 +123,9 @@ export const cellTarget = {
|
|||||||
if (monitor) {
|
if (monitor) {
|
||||||
const hoverUpperHalf = isDragUpper(props, monitor, component.el);
|
const hoverUpperHalf = isDragUpper(props, monitor, component.el);
|
||||||
// DropTargetSpec monitor definition could be undefined. we'll need a check for monitor in order to pass validation.
|
// DropTargetSpec monitor definition could be undefined. we'll need a check for monitor in order to pass validation.
|
||||||
|
const item: Props = monitor.getItem();
|
||||||
props.moveCell({
|
props.moveCell({
|
||||||
id: monitor.getItem().id,
|
id: item.id,
|
||||||
destinationId: props.id,
|
destinationId: props.id,
|
||||||
above: hoverUpperHalf,
|
above: hoverUpperHalf,
|
||||||
contentRef: props.contentRef,
|
contentRef: props.contentRef,
|
||||||
|
|||||||
@@ -438,7 +438,7 @@
|
|||||||
<!-- Provision collection throughput - end -->
|
<!-- Provision collection throughput - end -->
|
||||||
|
|
||||||
<!-- Custom indexes for mongo checkbox - start -->
|
<!-- Custom indexes for mongo checkbox - start -->
|
||||||
<div class="pkPadding" data-bind="visible: container.isEnableMongoCapabilityPresent()">
|
<div class="pkPadding" data-bind="visible: isEnableMongoCapabilityEnabled()">
|
||||||
<p>
|
<p>
|
||||||
<span class="addCollectionLabel">Indexing</span>
|
<span class="addCollectionLabel">Indexing</span>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
|
|||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
|
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
|
||||||
|
import { isCapabilityEnabled } from "../../Utils/CapabilityUtils";
|
||||||
import * as PricingUtils from "../../Utils/PricingUtils";
|
import * as PricingUtils from "../../Utils/PricingUtils";
|
||||||
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
|
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
|
||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
@@ -95,6 +96,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
public shouldCreateMongoWildcardIndex: ko.Observable<boolean>;
|
public shouldCreateMongoWildcardIndex: ko.Observable<boolean>;
|
||||||
|
|
||||||
private _isSynapseLinkEnabled: ko.Computed<boolean>;
|
private _isSynapseLinkEnabled: ko.Computed<boolean>;
|
||||||
|
private isEnableMongoCapabilityEnabled: ko.Observable<boolean>;
|
||||||
|
|
||||||
constructor(options: AddCollectionPaneOptions) {
|
constructor(options: AddCollectionPaneOptions) {
|
||||||
super(options);
|
super(options);
|
||||||
@@ -634,6 +636,8 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.isEnableMongoCapabilityEnabled = ko.observable(isCapabilityEnabled("EnableMongo"));
|
||||||
|
|
||||||
this.shouldCreateMongoWildcardIndex = ko.observable(this.container.isMongoIndexingEnabled());
|
this.shouldCreateMongoWildcardIndex = ko.observable(this.container.isMongoIndexingEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
|||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
import { getCollectionName } from "../../Utils/APITypeUtils";
|
import { getCollectionName } from "../../Utils/APITypeUtils";
|
||||||
|
import { isCapabilityEnabled } from "../../Utils/CapabilityUtils";
|
||||||
import { getUpsellMessage } from "../../Utils/PricingUtils";
|
import { getUpsellMessage } from "../../Utils/PricingUtils";
|
||||||
import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||||
import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput";
|
import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput";
|
||||||
@@ -80,7 +81,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
isSharded: userContext.apiType !== "Tables",
|
isSharded: userContext.apiType !== "Tables",
|
||||||
partitionKey: "",
|
partitionKey: "",
|
||||||
enableDedicatedThroughput: false,
|
enableDedicatedThroughput: false,
|
||||||
createMongoWildCardIndex: true,
|
createMongoWildCardIndex: isCapabilityEnabled("EnableMongo"),
|
||||||
useHashV2: false,
|
useHashV2: false,
|
||||||
enableAnalyticalStore: false,
|
enableAnalyticalStore: false,
|
||||||
uniqueKeys: [],
|
uniqueKeys: [],
|
||||||
@@ -525,7 +526,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack className="panelGroupSpacing" id="collapsibleSectionContent">
|
<Stack className="panelGroupSpacing" id="collapsibleSectionContent">
|
||||||
{userContext.databaseAccount.properties.capabilities.find((c) => c.name === "EnableMongo") && (
|
{isCapabilityEnabled("EnableMongo") && (
|
||||||
<Stack className="panelGroupSpacing">
|
<Stack className="panelGroupSpacing">
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<span className="mandatoryStar">* </span>
|
<span className="mandatoryStar">* </span>
|
||||||
@@ -851,7 +852,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return properties.capabilities.some(
|
return properties.capabilities?.some(
|
||||||
(capability) => capability.name === Constants.CapabilityNames.EnableStorageAnalytics
|
(capability) => capability.name === Constants.CapabilityNames.EnableStorageAnalytics
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1115,7 +1115,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||||||
className="ms-Button-flexContainer flexContainer-203"
|
className="ms-Button-flexContainer flexContainer-203"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<Component
|
<FontIcon
|
||||||
className="ms-Button-icon icon-205"
|
className="ms-Button-icon icon-205"
|
||||||
iconName="Cancel"
|
iconName="Cancel"
|
||||||
>
|
>
|
||||||
@@ -1131,10 +1131,10 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||||||
>
|
>
|
||||||
|
|
||||||
</i>
|
</i>
|
||||||
</Component>
|
</FontIcon>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomizedIconButton>
|
</CustomizedIconButton>
|
||||||
@@ -3650,7 +3650,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</DefaultButton>
|
</DefaultButton>
|
||||||
</CustomizedDefaultButton>
|
</CustomizedDefaultButton>
|
||||||
|
|||||||
@@ -1106,7 +1106,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
|||||||
className="ms-Button-flexContainer flexContainer-54"
|
className="ms-Button-flexContainer flexContainer-54"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<Component
|
<FontIcon
|
||||||
className="ms-Button-icon icon-56"
|
className="ms-Button-icon icon-56"
|
||||||
iconName="Cancel"
|
iconName="Cancel"
|
||||||
>
|
>
|
||||||
@@ -1122,10 +1122,10 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
|||||||
>
|
>
|
||||||
|
|
||||||
</i>
|
</i>
|
||||||
</Component>
|
</FontIcon>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomizedIconButton>
|
</CustomizedIconButton>
|
||||||
@@ -8213,7 +8213,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</DefaultButton>
|
</DefaultButton>
|
||||||
</CustomizedDefaultButton>
|
</CustomizedDefaultButton>
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -242,6 +243,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import AddCollectionPaneTemplate from "./AddCollectionPane.html";
|
|||||||
import AddDatabasePaneTemplate from "./AddDatabasePane.html";
|
import AddDatabasePaneTemplate from "./AddDatabasePane.html";
|
||||||
import CassandraAddCollectionPaneTemplate from "./CassandraAddCollectionPane.html";
|
import CassandraAddCollectionPaneTemplate from "./CassandraAddCollectionPane.html";
|
||||||
import GraphStylingPaneTemplate from "./GraphStylingPane.html";
|
import GraphStylingPaneTemplate from "./GraphStylingPane.html";
|
||||||
import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html";
|
|
||||||
import TableEditEntityPaneTemplate from "./Tables/TableEditEntityPane.html";
|
|
||||||
|
|
||||||
export class PaneComponent {
|
export class PaneComponent {
|
||||||
constructor(data: any) {
|
constructor(data: any) {
|
||||||
@@ -38,23 +36,6 @@ export class GraphStylingPaneComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TableAddEntityPaneComponent {
|
|
||||||
constructor() {
|
|
||||||
return {
|
|
||||||
viewModel: PaneComponent,
|
|
||||||
template: TableAddEntityPaneTemplate,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TableEditEntityPaneComponent {
|
|
||||||
constructor() {
|
|
||||||
return {
|
|
||||||
viewModel: PaneComponent,
|
|
||||||
template: TableEditEntityPaneTemplate,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class CassandraAddCollectionPaneComponent {
|
export class CassandraAddCollectionPaneComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1760,7 +1760,7 @@ exports[`Setup Notebooks Panel should render Default properly 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</DefaultButton>
|
</DefaultButton>
|
||||||
</CustomizedDefaultButton>
|
</CustomizedDefaultButton>
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -232,6 +233,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -2379,7 +2381,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
className="ms-Button-flexContainer flexContainer-154"
|
className="ms-Button-flexContainer flexContainer-154"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<Component
|
<FontIcon
|
||||||
className="ms-Button-icon icon-156"
|
className="ms-Button-icon icon-156"
|
||||||
iconName="Cancel"
|
iconName="Cancel"
|
||||||
>
|
>
|
||||||
@@ -2395,10 +2397,10 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
>
|
>
|
||||||
|
|
||||||
</i>
|
</i>
|
||||||
</Component>
|
</FontIcon>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomizedIconButton>
|
</CustomizedIconButton>
|
||||||
@@ -4837,7 +4839,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</DefaultButton>
|
</DefaultButton>
|
||||||
</CustomizedDefaultButton>
|
</CustomizedDefaultButton>
|
||||||
|
|||||||
@@ -1,225 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import _ from "underscore";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { userContext } from "../../../UserContext";
|
|
||||||
import Explorer from "../../Explorer";
|
|
||||||
import * as TableConstants from "../../Tables/Constants";
|
|
||||||
import * as Entities from "../../Tables/Entities";
|
|
||||||
import { CassandraAPIDataClient, CassandraTableKey } from "../../Tables/TableDataClient";
|
|
||||||
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
|
|
||||||
import * as Utilities from "../../Tables/Utilities";
|
|
||||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
|
||||||
import TableEntityPane from "./TableEntityPane";
|
|
||||||
|
|
||||||
export default class EditTableEntityPane extends TableEntityPane {
|
|
||||||
container: Explorer;
|
|
||||||
visible: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
public originEntity: Entities.ITableEntity;
|
|
||||||
public originalNumberOfProperties: number;
|
|
||||||
private originalDocument: any;
|
|
||||||
|
|
||||||
constructor(options: ViewModels.PaneOptions) {
|
|
||||||
super(options);
|
|
||||||
this.submitButtonText("Update Entity");
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
this.submitButtonText("Update Row");
|
|
||||||
}
|
|
||||||
this.scrollId = ko.observable<string>("editEntityScroll");
|
|
||||||
}
|
|
||||||
|
|
||||||
public submit() {
|
|
||||||
if (!this.canApply()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let entity: Entities.ITableEntity = this.updateEntity(this.displayedAttributes());
|
|
||||||
this.container.tableDataClient
|
|
||||||
.updateDocument(this.tableViewModel.queryTablesTab.collection, this.originalDocument, entity)
|
|
||||||
.then((newEntity: Entities.ITableEntity) => {
|
|
||||||
var numberOfProperties = 0;
|
|
||||||
for (var property in newEntity) {
|
|
||||||
if (
|
|
||||||
property !== TableEntityProcessor.keyProperties.attachments &&
|
|
||||||
property !== TableEntityProcessor.keyProperties.etag &&
|
|
||||||
property !== TableEntityProcessor.keyProperties.resourceId &&
|
|
||||||
property !== TableEntityProcessor.keyProperties.self &&
|
|
||||||
(userContext.apiType !== "Cassandra" || property !== TableConstants.EntityKeyNames.RowKey)
|
|
||||||
) {
|
|
||||||
numberOfProperties++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var propertiesDelta = numberOfProperties - this.originalNumberOfProperties;
|
|
||||||
|
|
||||||
return this.tableViewModel
|
|
||||||
.updateCachedEntity(newEntity)
|
|
||||||
.then(() => {
|
|
||||||
if (!this.tryInsertNewHeaders(this.tableViewModel, newEntity)) {
|
|
||||||
this.tableViewModel.redrawTableThrottled();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// Selecting updated entity
|
|
||||||
this.tableViewModel.selected.removeAll();
|
|
||||||
this.tableViewModel.selected.push(newEntity);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public open() {
|
|
||||||
this.displayedAttributes(this.constructDisplayedAttributes(this.originEntity));
|
|
||||||
if (userContext.apiType === "Tables") {
|
|
||||||
this.originalDocument = TableEntityProcessor.convertEntitiesToDocuments(
|
|
||||||
[<Entities.ITableEntityForTablesAPI>this.originEntity],
|
|
||||||
this.tableViewModel.queryTablesTab.collection
|
|
||||||
)[0]; // TODO change for Cassandra
|
|
||||||
this.originalDocument.id = ko.observable<string>(this.originalDocument.id);
|
|
||||||
} else {
|
|
||||||
this.originalDocument = this.originEntity;
|
|
||||||
}
|
|
||||||
this.updateIsActionEnabled();
|
|
||||||
super.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructDisplayedAttributes(entity: Entities.ITableEntity): EntityPropertyViewModel[] {
|
|
||||||
var displayedAttributes: EntityPropertyViewModel[] = [];
|
|
||||||
const keys = Object.keys(entity);
|
|
||||||
keys &&
|
|
||||||
keys.forEach((key: string) => {
|
|
||||||
if (
|
|
||||||
key !== TableEntityProcessor.keyProperties.attachments &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.etag &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.resourceId &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.self &&
|
|
||||||
(userContext.apiType !== "Cassandra" || key !== TableConstants.EntityKeyNames.RowKey)
|
|
||||||
) {
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
const cassandraKeys = this.tableViewModel.queryTablesTab.collection.cassandraKeys.partitionKeys
|
|
||||||
.concat(this.tableViewModel.queryTablesTab.collection.cassandraKeys.clusteringKeys)
|
|
||||||
.map((key) => key.property);
|
|
||||||
var entityAttribute: Entities.ITableEntityAttribute = entity[key];
|
|
||||||
var entityAttributeType: string = entityAttribute.$;
|
|
||||||
var displayValue: any = this.getPropertyDisplayValue(entity, key, entityAttributeType);
|
|
||||||
var removable: boolean = false;
|
|
||||||
// TODO figure out validation story for blob and Inet so we can allow adding/editing them
|
|
||||||
const nonEditableType: boolean =
|
|
||||||
entityAttributeType === TableConstants.CassandraType.Blob ||
|
|
||||||
entityAttributeType === TableConstants.CassandraType.Inet;
|
|
||||||
|
|
||||||
displayedAttributes.push(
|
|
||||||
new EntityPropertyViewModel(
|
|
||||||
this,
|
|
||||||
key,
|
|
||||||
entityAttributeType,
|
|
||||||
displayValue,
|
|
||||||
/* namePlaceholder */ undefined,
|
|
||||||
/* valuePlaceholder */ undefined,
|
|
||||||
false,
|
|
||||||
/* default valid name */ true,
|
|
||||||
/* default valid value */ true,
|
|
||||||
/* isRequired */ false,
|
|
||||||
removable,
|
|
||||||
/*value editable*/ !_.contains<string>(cassandraKeys, key) && !nonEditableType
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
var entityAttribute: Entities.ITableEntityAttribute = entity[key];
|
|
||||||
var entityAttributeType: string = entityAttribute.$;
|
|
||||||
var displayValue: any = this.getPropertyDisplayValue(entity, key, entityAttributeType);
|
|
||||||
var editable: boolean = this.isAttributeEditable(key, entityAttributeType);
|
|
||||||
// As per VSO:189935, Binary properties are read-only, we still want to be able to remove them.
|
|
||||||
var removable: boolean = editable || entityAttributeType === TableConstants.TableType.Binary;
|
|
||||||
|
|
||||||
displayedAttributes.push(
|
|
||||||
new EntityPropertyViewModel(
|
|
||||||
this,
|
|
||||||
key,
|
|
||||||
entityAttributeType,
|
|
||||||
displayValue,
|
|
||||||
/* namePlaceholder */ undefined,
|
|
||||||
/* valuePlaceholder */ undefined,
|
|
||||||
editable,
|
|
||||||
/* default valid name */ true,
|
|
||||||
/* default valid value */ true,
|
|
||||||
/* isRequired */ false,
|
|
||||||
removable
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
(<CassandraAPIDataClient>this.container.tableDataClient)
|
|
||||||
.getTableSchema(this.tableViewModel.queryTablesTab.collection)
|
|
||||||
.then((properties: CassandraTableKey[]) => {
|
|
||||||
properties &&
|
|
||||||
properties.forEach((property) => {
|
|
||||||
if (!_.contains(keys, property.property)) {
|
|
||||||
this.insertAttribute(property.property, property.type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return displayedAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateEntity(displayedAttributes: EntityPropertyViewModel[]): Entities.ITableEntity {
|
|
||||||
var updatedEntity: any = {};
|
|
||||||
displayedAttributes &&
|
|
||||||
displayedAttributes.forEach((attribute: EntityPropertyViewModel) => {
|
|
||||||
if (attribute.name() && (userContext.apiType !== "Cassandra" || attribute.value() !== "")) {
|
|
||||||
var value = attribute.getPropertyValue();
|
|
||||||
var type = attribute.type();
|
|
||||||
if (type === TableConstants.TableType.Int64) {
|
|
||||||
value = Utilities.padLongWithZeros(value);
|
|
||||||
}
|
|
||||||
updatedEntity[attribute.name()] = {
|
|
||||||
_: value,
|
|
||||||
$: type,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return updatedEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isAttributeEditable(attributeName: string, entityAttributeType: string) {
|
|
||||||
return !(
|
|
||||||
attributeName === TableConstants.EntityKeyNames.PartitionKey ||
|
|
||||||
attributeName === TableConstants.EntityKeyNames.RowKey ||
|
|
||||||
attributeName === TableConstants.EntityKeyNames.Timestamp ||
|
|
||||||
// As per VSO:189935, Making Binary properties read-only in Edit Entity dialog until we have a full story for it.
|
|
||||||
entityAttributeType === TableConstants.TableType.Binary
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPropertyDisplayValue(entity: Entities.ITableEntity, name: string, type: string): any {
|
|
||||||
var attribute: Entities.ITableEntityAttribute = entity[name];
|
|
||||||
var displayValue: any = attribute._;
|
|
||||||
var isBinary: boolean = type === TableConstants.TableType.Binary;
|
|
||||||
|
|
||||||
// Showing the value in base64 for binary properties since that is what the Azure Storage Client Library expects.
|
|
||||||
// This means that, even if the Azure Storage API returns a byte[] of binary content, it needs that same array
|
|
||||||
// *base64 - encoded * as the value for the updated property or the whole update operation will fail.
|
|
||||||
if (isBinary && displayValue && $.isArray(displayValue.data)) {
|
|
||||||
var bytes: number[] = displayValue.data;
|
|
||||||
displayValue = this.getBase64DisplayValue(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return displayValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getBase64DisplayValue(bytes: number[]): string {
|
|
||||||
var displayValue: string = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var chars: string[] = bytes.map((byte: number) => String.fromCharCode(byte));
|
|
||||||
var toEncode: string = chars.join("");
|
|
||||||
displayValue = window.btoa(toEncode);
|
|
||||||
} catch (error) {
|
|
||||||
// Error
|
|
||||||
}
|
|
||||||
|
|
||||||
return displayValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
|
|
||||||
import * as DateTimeUtilities from "../../Tables/QueryBuilder/DateTimeUtilities";
|
|
||||||
import * as EntityPropertyNameValidator from "./Validators/EntityPropertyNameValidator";
|
|
||||||
import EntityPropertyValueValidator from "./Validators/EntityPropertyValueValidator";
|
|
||||||
import * as Constants from "../../Tables/Constants";
|
|
||||||
import * as Utilities from "../../Tables/Utilities";
|
|
||||||
import TableEntityPane from "./TableEntityPane";
|
|
||||||
|
|
||||||
export interface IValidationResult {
|
|
||||||
isInvalid: boolean;
|
|
||||||
help: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IActionEnabledDialog {
|
|
||||||
updateIsActionEnabled: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* View model for an entity proprety
|
|
||||||
*/
|
|
||||||
export default class EntityPropertyViewModel {
|
|
||||||
/* Constants */
|
|
||||||
public static noTooltip = "";
|
|
||||||
// Maximum number of custom properties, see Azure Service Data Model
|
|
||||||
// At https://msdn.microsoft.com/library/azure/dd179338.aspx
|
|
||||||
public static maximumNumberOfProperties = 252;
|
|
||||||
|
|
||||||
// Labels
|
|
||||||
public closeButtonLabel: string = "Close"; // localize
|
|
||||||
|
|
||||||
/* Observables */
|
|
||||||
public name: ko.Observable<string>;
|
|
||||||
public type: ko.Observable<string>;
|
|
||||||
public value: ko.Observable<any>;
|
|
||||||
public inputType: ko.Computed<string>;
|
|
||||||
|
|
||||||
public nameTooltip: ko.Observable<string>;
|
|
||||||
public isInvalidName: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
public valueTooltip: ko.Observable<string>;
|
|
||||||
public isInvalidValue: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
public namePlaceholder: ko.Observable<string>;
|
|
||||||
public valuePlaceholder: ko.Observable<string>;
|
|
||||||
|
|
||||||
public hasFocus: ko.Observable<boolean>;
|
|
||||||
public valueHasFocus: ko.Observable<boolean>;
|
|
||||||
public isDateType: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
public editable: boolean; // If a property's name or type is editable, these two are always the same regarding editability.
|
|
||||||
public valueEditable: boolean; // If a property's value is editable, could be different from name or type.
|
|
||||||
public removable: boolean; // If a property is removable, usually, PartitionKey, RowKey and TimeStamp (if applicable) are not removable.
|
|
||||||
public isRequired: boolean; // If a property's value is required, used to differentiate the place holder label.
|
|
||||||
public ignoreEmptyValue: boolean;
|
|
||||||
|
|
||||||
/* Members */
|
|
||||||
private tableEntityPane: TableEntityPane;
|
|
||||||
private _validator: EntityPropertyValueValidator;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
tableEntityPane: TableEntityPane,
|
|
||||||
name: string,
|
|
||||||
type: string,
|
|
||||||
value: any,
|
|
||||||
namePlaceholder: string = "",
|
|
||||||
valuePlaceholder: string = "",
|
|
||||||
editable: boolean = false,
|
|
||||||
defaultValidName: boolean = true,
|
|
||||||
defaultValidValue: boolean = false,
|
|
||||||
isRequired: boolean = false,
|
|
||||||
removable: boolean = editable,
|
|
||||||
valueEditable: boolean = editable,
|
|
||||||
ignoreEmptyValue: boolean = false
|
|
||||||
) {
|
|
||||||
this.name = ko.observable<string>(name);
|
|
||||||
this.type = ko.observable<string>(type);
|
|
||||||
this.isDateType = ko.pureComputed<boolean>(() => this.type() === Constants.TableType.DateTime);
|
|
||||||
if (this.isDateType()) {
|
|
||||||
value = value ? DateTimeUtilities.getLocalDateTime(value) : value;
|
|
||||||
}
|
|
||||||
this.value = ko.observable(value);
|
|
||||||
this.inputType = ko.pureComputed<string>(() => {
|
|
||||||
if (!this.valueHasFocus() && !this.value() && this.isDateType()) {
|
|
||||||
return Constants.InputType.Text;
|
|
||||||
}
|
|
||||||
return Utilities.getInputTypeFromDisplayedName(this.type());
|
|
||||||
});
|
|
||||||
|
|
||||||
this.namePlaceholder = ko.observable<string>(namePlaceholder);
|
|
||||||
this.valuePlaceholder = ko.observable<string>(valuePlaceholder);
|
|
||||||
|
|
||||||
this.editable = editable;
|
|
||||||
this.isRequired = isRequired;
|
|
||||||
this.removable = removable;
|
|
||||||
this.valueEditable = valueEditable;
|
|
||||||
|
|
||||||
this._validator = new EntityPropertyValueValidator(isRequired);
|
|
||||||
|
|
||||||
this.tableEntityPane = tableEntityPane;
|
|
||||||
|
|
||||||
this.nameTooltip = ko.observable<string>(EntityPropertyViewModel.noTooltip);
|
|
||||||
this.isInvalidName = ko.observable<boolean>(!defaultValidName);
|
|
||||||
this.name.subscribe((name: string) => this.validateName(name));
|
|
||||||
if (!defaultValidName) {
|
|
||||||
this.validateName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.valueTooltip = ko.observable<string>(EntityPropertyViewModel.noTooltip);
|
|
||||||
this.isInvalidValue = ko.observable<boolean>(!defaultValidValue);
|
|
||||||
this.value.subscribe((value: string) => this.validateValue(value, this.type()));
|
|
||||||
if (!defaultValidValue) {
|
|
||||||
this.validateValue(value, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.type.subscribe((type: string) => this.validateValue(this.value(), type));
|
|
||||||
|
|
||||||
this.hasFocus = ko.observable<boolean>(false);
|
|
||||||
this.valueHasFocus = ko.observable<boolean>(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the Javascript value of the entity property based on its EDM type.
|
|
||||||
*/
|
|
||||||
public getPropertyValue(): any {
|
|
||||||
var value: string = this.value();
|
|
||||||
if (this.type() === Constants.TableType.DateTime) {
|
|
||||||
value = DateTimeUtilities.getUTCDateTime(value);
|
|
||||||
}
|
|
||||||
return this._validator.parseValue(value, this.type());
|
|
||||||
}
|
|
||||||
|
|
||||||
private validateName(name: string): void {
|
|
||||||
var result: IValidationResult = this.isInvalidNameInput(name);
|
|
||||||
|
|
||||||
this.isInvalidName(result.isInvalid);
|
|
||||||
this.nameTooltip(result.help);
|
|
||||||
this.namePlaceholder(result.help);
|
|
||||||
this.tableEntityPane.updateIsActionEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
private validateValue(value: string, type: string): void {
|
|
||||||
var result: IValidationResult = this.isInvalidValueInput(value, type);
|
|
||||||
if (!result) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isInvalidValue(result.isInvalid);
|
|
||||||
this.valueTooltip(result.help);
|
|
||||||
this.valuePlaceholder(result.help);
|
|
||||||
this.tableEntityPane.updateIsActionEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
private isInvalidNameInput(name: string): IValidationResult {
|
|
||||||
return EntityPropertyNameValidator.validate(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private isInvalidValueInput(value: string, type: string): IValidationResult {
|
|
||||||
if (this.ignoreEmptyValue && this.value() === "") {
|
|
||||||
return { isInvalid: false, help: "" };
|
|
||||||
}
|
|
||||||
return this._validator.validate(value, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
<div data-bind="visible: visible">
|
|
||||||
<div
|
|
||||||
class="contextual-pane-out"
|
|
||||||
data-bind="
|
|
||||||
click: cancel,
|
|
||||||
clickBubble: false"
|
|
||||||
></div>
|
|
||||||
<div class="contextual-pane" style="width: 700px" id="addtableentitypane">
|
|
||||||
<!-- Add table entity form - Start -->
|
|
||||||
<div
|
|
||||||
class="contextual-pane-in"
|
|
||||||
data-bind="
|
|
||||||
visible: !isEditing()"
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
class="paneContentContainer"
|
|
||||||
data-bind="
|
|
||||||
submit: submit"
|
|
||||||
>
|
|
||||||
<!-- Add table entity header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
|
||||||
<div
|
|
||||||
id="closeAddEntityPane"
|
|
||||||
class="closeImg"
|
|
||||||
role="button"
|
|
||||||
aria-label="Close pane"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
click: cancel, event: { keydown: onCloseKeyPress }"
|
|
||||||
>
|
|
||||||
<img src="/images/close-black.svg" title="Close" alt="Close" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Add table entity header - End -->
|
|
||||||
<div class="tableParamContent paneContentContainer">
|
|
||||||
<div class="entity-table">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell entity-table-property-header" data-bind="text: attributeNameLabel"></div>
|
|
||||||
<div class="entity-table-cell entity-table-type-header" data-bind="text: dataTypeLabel"></div>
|
|
||||||
<div class="entity-table-cell entity-table-value-header" data-bind="text: attributeValueLabel"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table-scroll-box" id="addEntityScroll">
|
|
||||||
<div class="entity-table" data-bind="foreach: displayedAttributes">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell entity-table-property-column">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="entity-table-field entity-table-property-column"
|
|
||||||
required
|
|
||||||
data-bind="
|
|
||||||
textInput: name,
|
|
||||||
attr: { title: nameTooltip, placeholder: namePlaceholder, 'aria-label': 'property name' },
|
|
||||||
css: { 'invalid-field': isInvalidName },
|
|
||||||
readOnly: !editable,
|
|
||||||
hasFocus: hasFocus"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table-cell entity-table-type-column">
|
|
||||||
<select
|
|
||||||
class="entity-table-field"
|
|
||||||
data-bind="
|
|
||||||
options: $parent.edmTypes,
|
|
||||||
optionsAfterRender: $parent.setOptionDisable,
|
|
||||||
value: type,
|
|
||||||
attr: { 'aria-label': 'type' },
|
|
||||||
enable: editable,
|
|
||||||
readOnly: !editable"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
<!-- ko ifnot: isDateType -->
|
|
||||||
<div class="entity-table-cell entity-table-value-column">
|
|
||||||
<input
|
|
||||||
class="entity-table-field"
|
|
||||||
id="addTableEntityValue"
|
|
||||||
step="1"
|
|
||||||
data-bind="
|
|
||||||
textInput: value,
|
|
||||||
attr: { title: valueTooltip, placeholder: valuePlaceholder, type: inputType, 'aria-label': 'value' },
|
|
||||||
css: { 'invalid-field': isInvalidValue },
|
|
||||||
readOnly: !valueEditable"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- ko if: isDateType -->
|
|
||||||
<div class="entity-table-cell entity-table-value-column">
|
|
||||||
<input
|
|
||||||
class="entity-table-field"
|
|
||||||
step="1"
|
|
||||||
data-bind="
|
|
||||||
value: value,
|
|
||||||
attr: { title: valueTooltip, placeholder: valuePlaceholder, type: inputType },
|
|
||||||
css: { 'invalid-field': isInvalidValue },
|
|
||||||
readOnly: !valueEditable,
|
|
||||||
hasFocus: valueHasFocus"
|
|
||||||
autocomplete="off"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<div class="entity-table-cell entity-table-action-column" data-bind="if: removable || valueEditable">
|
|
||||||
<span
|
|
||||||
class="entity-Edit-Cancel"
|
|
||||||
title="Edit property"
|
|
||||||
role="button"
|
|
||||||
aria-label="Edit property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.editAttribute.bind($data, $index()), visible: valueEditable, event: { keydown: $parent.onEditPropertyKeyDown.bind($data, $index()) }"
|
|
||||||
>
|
|
||||||
<img class="entity-Editor-Cancel-Img" src="/images/Edit_entity.svg" alt="Edit" />
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="entity-Edit-Cancel"
|
|
||||||
title="Delete property"
|
|
||||||
role="button"
|
|
||||||
aria-label="Delete property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.removeAttribute.bind($data, $index()), visible: removable, event: { keydown: $parent.onDeletePropertyKeyDown.bind($data, $index()) }"
|
|
||||||
>
|
|
||||||
<img class="entity-Editor-Cancel-Img" src="/images/delete.svg" alt="Cancel" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table addProperty">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell">
|
|
||||||
<span
|
|
||||||
class="commandButton"
|
|
||||||
id="addProperty"
|
|
||||||
role="button"
|
|
||||||
aria-label="Add property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="visible: canAdd, click: insertAttribute, event: { keydown: onAddPropertyKeyDown }"
|
|
||||||
autofocus
|
|
||||||
>
|
|
||||||
<img class="addPropertyImg" src="/images/Add-property.svg" alt="Insert attribute" />
|
|
||||||
<span data-bind="text: addButtonLabel"> </span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="paneFooter">
|
|
||||||
<div class="leftpanel-okbut">
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
class="btncreatecoll1"
|
|
||||||
data-bind="value: submitButtonText, event: { keydown: onSubmitKeyPress }"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<!-- Add table entity form - End -->
|
|
||||||
<!-- Add table entity editor - Start -->
|
|
||||||
<div id="editor-panel-addEntity" data-bind="visible: isEditing()" style="display: none">
|
|
||||||
<div data-bind="with: editingProperty()">
|
|
||||||
<!-- Add table entity editor header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span
|
|
||||||
class="backBtn"
|
|
||||||
aria-label="Back"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
click: $parent.finishEditingAttribute, event: { keydown: $parent.onBackButtonKeyDown }"
|
|
||||||
>
|
|
||||||
<img src="/images/RevertBack.svg" alt="BackIcon" />
|
|
||||||
</span>
|
|
||||||
<span class="edit-value-text" data-bind="text: name"></span>
|
|
||||||
</div>
|
|
||||||
<!-- Add table entity editor header - End -->
|
|
||||||
<div class="seconddivbg paddingspan2">
|
|
||||||
<textarea
|
|
||||||
class="entity-editor-expanded"
|
|
||||||
id="textAreaEditProperty"
|
|
||||||
tabindex="0"
|
|
||||||
rows="21"
|
|
||||||
data-bind="value: value, attr: { 'aria-label': name }"
|
|
||||||
style="width: 95%"
|
|
||||||
autofocus
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Add table entity editor - End -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
<div data-bind="visible: visible">
|
|
||||||
<div
|
|
||||||
class="contextual-pane-out"
|
|
||||||
data-bind="
|
|
||||||
click: cancel,
|
|
||||||
clickBubble: false"
|
|
||||||
></div>
|
|
||||||
<div class="contextual-pane" style="width: 700px" id="edittableentitypane">
|
|
||||||
<!-- Edit table entity form - Start -->
|
|
||||||
<div
|
|
||||||
class="contextual-pane-in"
|
|
||||||
data-bind="
|
|
||||||
visible: !isEditing()"
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
class="paneContentContainer"
|
|
||||||
data-bind="
|
|
||||||
submit: submit"
|
|
||||||
>
|
|
||||||
<!-- Edit table entity header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
|
||||||
<div
|
|
||||||
class="closeImg"
|
|
||||||
role="button"
|
|
||||||
aria-label="Close pane"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
click: cancel, event: { keydown: onCloseKeyPress }"
|
|
||||||
>
|
|
||||||
<img src="/images/close-black.svg" title="Close" alt="Close" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Edit table entity header - End -->
|
|
||||||
<div class="tableParamContent paneContentContainer">
|
|
||||||
<div class="entity-table">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell entity-table-property-header" data-bind="text: attributeNameLabel"></div>
|
|
||||||
<div class="entity-table-cell entity-table-type-header" data-bind="text: dataTypeLabel"></div>
|
|
||||||
<div class="entity-table-cell entity-table-value-header" data-bind="text: attributeValueLabel"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table-scroll-box" id="editEntityScroll">
|
|
||||||
<div class="entity-table" data-bind="foreach: displayedAttributes">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell entity-table-property-column">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="entity-table-field entity-table-property-column"
|
|
||||||
required
|
|
||||||
data-bind="
|
|
||||||
textInput: name,
|
|
||||||
attr: { title: nameTooltip, placeholder: namePlaceholder, 'aria-label': 'property name' },
|
|
||||||
css: { 'invalid-field': isInvalidName },
|
|
||||||
readOnly: !editable,
|
|
||||||
hasFocus: hasFocus"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table-cell entity-table-type-column">
|
|
||||||
<select
|
|
||||||
class="entity-table-field"
|
|
||||||
data-bind="
|
|
||||||
options: $parent.edmTypes,
|
|
||||||
optionsAfterRender: $parent.setOptionDisable,
|
|
||||||
value: type,
|
|
||||||
attr: { 'aria-label': 'type' },
|
|
||||||
enable: editable,
|
|
||||||
readOnly: !editable"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
<!-- ko ifnot: isDateType -->
|
|
||||||
<div class="entity-table-cell entity-table-value-column">
|
|
||||||
<input
|
|
||||||
class="entity-table-field"
|
|
||||||
step="1"
|
|
||||||
data-bind="
|
|
||||||
textInput: value,
|
|
||||||
attr: { title: valueTooltip, placeholder: valuePlaceholder, type: inputType, 'aria-label': 'value' },
|
|
||||||
css: { 'invalid-field': isInvalidValue },
|
|
||||||
readOnly: !valueEditable"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- ko if: isDateType -->
|
|
||||||
<div class="entity-table-cell entity-table-value-column">
|
|
||||||
<input
|
|
||||||
class="entity-table-field"
|
|
||||||
step="1"
|
|
||||||
data-bind="
|
|
||||||
value: value,
|
|
||||||
attr: { title: valueTooltip, placeholder: valuePlaceholder, type: inputType, 'aria-label': 'value' },
|
|
||||||
css: { 'invalid-field': isInvalidValue },
|
|
||||||
readOnly: !valueEditable,
|
|
||||||
hasFocus: valueHasFocus"
|
|
||||||
autocomplete="off"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<div class="entity-table-cell entity-table-action-column" data-bind="if: removable || valueEditable">
|
|
||||||
<span
|
|
||||||
class="entity-Edit-Cancel"
|
|
||||||
role="button"
|
|
||||||
aria-label="Edit property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.editAttribute.bind($data, $index()), visible: valueEditable, event: { keydown: $parent.onEditPropertyKeyDown.bind($data, $index()) }"
|
|
||||||
title="Edit property"
|
|
||||||
>
|
|
||||||
<img class="entity-Editor-Cancel-Img" src="/images/Edit_entity.svg" alt="Edit attribute" />
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="entity-Edit-Cancel"
|
|
||||||
role="button"
|
|
||||||
aria-label="Delete property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.removeAttribute.bind($data, $index()), visible: removable, event: { keydown: $parent.onDeletePropertyKeyDown.bind($data, $index()) }"
|
|
||||||
title="Delete property"
|
|
||||||
>
|
|
||||||
<img class="entity-Editor-Cancel-Img" src="/images/delete.svg" alt="Remove attribute" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table addProperty">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell">
|
|
||||||
<span
|
|
||||||
class="commandButton"
|
|
||||||
role="button"
|
|
||||||
aria-label="Add property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="visible: canAdd, click: insertAttribute, event: { keydown: onAddPropertyKeyDown }"
|
|
||||||
autofocus
|
|
||||||
>
|
|
||||||
<img class="addPropertyImg" src="/images/Add-property.svg" alt="Add attribute" />
|
|
||||||
<span data-bind="text: addButtonLabel"> </span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="paneFooter">
|
|
||||||
<div class="leftpanel-okbut">
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
value="Update Entity"
|
|
||||||
class="btncreatecoll1"
|
|
||||||
data-bind="value: submitButtonText, event: { keydown: onSubmitKeyPress }"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<!-- Edit table entity form - End -->
|
|
||||||
<!-- Edit table entity editor - Start -->
|
|
||||||
<div id="editor-panel-editEntity" data-bind="visible: isEditing()" style="display: none">
|
|
||||||
<div data-bind="with: editingProperty()">
|
|
||||||
<!-- Edit table entity editor header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span
|
|
||||||
class="backBtn"
|
|
||||||
aria-label="Back"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
click: $parent.finishEditingAttribute, event: { keydown: $parent.onBackButtonKeyDown }"
|
|
||||||
>
|
|
||||||
<img src="/images/RevertBack.svg" alt="BackIcon" />
|
|
||||||
</span>
|
|
||||||
<span class="edit-value-text" data-bind="text: name"></span>
|
|
||||||
</div>
|
|
||||||
<!-- Edit table entity editor header - End -->
|
|
||||||
<div class="seconddivbg paddingspan2">
|
|
||||||
<textarea
|
|
||||||
class="entity-editor-expanded"
|
|
||||||
id="editor-area"
|
|
||||||
tabindex="0"
|
|
||||||
rows="21"
|
|
||||||
data-bind="value: value, attr: { 'aria-label': name }"
|
|
||||||
autofocus
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Edit table entity editor - End -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,279 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import _ from "underscore";
|
|
||||||
import { KeyCodes } from "../../../Common/Constants";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { userContext } from "../../../UserContext";
|
|
||||||
import * as TableConstants from "../../Tables/Constants";
|
|
||||||
import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities";
|
|
||||||
import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel";
|
|
||||||
import * as Entities from "../../Tables/Entities";
|
|
||||||
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
|
|
||||||
import * as Utilities from "../../Tables/Utilities";
|
|
||||||
import { ContextualPaneBase } from "../ContextualPaneBase";
|
|
||||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
|
||||||
|
|
||||||
// Class with variables and functions that are common to both adding and editing entities
|
|
||||||
export default abstract class TableEntityPane extends ContextualPaneBase {
|
|
||||||
protected static requiredFieldsForTablesAPI: string[] = [
|
|
||||||
TableConstants.EntityKeyNames.PartitionKey,
|
|
||||||
TableConstants.EntityKeyNames.RowKey,
|
|
||||||
];
|
|
||||||
|
|
||||||
/* Labels */
|
|
||||||
public attributeNameLabel = "Property Name"; // localize
|
|
||||||
public dataTypeLabel = "Type"; // localize
|
|
||||||
public attributeValueLabel = "Value"; // localize
|
|
||||||
|
|
||||||
/* Controls */
|
|
||||||
public removeButtonLabel = "Remove"; // localize
|
|
||||||
public editButtonLabel = "Edit"; // localize
|
|
||||||
public addButtonLabel = "Add Property"; // localize
|
|
||||||
|
|
||||||
public edmTypes: ko.ObservableArray<string> = ko.observableArray([
|
|
||||||
TableConstants.TableType.String,
|
|
||||||
TableConstants.TableType.Boolean,
|
|
||||||
TableConstants.TableType.Binary,
|
|
||||||
TableConstants.TableType.DateTime,
|
|
||||||
TableConstants.TableType.Double,
|
|
||||||
TableConstants.TableType.Guid,
|
|
||||||
TableConstants.TableType.Int32,
|
|
||||||
TableConstants.TableType.Int64,
|
|
||||||
]);
|
|
||||||
|
|
||||||
public canAdd: ko.Computed<boolean>;
|
|
||||||
public canApply: ko.Observable<boolean>;
|
|
||||||
public displayedAttributes = ko.observableArray<EntityPropertyViewModel>();
|
|
||||||
public editingProperty = ko.observable<EntityPropertyViewModel>();
|
|
||||||
public isEditing = ko.observable<boolean>(false);
|
|
||||||
public submitButtonText = ko.observable<string>();
|
|
||||||
|
|
||||||
public tableViewModel: TableEntityListViewModel;
|
|
||||||
|
|
||||||
protected scrollId: ko.Observable<string>;
|
|
||||||
|
|
||||||
constructor(options: ViewModels.PaneOptions) {
|
|
||||||
super(options);
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
this.edmTypes([
|
|
||||||
TableConstants.CassandraType.Text,
|
|
||||||
TableConstants.CassandraType.Ascii,
|
|
||||||
TableConstants.CassandraType.Bigint,
|
|
||||||
TableConstants.CassandraType.Blob,
|
|
||||||
TableConstants.CassandraType.Boolean,
|
|
||||||
TableConstants.CassandraType.Decimal,
|
|
||||||
TableConstants.CassandraType.Double,
|
|
||||||
TableConstants.CassandraType.Float,
|
|
||||||
TableConstants.CassandraType.Int,
|
|
||||||
TableConstants.CassandraType.Uuid,
|
|
||||||
TableConstants.CassandraType.Varchar,
|
|
||||||
TableConstants.CassandraType.Varint,
|
|
||||||
TableConstants.CassandraType.Inet,
|
|
||||||
TableConstants.CassandraType.Smallint,
|
|
||||||
TableConstants.CassandraType.Tinyint,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.canAdd = ko.computed<boolean>(() => {
|
|
||||||
// Cassandra can't add since the schema can't be changed once created
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Adding '2' to the maximum to take into account PartitionKey and RowKey
|
|
||||||
return this.displayedAttributes().length < EntityPropertyViewModel.maximumNumberOfProperties + 2;
|
|
||||||
});
|
|
||||||
this.canApply = ko.observable<boolean>(true);
|
|
||||||
this.editingProperty(this.displayedAttributes()[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeAttribute = (index: number, data: any): void => {
|
|
||||||
this.displayedAttributes.splice(index, 1);
|
|
||||||
this.updateIsActionEnabled();
|
|
||||||
document.getElementById("addProperty").focus();
|
|
||||||
};
|
|
||||||
|
|
||||||
public editAttribute = (index: number, data: EntityPropertyViewModel): void => {
|
|
||||||
this.editingProperty(data);
|
|
||||||
this.isEditing(true);
|
|
||||||
document.getElementById("textAreaEditProperty").focus();
|
|
||||||
};
|
|
||||||
|
|
||||||
public finishEditingAttribute = (): void => {
|
|
||||||
this.isEditing(false);
|
|
||||||
this.editingProperty(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
public onKeyUp = (data: any, event: KeyboardEvent): boolean => {
|
|
||||||
var handled: boolean = Utilities.onEsc(event, ($sourceElement: JQuery) => {
|
|
||||||
this.finishEditingAttribute();
|
|
||||||
});
|
|
||||||
|
|
||||||
return !handled;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onAddPropertyKeyDown = (source: any, event: KeyboardEvent): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.insertAttribute();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onEditPropertyKeyDown = (
|
|
||||||
index: number,
|
|
||||||
data: EntityPropertyViewModel,
|
|
||||||
event: KeyboardEvent,
|
|
||||||
source: any
|
|
||||||
): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.editAttribute(index, data);
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onDeletePropertyKeyDown = (
|
|
||||||
index: number,
|
|
||||||
data: EntityPropertyViewModel,
|
|
||||||
event: KeyboardEvent,
|
|
||||||
source: any
|
|
||||||
): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.removeAttribute(index, data);
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onBackButtonKeyDown = (source: any, event: KeyboardEvent): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.finishEditingAttribute();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public insertAttribute = (name?: string, type?: string): void => {
|
|
||||||
let entityProperty: EntityPropertyViewModel;
|
|
||||||
if (!!name && !!type && userContext.apiType === "Cassandra") {
|
|
||||||
// TODO figure out validation story for blob and Inet so we can allow adding/editing them
|
|
||||||
const nonEditableType: boolean =
|
|
||||||
type === TableConstants.CassandraType.Blob || type === TableConstants.CassandraType.Inet;
|
|
||||||
entityProperty = new EntityPropertyViewModel(
|
|
||||||
this,
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
"", // default to empty string
|
|
||||||
/* namePlaceholder */ undefined,
|
|
||||||
/* valuePlaceholder */ undefined,
|
|
||||||
/* editable */ false,
|
|
||||||
/* default valid name */ false,
|
|
||||||
/* default valid value */ true,
|
|
||||||
/* isRequired */ false,
|
|
||||||
/* removable */ false,
|
|
||||||
/*value editable*/ !nonEditableType
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
entityProperty = new EntityPropertyViewModel(
|
|
||||||
this,
|
|
||||||
"",
|
|
||||||
this.edmTypes()[0], // default to the first Edm type: 'string'
|
|
||||||
"", // default to empty string
|
|
||||||
/* namePlaceholder */ undefined,
|
|
||||||
/* valuePlaceholder */ undefined,
|
|
||||||
/* editable */ true,
|
|
||||||
/* default valid name */ false,
|
|
||||||
/* default valid value */ true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.displayedAttributes.push(entityProperty);
|
|
||||||
this.updateIsActionEnabled();
|
|
||||||
this.scrollToBottom();
|
|
||||||
|
|
||||||
entityProperty.hasFocus(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
public updateIsActionEnabled(needRequiredFields: boolean = true): void {
|
|
||||||
var properties: EntityPropertyViewModel[] = this.displayedAttributes() || [];
|
|
||||||
var disable: boolean = _.some(properties, (property: EntityPropertyViewModel) => {
|
|
||||||
return property.isInvalidName() || property.isInvalidValue();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.canApply(!disable);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected entityFromAttributes(displayedAttributes: EntityPropertyViewModel[]): Entities.ITableEntity {
|
|
||||||
var entity: any = {};
|
|
||||||
|
|
||||||
displayedAttributes &&
|
|
||||||
displayedAttributes.forEach((attribute: EntityPropertyViewModel) => {
|
|
||||||
if (attribute.name() && (attribute.value() !== "" || attribute.isRequired)) {
|
|
||||||
var value = attribute.getPropertyValue();
|
|
||||||
var type = attribute.type();
|
|
||||||
if (type === TableConstants.TableType.Int64) {
|
|
||||||
value = Utilities.padLongWithZeros(value);
|
|
||||||
}
|
|
||||||
entity[attribute.name()] = {
|
|
||||||
_: value,
|
|
||||||
$: type,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing Binary from Add Entity dialog until we have a full story for it.
|
|
||||||
protected setOptionDisable(option: Node, value: string): void {
|
|
||||||
ko.applyBindingsToNode(option, { disable: value === TableConstants.TableType.Binary }, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the updated entity to see if there are any new attributes that old headers don't have.
|
|
||||||
* In this case, add these attributes names as new headers.
|
|
||||||
* Remarks: adding new headers will automatically trigger table redraw.
|
|
||||||
*/
|
|
||||||
protected tryInsertNewHeaders(viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean {
|
|
||||||
var newHeaders: string[] = [];
|
|
||||||
const keys = Object.keys(newEntity);
|
|
||||||
keys &&
|
|
||||||
keys.forEach((key: string) => {
|
|
||||||
if (
|
|
||||||
!_.contains(viewModel.headers, key) &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.attachments &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.etag &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.resourceId &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.self &&
|
|
||||||
(userContext.apiType !== "Cassandra" || key !== TableConstants.EntityKeyNames.RowKey)
|
|
||||||
) {
|
|
||||||
newHeaders.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var newHeadersInserted: boolean = false;
|
|
||||||
if (newHeaders.length) {
|
|
||||||
if (!DataTableUtilities.checkForDefaultHeader(viewModel.headers)) {
|
|
||||||
newHeaders = viewModel.headers.concat(newHeaders);
|
|
||||||
}
|
|
||||||
viewModel.updateHeaders(newHeaders, /* notifyColumnChanges */ true, /* enablePrompt */ false);
|
|
||||||
newHeadersInserted = true;
|
|
||||||
}
|
|
||||||
return newHeadersInserted;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected scrollToBottom(): void {
|
|
||||||
var scrollBox = document.getElementById(this.scrollId());
|
|
||||||
var isScrolledToBottom = scrollBox.scrollHeight - scrollBox.clientHeight <= scrollBox.scrollHeight + 1;
|
|
||||||
if (isScrolledToBottom) {
|
|
||||||
scrollBox.scrollTop = scrollBox.scrollHeight - scrollBox.clientHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1110,7 +1110,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
|
|||||||
className="ms-Button-flexContainer flexContainer-54"
|
className="ms-Button-flexContainer flexContainer-54"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<Component
|
<FontIcon
|
||||||
className="ms-Button-icon icon-56"
|
className="ms-Button-icon icon-56"
|
||||||
iconName="Cancel"
|
iconName="Cancel"
|
||||||
>
|
>
|
||||||
@@ -1126,10 +1126,10 @@ exports[`Table query select Panel should render Default properly 1`] = `
|
|||||||
>
|
>
|
||||||
|
|
||||||
</i>
|
</i>
|
||||||
</Component>
|
</FontIcon>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomizedIconButton>
|
</CustomizedIconButton>
|
||||||
@@ -4180,7 +4180,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</DefaultButton>
|
</DefaultButton>
|
||||||
</CustomizedDefaultButton>
|
</CustomizedDefaultButton>
|
||||||
|
|||||||
@@ -2663,7 +2663,7 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
|
|||||||
className="ms-Button-flexContainer flexContainer-73"
|
className="ms-Button-flexContainer flexContainer-73"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<Component
|
<FontIcon
|
||||||
className="ms-Button-icon icon-75"
|
className="ms-Button-icon icon-75"
|
||||||
iconName="Cancel"
|
iconName="Cancel"
|
||||||
>
|
>
|
||||||
@@ -2679,10 +2679,10 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
|
|||||||
>
|
>
|
||||||
|
|
||||||
</i>
|
</i>
|
||||||
</Component>
|
</FontIcon>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomizedIconButton>
|
</CustomizedIconButton>
|
||||||
|
|||||||
@@ -2664,7 +2664,7 @@ exports[`Excute Edit Table Entity Pane should render Default properly 1`] = `
|
|||||||
className="ms-Button-flexContainer flexContainer-73"
|
className="ms-Button-flexContainer flexContainer-73"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<Component
|
<FontIcon
|
||||||
className="ms-Button-icon icon-75"
|
className="ms-Button-icon icon-75"
|
||||||
iconName="Cancel"
|
iconName="Cancel"
|
||||||
>
|
>
|
||||||
@@ -2680,10 +2680,10 @@ exports[`Excute Edit Table Entity Pane should render Default properly 1`] = `
|
|||||||
>
|
>
|
||||||
|
|
||||||
</i>
|
</i>
|
||||||
</Component>
|
</FontIcon>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomizedIconButton>
|
</CustomizedIconButton>
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
|||||||
multiple={true}
|
multiple={true}
|
||||||
onUpload={[Function]}
|
onUpload={[Function]}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
tooltip="Select one or more JSON files to upload. Each file can contain a single JSON document or an array of JSON documents. The combined size of all files in an individual upload operation must be less than 2 MB. You can perform multiple upload operations for larger data sets."
|
tooltip="Select one or more JSON files to upload. Each file can contain a single JSON document or an array of JSON
|
||||||
|
documents. The combined size of all files in an individual upload operation must be less than 2 MB. You
|
||||||
|
can perform multiple upload operations for larger data sets."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</GenericRightPaneComponent>
|
</GenericRightPaneComponent>
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -230,6 +231,7 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
|||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
|
"isEnableMongoCapabilityEnabled": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
"isFixedStorageSelected": [Function],
|
"isFixedStorageSelected": [Function],
|
||||||
"isFreeTierAccount": [Function],
|
"isFreeTierAccount": [Function],
|
||||||
@@ -4021,7 +4023,7 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Component />
|
<FocusRects />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</DefaultButton>
|
</DefaultButton>
|
||||||
</CustomizedDefaultButton>
|
</CustomizedDefaultButton>
|
||||||
|
|||||||
@@ -183,12 +183,14 @@ export function convertEntityToNewDocument(entity: Entities.ITableEntityForTable
|
|||||||
parsedValue = DateTimeUtilities.convertJSDateToTicksWithPadding(propertyValue);
|
parsedValue = DateTimeUtilities.convertJSDateToTicksWithPadding(propertyValue);
|
||||||
break;
|
break;
|
||||||
case Constants.TableType.Boolean:
|
case Constants.TableType.Boolean:
|
||||||
parsedValue = propertyValue.toLowerCase() === "true";
|
parsedValue = propertyValue.toString().toLowerCase() === "true";
|
||||||
break;
|
break;
|
||||||
case Constants.TableType.Int32:
|
case Constants.TableType.Int32:
|
||||||
case Constants.TableType.Int64:
|
|
||||||
parsedValue = parseInt(propertyValue, 10);
|
parsedValue = parseInt(propertyValue, 10);
|
||||||
break;
|
break;
|
||||||
|
case Constants.TableType.Int64:
|
||||||
|
parsedValue = propertyValue.toString();
|
||||||
|
break;
|
||||||
case Constants.TableType.Double:
|
case Constants.TableType.Double:
|
||||||
parsedValue = parseFloat(propertyValue);
|
parsedValue = parseFloat(propertyValue);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
|
||||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
|
||||||
import {
|
|
||||||
NotebookViewerComponent,
|
|
||||||
NotebookViewerComponentProps,
|
|
||||||
} from "../Controls/NotebookViewer/NotebookViewerComponent";
|
|
||||||
import Explorer from "../Explorer";
|
|
||||||
import TabsBase from "./TabsBase";
|
|
||||||
|
|
||||||
interface NotebookViewerTabOptions extends ViewModels.TabOptions {
|
|
||||||
account: DatabaseAccount;
|
|
||||||
container: Explorer;
|
|
||||||
notebookUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notebook Viewer tab
|
|
||||||
*/
|
|
||||||
class NotebookViewerComponentAdapter implements ReactAdapter {
|
|
||||||
// parameters: true: show, false: hide
|
|
||||||
public parameters: ko.Computed<boolean>;
|
|
||||||
constructor(private notebookUrl: string) {}
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
const props: NotebookViewerComponentProps = {
|
|
||||||
notebookUrl: this.notebookUrl,
|
|
||||||
backNavigationText: undefined,
|
|
||||||
onBackClick: undefined,
|
|
||||||
onTagClick: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.parameters() ? <NotebookViewerComponent {...props} /> : <></>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class NotebookViewerTab extends TabsBase {
|
|
||||||
public readonly html = '<div style="height: 100%" data-bind="react:notebookViewerComponentAdapter"></div>';
|
|
||||||
private container: Explorer;
|
|
||||||
public notebookUrl: string;
|
|
||||||
|
|
||||||
public notebookViewerComponentAdapter: NotebookViewerComponentAdapter;
|
|
||||||
|
|
||||||
constructor(options: NotebookViewerTabOptions) {
|
|
||||||
super(options);
|
|
||||||
this.container = options.container;
|
|
||||||
this.notebookUrl = options.notebookUrl;
|
|
||||||
|
|
||||||
this.notebookViewerComponentAdapter = new NotebookViewerComponentAdapter(options.notebookUrl);
|
|
||||||
|
|
||||||
this.notebookViewerComponentAdapter.parameters = ko.computed<boolean>(() => {
|
|
||||||
if (this.isTemplateReady() && this.container.isNotebookEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getContainer(): Explorer {
|
|
||||||
return this.container;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getTabsButtons(): CommandButtonComponentProps[] {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected buildCommandBarOptions(): void {
|
|
||||||
this.updateNavbarWithTabsButtons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
src/Utils/CapabilityUtils.ts
Normal file
4
src/Utils/CapabilityUtils.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { userContext } from "../UserContext";
|
||||||
|
|
||||||
|
export const isCapabilityEnabled = (capabilityName: string): boolean =>
|
||||||
|
userContext.databaseAccount?.properties?.capabilities?.some((capability) => capability.name === capabilityName);
|
||||||
8
src/global.d.ts
vendored
8
src/global.d.ts
vendored
@@ -1,6 +1,14 @@
|
|||||||
|
import { PageWaitForSelectorOptions } from "expect-playwright";
|
||||||
import Explorer from "./Explorer/Explorer";
|
import Explorer from "./Explorer/Explorer";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
namespace jest {
|
||||||
|
interface Matchers<R> {
|
||||||
|
toHaveFocus(selector: string, options?: PageWaitForSelectorOptions): Promise<R>;
|
||||||
|
toHaveTextContent(htmlElement: string): object;
|
||||||
|
toHaveValue(value: string | string[] | number): object;
|
||||||
|
}
|
||||||
|
}
|
||||||
interface Window {
|
interface Window {
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
import { initializeIcons } from "@fluentui/react";
|
||||||
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { configure } from "enzyme";
|
import { configure } from "enzyme";
|
||||||
import "jest-canvas-mock";
|
import "jest-canvas-mock";
|
||||||
import { initializeIcons } from "@fluentui/react";
|
|
||||||
import { TextDecoder, TextEncoder } from "util";
|
import { TextDecoder, TextEncoder } from "util";
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
if (typeof window.URL.createObjectURL === "undefined") {
|
if (typeof window.URL.createObjectURL === "undefined") {
|
||||||
Object.defineProperty(window.URL, "createObjectURL", { value: () => {} });
|
Object.defineProperty(window.URL, "createObjectURL", { value: () => { } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Remove when jquery and documentdbclient SDK are removed
|
// TODO Remove when jquery and documentdbclient SDK are removed
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"allowUnreachableCode": false,
|
"allowUnreachableCode": false,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"./src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts",
|
"./src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts",
|
||||||
"./src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts",
|
"./src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts",
|
||||||
"./src/Explorer/Graph/GraphExplorerComponent/GraphData.ts",
|
"./src/Explorer/Graph/GraphExplorerComponent/GraphData.ts",
|
||||||
|
"./src/Explorer/LazyMonaco.ts",
|
||||||
"./src/Explorer/Notebook/FileSystemUtil.ts",
|
"./src/Explorer/Notebook/FileSystemUtil.ts",
|
||||||
"./src/Explorer/Notebook/NTeractUtil.ts",
|
"./src/Explorer/Notebook/NTeractUtil.ts",
|
||||||
"./src/Explorer/Notebook/NotebookComponent/actions.ts",
|
"./src/Explorer/Notebook/NotebookComponent/actions.ts",
|
||||||
@@ -54,6 +55,8 @@
|
|||||||
"./src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx",
|
"./src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx",
|
||||||
"./src/Explorer/Notebook/NotebookRenderer/decorators/CellCreator.tsx",
|
"./src/Explorer/Notebook/NotebookRenderer/decorators/CellCreator.tsx",
|
||||||
"./src/Explorer/Notebook/NotebookUtil.ts",
|
"./src/Explorer/Notebook/NotebookUtil.ts",
|
||||||
|
"./src/Explorer/OpenFullScreen.test.tsx",
|
||||||
|
"./src/Explorer/OpenFullScreen.tsx",
|
||||||
"./src/Explorer/Panes/PaneComponents.ts",
|
"./src/Explorer/Panes/PaneComponents.ts",
|
||||||
"./src/Explorer/Panes/PanelFooterComponent.tsx",
|
"./src/Explorer/Panes/PanelFooterComponent.tsx",
|
||||||
"./src/Explorer/Panes/PanelLoadingScreen.tsx",
|
"./src/Explorer/Panes/PanelLoadingScreen.tsx",
|
||||||
@@ -70,6 +73,7 @@
|
|||||||
"./src/HostedExplorerChildFrame.ts",
|
"./src/HostedExplorerChildFrame.ts",
|
||||||
"./src/Index.ts",
|
"./src/Index.ts",
|
||||||
"./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts",
|
"./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts",
|
||||||
|
"./src/Platform/Hosted/Authorization.ts",
|
||||||
"./src/Platform/Hosted/Components/SignInButton.tsx",
|
"./src/Platform/Hosted/Components/SignInButton.tsx",
|
||||||
"./src/Platform/Hosted/extractFeatures.test.ts",
|
"./src/Platform/Hosted/extractFeatures.test.ts",
|
||||||
"./src/Platform/Hosted/extractFeatures.ts",
|
"./src/Platform/Hosted/extractFeatures.ts",
|
||||||
@@ -87,6 +91,7 @@
|
|||||||
"./src/Shared/StringUtility.ts",
|
"./src/Shared/StringUtility.ts",
|
||||||
"./src/Shared/appInsights.ts",
|
"./src/Shared/appInsights.ts",
|
||||||
"./src/UserContext.ts",
|
"./src/UserContext.ts",
|
||||||
|
"./src/Utils/APITypeUtils.ts",
|
||||||
"./src/Utils/AutoPilotUtils.ts",
|
"./src/Utils/AutoPilotUtils.ts",
|
||||||
"./src/Utils/Base64Utils.test.ts",
|
"./src/Utils/Base64Utils.test.ts",
|
||||||
"./src/Utils/Base64Utils.ts",
|
"./src/Utils/Base64Utils.ts",
|
||||||
@@ -97,15 +102,19 @@
|
|||||||
"./src/Utils/MessageValidation.ts",
|
"./src/Utils/MessageValidation.ts",
|
||||||
"./src/Utils/PricingUtils.ts",
|
"./src/Utils/PricingUtils.ts",
|
||||||
"./src/Utils/StringUtils.ts",
|
"./src/Utils/StringUtils.ts",
|
||||||
|
"./src/Utils/StyleUtils.ts",
|
||||||
"./src/Utils/WindowUtils.test.ts",
|
"./src/Utils/WindowUtils.test.ts",
|
||||||
"./src/Utils/WindowUtils.ts",
|
"./src/Utils/WindowUtils.ts",
|
||||||
"./src/hooks/useDirectories.tsx",
|
"./src/hooks/useDirectories.tsx",
|
||||||
|
"./src/hooks/useFullScreenURLs.tsx",
|
||||||
|
"./src/hooks/useObservable.ts",
|
||||||
"./src/i18n.ts",
|
"./src/i18n.ts",
|
||||||
"./src/quickstart.ts",
|
"./src/quickstart.ts",
|
||||||
"./src/setupTests.ts",
|
"./src/setupTests.ts",
|
||||||
"./src/userContext.test.ts"
|
"./src/userContext.test.ts"
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
|
"src/CellOutputViewer/transforms/**/*",
|
||||||
"src/Controls/**/*",
|
"src/Controls/**/*",
|
||||||
"src/Definitions/**/*",
|
"src/Definitions/**/*",
|
||||||
"src/Explorer/Controls/ErrorDisplayComponent/**/*",
|
"src/Explorer/Controls/ErrorDisplayComponent/**/*",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
|||||||
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
|
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
|
||||||
const CreateFileWebpack = require("create-file-webpack");
|
const CreateFileWebpack = require("create-file-webpack");
|
||||||
const childProcess = require("child_process");
|
const childProcess = require("child_process");
|
||||||
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
|
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
const isCI = require("is-ci");
|
const isCI = require("is-ci");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user