mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-23 10:51:30 +00:00
Compare commits
63 Commits
eslint/fix
...
resolve_Co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9780ff3ad | ||
|
|
aff7133095 | ||
|
|
bfd4948fb9 | ||
|
|
1c54459708 | ||
|
|
df3b18d585 | ||
|
|
882f0e1554 | ||
|
|
b67b76cc87 | ||
|
|
734ee1e436 | ||
|
|
ff498b51e2 | ||
|
|
ed1ffb692f | ||
|
|
f7fa3f7c09 | ||
|
|
6ebf19c0c9 | ||
|
|
deb18e5aa1 | ||
|
|
f968f57543 | ||
|
|
6da226fedb | ||
|
|
6ca8e3c6f4 | ||
|
|
f7e7240010 | ||
|
|
acc095a482 | ||
|
|
288e6410f3 | ||
|
|
9ac3392271 | ||
|
|
9a908dde9a | ||
|
|
ddd2e63fe7 | ||
|
|
34c59b4872 | ||
|
|
7d28af4fc7 | ||
|
|
50b99ceb42 | ||
|
|
15a26d6500 | ||
|
|
a8150af269 | ||
|
|
6a9a0156a3 | ||
|
|
ead28f043f | ||
|
|
b05e5a2145 | ||
|
|
5e8aa491ba | ||
|
|
a730c08292 | ||
|
|
3dce5cd129 | ||
|
|
7c186c3ef2 | ||
|
|
d8840a0dfd | ||
|
|
f853c4ec2f | ||
|
|
9bf5f48165 | ||
|
|
0b2a204b70 | ||
|
|
c28593b752 | ||
|
|
4cbbef9574 | ||
|
|
300c952a9b | ||
|
|
38c3761260 | ||
|
|
3032f689b6 | ||
|
|
8b30af3d9e | ||
|
|
e10240bd7a | ||
|
|
ae9c27795e | ||
|
|
d7997d716e | ||
|
|
af0dc3094b | ||
|
|
665270296f | ||
|
|
2d945c8231 | ||
|
|
8866976bb4 | ||
|
|
d10f3c69f1 | ||
|
|
7e4f030547 | ||
|
|
05f46dd635 | ||
|
|
65882ea831 | ||
|
|
d301294eb5 | ||
|
|
9fd50ee9dd | ||
|
|
413472045d | ||
|
|
989f7b9cd2 | ||
|
|
00ccf4e525 | ||
|
|
afccd262a9 | ||
|
|
e9ad009f79 | ||
|
|
fc1067137b |
@@ -6,7 +6,7 @@ src/Api/Apis.ts
|
||||
src/AuthType.ts
|
||||
src/Bindings/BindingHandlersRegisterer.ts
|
||||
src/Bindings/ReactBindingHandler.ts
|
||||
src/Common/Constants.ts
|
||||
src/Common/Constants/index.ts
|
||||
src/Common/CosmosClient.test.ts
|
||||
src/Common/CosmosClient.ts
|
||||
src/Common/DataAccessUtilityBase.test.ts
|
||||
@@ -81,17 +81,13 @@ src/Explorer/Tables/DataTable/DataTableBindingManager.ts
|
||||
src/Explorer/Tables/DataTable/DataTableBuilder.ts
|
||||
src/Explorer/Tables/DataTable/DataTableContextMenu.ts
|
||||
src/Explorer/Tables/DataTable/DataTableOperationManager.ts
|
||||
src/Explorer/Tables/DataTable/DataTableOperations.ts
|
||||
# src/Explorer/Tables/DataTable/DataTableOperations.ts
|
||||
src/Explorer/Tables/DataTable/DataTableViewModel.ts
|
||||
src/Explorer/Tables/DataTable/TableCommands.ts
|
||||
src/Explorer/Tables/DataTable/TableEntityCache.ts
|
||||
# src/Explorer/Tables/DataTable/TableCommands.ts
|
||||
# src/Explorer/Tables/DataTable/TableEntityCache.ts
|
||||
src/Explorer/Tables/DataTable/TableEntityListViewModel.ts
|
||||
src/Explorer/Tables/Entities.ts
|
||||
src/Explorer/Tables/QueryBuilder/ClauseGroup.ts
|
||||
src/Explorer/Tables/QueryBuilder/ClauseGroupViewModel.ts
|
||||
# src/Explorer/Tables/Entities.ts
|
||||
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
|
||||
src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts
|
||||
src/Explorer/Tables/QueryBuilder/QueryClauseViewModel.ts
|
||||
src/Explorer/Tables/TableDataClient.ts
|
||||
src/Explorer/Tables/TableEntityProcessor.ts
|
||||
src/Explorer/Tables/Utilities.ts
|
||||
@@ -115,15 +111,10 @@ src/Explorer/Tree/ObjectId.ts
|
||||
src/Explorer/Tree/ResourceTokenCollection.ts
|
||||
src/Explorer/Tree/StoredProcedure.ts
|
||||
src/Explorer/Tree/TreeComponents.ts
|
||||
src/Explorer/Tree/Trigger.ts
|
||||
src/Explorer/WaitsForTemplateViewModel.ts
|
||||
src/GitHub/GitHubClient.test.ts
|
||||
src/GitHub/GitHubClient.ts
|
||||
src/GitHub/GitHubConnector.ts
|
||||
src/GitHub/GitHubOAuthService.ts
|
||||
src/Index.ts
|
||||
src/Juno/JunoClient.test.ts
|
||||
src/Juno/JunoClient.ts
|
||||
src/Platform/Hosted/Authorization.ts
|
||||
src/ReactDevTools.ts
|
||||
src/Shared/Constants.ts
|
||||
@@ -137,18 +128,26 @@ src/Terminal/NotebookAppContracts.d.ts
|
||||
src/applyExplorerBindings.ts
|
||||
src/global.d.ts
|
||||
src/setupTests.ts
|
||||
src/Explorer/Controls/InputTypeahead/InputTypeaheadComponent.tsx
|
||||
src/Explorer/Controls/Notebook/NotebookTerminalComponent.test.tsx
|
||||
src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
|
||||
src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx
|
||||
src/Explorer/Controls/TreeComponent/TreeComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx
|
||||
; src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphVizComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/LeftPaneComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/MiddlePaneComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.test.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
|
||||
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
|
||||
src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx
|
||||
src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx
|
||||
; src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx
|
||||
src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx
|
||||
src/Explorer/Notebook/NotebookComponent/contents/index.tsx
|
||||
src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx
|
||||
; src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx
|
||||
src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx
|
||||
src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx
|
||||
src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx
|
||||
|
||||
@@ -39,7 +39,6 @@ module.exports = {
|
||||
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-extraneous-class": "error",
|
||||
"no-null/no-null": "error",
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }],
|
||||
eqeqeq: "error",
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -22,5 +22,6 @@
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true,
|
||||
"source.organizeImports": true
|
||||
}
|
||||
},
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||
}
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
viewBox="0 0 16 16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path
|
||||
d="M14,2.691l-5.301,5.309l5.301,5.309l-0.691,0.691l-5.309,-5.301l-5.309,5.301l-0.691,-0.691l5.301,-5.309l-5.301,-5.309l0.691,-0.691l5.309,5.301l5.309,-5.301l0.691,0.691Z"
|
||||
transform="scale(0.5)"
|
||||
fill="#000"
|
||||
stroke="#CCC"
|
||||
>
|
||||
transform="scale(0.5)" fill="#000" stroke="#000">
|
||||
</path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 449 B |
@@ -37,8 +37,8 @@ module.exports = {
|
||||
global: {
|
||||
branches: 25,
|
||||
functions: 25,
|
||||
lines: 30,
|
||||
statements: 30,
|
||||
lines: 29,
|
||||
statements: 29,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -129,6 +129,8 @@ module.exports = {
|
||||
// The test environment that will be used for testing
|
||||
// testEnvironment: "jest-environment-jsdom",
|
||||
|
||||
modulePaths: ["node_modules", "<rootDir>/src"],
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
|
||||
@@ -2357,6 +2357,8 @@ a:link {
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
min-height: 300px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
@@ -2832,6 +2834,8 @@ a:link {
|
||||
|
||||
#explorerNotificationConsole {
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
.uniqueIndexesContainer {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
|
||||
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
|
||||
import { userContext } from "../UserContext";
|
||||
import { NormalizedEventKey } from "./Constants";
|
||||
|
||||
export interface CollapsedResourceTreeProps {
|
||||
toggleLeftPaneExpanded: () => void;
|
||||
@@ -11,6 +12,21 @@ export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps
|
||||
toggleLeftPaneExpanded,
|
||||
isLeftPaneExpanded,
|
||||
}: CollapsedResourceTreeProps): JSX.Element => {
|
||||
const focusButton = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
||||
|
||||
useEffect(() => {
|
||||
if (focusButton.current) {
|
||||
focusButton.current.focus();
|
||||
}
|
||||
});
|
||||
|
||||
const onKeyPressToggleLeftPaneExpanded = (event: React.KeyboardEvent) => {
|
||||
if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
|
||||
toggleLeftPaneExpanded();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div id="mini" className={!isLeftPaneExpanded ? "mini toggle-mini" : "hiddenMain"}>
|
||||
<div className="main-nav nav">
|
||||
@@ -21,11 +37,14 @@ export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="Expand Tree"
|
||||
onClick={toggleLeftPaneExpanded}
|
||||
onKeyPress={onKeyPressToggleLeftPaneExpanded}
|
||||
ref={focusButton}
|
||||
>
|
||||
<span className="leftarrowCollapsed" onClick={toggleLeftPaneExpanded}>
|
||||
<span className="leftarrowCollapsed">
|
||||
<img className="arrowCollapsed" src={arrowLeftImg} alt="Expand" />
|
||||
</span>
|
||||
<span className="collectionCollapsed" onClick={toggleLeftPaneExpanded}>
|
||||
<span className="collectionCollapsed">
|
||||
<span>{userContext.apiType} API</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
@@ -1,385 +0,0 @@
|
||||
export class CodeOfConductEndpoints {
|
||||
public static privacyStatement: string = "https://aka.ms/ms-privacy-policy";
|
||||
public static codeOfConduct: string = "https://aka.ms/cosmos-code-of-conduct";
|
||||
public static termsOfUse: string = "https://aka.ms/ms-terms-of-use";
|
||||
}
|
||||
|
||||
export class EndpointsRegex {
|
||||
public static readonly cassandra = [
|
||||
"AccountEndpoint=(.*).cassandra.cosmosdb.azure.com",
|
||||
"HostName=(.*).cassandra.cosmos.azure.com",
|
||||
];
|
||||
public static readonly mongo = "mongodb://.*:(.*)@(.*).documents.azure.com";
|
||||
public static readonly mongoCompute = "mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com";
|
||||
public static readonly sql = "AccountEndpoint=https://(.*).documents.azure.com";
|
||||
public static readonly table = "TableEndpoint=https://(.*).table.cosmosdb.azure.com";
|
||||
}
|
||||
|
||||
export class ApiEndpoints {
|
||||
public static runtimeProxy: string = "/api/RuntimeProxy";
|
||||
public static guestRuntimeProxy: string = "/api/guest/RuntimeProxy";
|
||||
}
|
||||
|
||||
export class ServerIds {
|
||||
public static localhost: string = "localhost";
|
||||
public static blackforest: string = "blackforest";
|
||||
public static fairfax: string = "fairfax";
|
||||
public static mooncake: string = "mooncake";
|
||||
public static productionPortal: string = "prod";
|
||||
public static dev: string = "dev";
|
||||
}
|
||||
|
||||
export class ArmApiVersions {
|
||||
public static readonly documentDB: string = "2015-11-06";
|
||||
public static readonly arcadia: string = "2019-06-01-preview";
|
||||
public static readonly arcadiaLivy: string = "2019-11-01-preview";
|
||||
public static readonly arm: string = "2015-11-01";
|
||||
public static readonly armFeatures: string = "2014-08-01-preview";
|
||||
public static readonly publicVersion = "2020-04-01";
|
||||
}
|
||||
|
||||
export class ArmResourceTypes {
|
||||
public static readonly notebookWorkspaces = "Microsoft.DocumentDB/databaseAccounts/notebookWorkspaces";
|
||||
public static readonly synapseWorkspaces = "Microsoft.Synapse/workspaces";
|
||||
}
|
||||
|
||||
export class BackendDefaults {
|
||||
public static partitionKeyKind = "Hash";
|
||||
public static singlePartitionStorageInGb: string = "10";
|
||||
public static multiPartitionStorageInGb: string = "100";
|
||||
public static maxChangeFeedRetentionDuration: number = 10;
|
||||
public static partitionKeyVersion = 2;
|
||||
}
|
||||
|
||||
export class ClientDefaults {
|
||||
public static requestTimeoutMs: number = 60000;
|
||||
public static portalCacheTimeoutMs: number = 10000;
|
||||
public static errorNotificationTimeoutMs: number = 5000;
|
||||
public static copyHelperTimeoutMs: number = 2000;
|
||||
public static waitForDOMElementMs: number = 500;
|
||||
public static cacheBustingTimeoutMs: number =
|
||||
10 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/;
|
||||
public static databaseThroughputIncreaseFactor: number = 100;
|
||||
public static readonly arcadiaTokenRefreshInterval: number =
|
||||
20 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/;
|
||||
public static readonly arcadiaTokenRefreshIntervalPaddingMs: number = 2000;
|
||||
}
|
||||
|
||||
export enum AccountKind {
|
||||
DocumentDB = "DocumentDB",
|
||||
MongoDB = "MongoDB",
|
||||
Parse = "Parse",
|
||||
GlobalDocumentDB = "GlobalDocumentDB",
|
||||
Default = "DocumentDB",
|
||||
}
|
||||
|
||||
export class CorrelationBackend {
|
||||
public static Url: string = "https://aka.ms/cosmosdbanalytics";
|
||||
}
|
||||
|
||||
export class CapabilityNames {
|
||||
public static EnableTable: string = "EnableTable";
|
||||
public static EnableGremlin: string = "EnableGremlin";
|
||||
public static EnableCassandra: string = "EnableCassandra";
|
||||
public static EnableAutoScale: string = "EnableAutoScale";
|
||||
public static readonly EnableNotebooks: string = "EnableNotebooks";
|
||||
public static readonly EnableStorageAnalytics: string = "EnableStorageAnalytics";
|
||||
public static readonly EnableMongo: string = "EnableMongo";
|
||||
public static readonly EnableServerless: string = "EnableServerless";
|
||||
}
|
||||
|
||||
// flight names returned from the portal are always lowercase
|
||||
export class Flights {
|
||||
public static readonly SettingsV2 = "settingsv2";
|
||||
public static readonly MongoIndexEditor = "mongoindexeditor";
|
||||
public static readonly MongoIndexing = "mongoindexing";
|
||||
public static readonly AutoscaleTest = "autoscaletest";
|
||||
public static readonly PartitionKeyTest = "partitionkeytest";
|
||||
public static readonly PKPartitionKeyTest = "pkpartitionkeytest";
|
||||
public static readonly Phoenix = "phoenix";
|
||||
}
|
||||
|
||||
export class AfecFeatures {
|
||||
public static readonly Spark = "spark-public-preview";
|
||||
public static readonly Notebooks = "sparknotebooks-public-preview";
|
||||
public static readonly StorageAnalytics = "storageanalytics-public-preview";
|
||||
}
|
||||
|
||||
export class TagNames {
|
||||
public static defaultExperience: string = "defaultExperience";
|
||||
}
|
||||
|
||||
export class MongoDBAccounts {
|
||||
public static protocol: string = "https";
|
||||
public static defaultPort: string = "10255";
|
||||
}
|
||||
|
||||
export enum MongoBackendEndpointType {
|
||||
local,
|
||||
remote,
|
||||
}
|
||||
|
||||
// TODO: 435619 Add default endpoints per cloud and use regional only when available
|
||||
export class CassandraBackend {
|
||||
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
||||
public static readonly guestCreateOrDeleteApi: string = "api/guest/cassandra/createordelete";
|
||||
public static readonly queryApi: string = "api/cassandra";
|
||||
public static readonly guestQueryApi: string = "api/guest/cassandra";
|
||||
public static readonly keysApi: string = "api/cassandra/keys";
|
||||
public static readonly guestKeysApi: string = "api/guest/cassandra/keys";
|
||||
public static readonly schemaApi: string = "api/cassandra/schema";
|
||||
public static readonly guestSchemaApi: string = "api/guest/cassandra/schema";
|
||||
}
|
||||
|
||||
export class Queries {
|
||||
public static CustomPageOption: string = "custom";
|
||||
public static UnlimitedPageOption: string = "unlimited";
|
||||
public static itemsPerPage: number = 100;
|
||||
public static unlimitedItemsPerPage: number = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions
|
||||
|
||||
public static QueryEditorMinHeightRatio: number = 0.1;
|
||||
public static QueryEditorMaxHeightRatio: number = 0.4;
|
||||
public static readonly DefaultMaxDegreeOfParallelism = 6;
|
||||
}
|
||||
|
||||
export class SavedQueries {
|
||||
public static readonly CollectionName: string = "___Query";
|
||||
public static readonly DatabaseName: string = "___Cosmos";
|
||||
public static readonly OfferThroughput: number = 400;
|
||||
public static readonly PartitionKeyProperty: string = "id";
|
||||
}
|
||||
|
||||
export class DocumentsGridMetrics {
|
||||
public static DocumentsPerPage: number = 100;
|
||||
public static IndividualRowHeight: number = 34;
|
||||
public static BufferHeight: number = 28;
|
||||
public static SplitterMinWidth: number = 200;
|
||||
public static SplitterMaxWidth: number = 360;
|
||||
|
||||
public static DocumentEditorMinWidthRatio: number = 0.2;
|
||||
public static DocumentEditorMaxWidthRatio: number = 0.4;
|
||||
}
|
||||
|
||||
export class Areas {
|
||||
public static ResourceTree: string = "Resource Tree";
|
||||
public static ContextualPane: string = "Contextual Pane";
|
||||
public static Tab: string = "Tab";
|
||||
public static ShareDialog: string = "Share Access Dialog";
|
||||
public static Notebook: string = "Notebook";
|
||||
}
|
||||
|
||||
export class HttpHeaders {
|
||||
public static activityId: string = "x-ms-activity-id";
|
||||
public static apiType: string = "x-ms-cosmos-apitype";
|
||||
public static authorization: string = "authorization";
|
||||
public static collectionIndexTransformationProgress: string =
|
||||
"x-ms-documentdb-collection-index-transformation-progress";
|
||||
public static continuation: string = "x-ms-continuation";
|
||||
public static correlationRequestId: string = "x-ms-correlation-request-id";
|
||||
public static enableScriptLogging: string = "x-ms-documentdb-script-enable-logging";
|
||||
public static guestAccessToken: string = "x-ms-encrypted-auth-token";
|
||||
public static getReadOnlyKey: string = "x-ms-get-read-only-key";
|
||||
public static connectionString: string = "x-ms-connection-string";
|
||||
public static msDate: string = "x-ms-date";
|
||||
public static location: string = "Location";
|
||||
public static contentType: string = "Content-Type";
|
||||
public static offerReplacePending: string = "x-ms-offer-replace-pending";
|
||||
public static user: string = "x-ms-user";
|
||||
public static populatePartitionStatistics: string = "x-ms-documentdb-populatepartitionstatistics";
|
||||
public static queryMetrics: string = "x-ms-documentdb-query-metrics";
|
||||
public static requestCharge: string = "x-ms-request-charge";
|
||||
public static resourceQuota: string = "x-ms-resource-quota";
|
||||
public static resourceUsage: string = "x-ms-resource-usage";
|
||||
public static retryAfterMs: string = "x-ms-retry-after-ms";
|
||||
public static scriptLogResults: string = "x-ms-documentdb-script-log-results";
|
||||
public static populateCollectionThroughputInfo = "x-ms-documentdb-populatecollectionthroughputinfo";
|
||||
public static supportSpatialLegacyCoordinates = "x-ms-documentdb-supportspatiallegacycoordinates";
|
||||
public static usePolygonsSmallerThanAHemisphere = "x-ms-documentdb-usepolygonssmallerthanahemisphere";
|
||||
public static autoPilotThroughput = "autoscaleSettings";
|
||||
public static autoPilotThroughputSDK = "x-ms-cosmos-offer-autopilot-settings";
|
||||
public static partitionKey: string = "x-ms-documentdb-partitionkey";
|
||||
public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput";
|
||||
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
|
||||
}
|
||||
|
||||
export class ApiType {
|
||||
// Mapped to hexadecimal values in the backend
|
||||
public static readonly MongoDB: number = 1;
|
||||
public static readonly Gremlin: number = 2;
|
||||
public static readonly Cassandra: number = 4;
|
||||
public static readonly Table: number = 8;
|
||||
public static readonly SQL: number = 16;
|
||||
}
|
||||
|
||||
export class HttpStatusCodes {
|
||||
public static readonly OK: number = 200;
|
||||
public static readonly Created: number = 201;
|
||||
public static readonly Accepted: number = 202;
|
||||
public static readonly NoContent: number = 204;
|
||||
public static readonly NotModified: number = 304;
|
||||
public static readonly Unauthorized: number = 401;
|
||||
public static readonly Forbidden: number = 403;
|
||||
public static readonly NotFound: number = 404;
|
||||
public static readonly TooManyRequests: number = 429;
|
||||
public static readonly Conflict: number = 409;
|
||||
|
||||
public static readonly InternalServerError: number = 500;
|
||||
public static readonly BadGateway: number = 502;
|
||||
public static readonly ServiceUnavailable: number = 503;
|
||||
public static readonly GatewayTimeout: number = 504;
|
||||
|
||||
public static readonly RetryableStatusCodes: number[] = [
|
||||
HttpStatusCodes.TooManyRequests,
|
||||
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
|
||||
HttpStatusCodes.BadGateway,
|
||||
HttpStatusCodes.ServiceUnavailable,
|
||||
HttpStatusCodes.GatewayTimeout,
|
||||
];
|
||||
}
|
||||
|
||||
export class Urls {
|
||||
public static feedbackEmail = "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Data%20Explorer%20Feedback";
|
||||
public static autoscaleMigration = "https://aka.ms/cosmos-autoscale-migration";
|
||||
public static freeTierInformation = "https://aka.ms/cosmos-free-tier";
|
||||
public static cosmosPricing = "https://aka.ms/azure-cosmos-db-pricing";
|
||||
}
|
||||
|
||||
export class HashRoutePrefixes {
|
||||
public static databases: string = "/dbs/{db_id}";
|
||||
public static collections: string = "/dbs/{db_id}/colls/{coll_id}";
|
||||
public static sprocHash: string = "/sprocs/";
|
||||
public static sprocs: string = HashRoutePrefixes.collections + HashRoutePrefixes.sprocHash + "{sproc_id}";
|
||||
public static docs: string = HashRoutePrefixes.collections + "/docs/{doc_id}/";
|
||||
public static conflicts: string = HashRoutePrefixes.collections + "/conflicts";
|
||||
|
||||
public static databasesWithId(databaseId: string): string {
|
||||
return this.databases.replace("{db_id}", databaseId).replace("/", ""); // strip the first slash since hasher adds it
|
||||
}
|
||||
|
||||
public static collectionsWithIds(databaseId: string, collectionId: string): string {
|
||||
const transformedDatabasePrefix: string = this.collections.replace("{db_id}", databaseId);
|
||||
|
||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it
|
||||
}
|
||||
|
||||
public static sprocWithIds(
|
||||
databaseId: string,
|
||||
collectionId: string,
|
||||
sprocId: string,
|
||||
stripFirstSlash: boolean = true
|
||||
): string {
|
||||
const transformedDatabasePrefix: string = this.sprocs.replace("{db_id}", databaseId);
|
||||
|
||||
const transformedSprocRoute: string = transformedDatabasePrefix
|
||||
.replace("{coll_id}", collectionId)
|
||||
.replace("{sproc_id}", sprocId);
|
||||
if (!!stripFirstSlash) {
|
||||
return transformedSprocRoute.replace("/", ""); // strip the first slash since hasher adds it
|
||||
}
|
||||
|
||||
return transformedSprocRoute;
|
||||
}
|
||||
|
||||
public static conflictsWithIds(databaseId: string, collectionId: string) {
|
||||
const transformedDatabasePrefix: string = this.conflicts.replace("{db_id}", databaseId);
|
||||
|
||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it;
|
||||
}
|
||||
|
||||
public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
|
||||
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
|
||||
|
||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("{doc_id}", docId).replace("/", ""); // strip the first slash since hasher adds it
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigurationOverridesValues {
|
||||
public static IsBsonSchemaV2: string = "true";
|
||||
}
|
||||
|
||||
export class KeyCodes {
|
||||
public static Space: number = 32;
|
||||
public static Enter: number = 13;
|
||||
public static Escape: number = 27;
|
||||
public static UpArrow: number = 38;
|
||||
public static DownArrow: number = 40;
|
||||
public static LeftArrow: number = 37;
|
||||
public static RightArrow: number = 39;
|
||||
public static Tab: number = 9;
|
||||
}
|
||||
|
||||
// Normalized per: https://www.w3.org/TR/uievents-key/#named-key-attribute-values
|
||||
export class NormalizedEventKey {
|
||||
public static readonly Space = " ";
|
||||
public static readonly Enter = "Enter";
|
||||
public static readonly Escape = "Escape";
|
||||
public static readonly UpArrow = "ArrowUp";
|
||||
public static readonly DownArrow = "ArrowDown";
|
||||
public static readonly LeftArrow = "ArrowLeft";
|
||||
public static readonly RightArrow = "ArrowRight";
|
||||
}
|
||||
|
||||
export class TryCosmosExperience {
|
||||
public static extendUrl: string = "https://trycosmosdb.azure.com/api/resource/extendportal?userId={0}";
|
||||
public static deleteUrl: string = "https://trycosmosdb.azure.com/api/resource/deleteportal?userId={0}";
|
||||
public static collectionsPerAccount: number = 3;
|
||||
public static maxRU: number = 5000;
|
||||
public static defaultRU: number = 3000;
|
||||
}
|
||||
|
||||
export class OfferVersions {
|
||||
public static V1: string = "V1";
|
||||
public static V2: string = "V2";
|
||||
}
|
||||
|
||||
export enum ConflictOperationType {
|
||||
Replace = "replace",
|
||||
Create = "create",
|
||||
Delete = "delete",
|
||||
}
|
||||
|
||||
export enum ConnectionStatusType {
|
||||
Connecting = "Connecting",
|
||||
Allocating = "Allocating",
|
||||
Connected = "Connected",
|
||||
Failed = "Connection Failed",
|
||||
}
|
||||
|
||||
export const EmulatorMasterKey =
|
||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||
|
||||
// A variable @MyVariable defined in Constants.less is accessible as StyleConstants.MyVariable
|
||||
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
|
||||
|
||||
export class Notebook {
|
||||
public static readonly defaultBasePath = "./notebooks";
|
||||
public static readonly heartbeatDelayMs = 5000;
|
||||
public static readonly kernelRestartInitialDelayMs = 1000;
|
||||
public static readonly kernelRestartMaxDelayMs = 20000;
|
||||
public static readonly autoSaveIntervalMs = 120000;
|
||||
public static readonly temporarilyDownMsg = "Notebooks is currently not available. We are working on it.";
|
||||
public static readonly mongoShellTemporarilyDownMsg =
|
||||
"We have identified an issue with the Mongo Shell and it is unavailable right now. We are actively working on the mitigation.";
|
||||
public static readonly cassandraShellTemporarilyDownMsg =
|
||||
"We have identified an issue with the Cassandra Shell and it is unavailable right now. We are actively working on the mitigation.";
|
||||
}
|
||||
|
||||
export class SparkLibrary {
|
||||
public static readonly nameMinLength = 3;
|
||||
public static readonly nameMaxLength = 63;
|
||||
}
|
||||
|
||||
export class AnalyticalStorageTtl {
|
||||
public static readonly Days90: number = 7776000;
|
||||
public static readonly Infinite: number = -1;
|
||||
public static readonly Disabled: number = 0;
|
||||
}
|
||||
|
||||
export class TerminalQueryParams {
|
||||
public static readonly Terminal = "terminal";
|
||||
public static readonly Server = "server";
|
||||
public static readonly Token = "token";
|
||||
public static readonly SubscriptionId = "subscriptionId";
|
||||
public static readonly TerminalEndpoint = "terminalEndpoint";
|
||||
}
|
||||
3
src/Common/Constants/AfecFeatures.ts
Normal file
3
src/Common/Constants/AfecFeatures.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const Spark = "spark-public-preview";
|
||||
export const Notebooks = "sparknotebooks-public-preview";
|
||||
export const StorageAnalytics = "storageanalytics-public-preview";
|
||||
3
src/Common/Constants/AnalyticalStorageTtl.ts
Normal file
3
src/Common/Constants/AnalyticalStorageTtl.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const Days90 = 7776000;
|
||||
export const Infinite = -1;
|
||||
export const Disabled = 0;
|
||||
2
src/Common/Constants/ApiEndpoints.ts
Normal file
2
src/Common/Constants/ApiEndpoints.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const runtimeProxy = "/api/RuntimeProxy";
|
||||
export const guestRuntimeProxy = "/api/guest/RuntimeProxy";
|
||||
6
src/Common/Constants/ApiType.ts
Normal file
6
src/Common/Constants/ApiType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// Mapped to hexadecimal values in the backend
|
||||
export const MongoDB = 1;
|
||||
export const Gremlin = 2;
|
||||
export const Cassandra = 4;
|
||||
export const Table = 8;
|
||||
export const SQL = 16;
|
||||
5
src/Common/Constants/Areas.ts
Normal file
5
src/Common/Constants/Areas.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const ResourceTree = "Resource Tree";
|
||||
export const ContextualPane = "Contextual Pane";
|
||||
export const Tab = "Tab";
|
||||
export const ShareDialog = "Share Access Dialog";
|
||||
export const Notebook = "Notebook";
|
||||
6
src/Common/Constants/ArmApiVersions.ts
Normal file
6
src/Common/Constants/ArmApiVersions.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const documentDB = "2015-11-06";
|
||||
export const arcadia = "2019-06-01-preview";
|
||||
export const arcadiaLivy = "2019-11-01-preview";
|
||||
export const arm = "2015-11-01";
|
||||
export const armFeatures = "2014-08-01-preview";
|
||||
export const publicVersion = "2020-04-01";
|
||||
2
src/Common/Constants/ArmResourceTypes.ts
Normal file
2
src/Common/Constants/ArmResourceTypes.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const notebookWorkspaces = "Microsoft.DocumentDB/databaseAccounts/notebookWorkspaces";
|
||||
export const synapseWorkspaces = "Microsoft.Synapse/workspaces";
|
||||
5
src/Common/Constants/BackendDefaults.ts
Normal file
5
src/Common/Constants/BackendDefaults.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const partitionKeyKind = "Hash";
|
||||
export const singlePartitionStorageInGb = "10";
|
||||
export const multiPartitionStorageInGb = "100";
|
||||
export const maxChangeFeedRetentionDuration = 10;
|
||||
export const partitionKeyVersion = 2;
|
||||
8
src/Common/Constants/CapabilityNames.ts
Normal file
8
src/Common/Constants/CapabilityNames.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const EnableTable = "EnableTable";
|
||||
export const EnableGremlin = "EnableGremlin";
|
||||
export const EnableCassandra = "EnableCassandra";
|
||||
export const EnableAutoScale = "EnableAutoScale";
|
||||
export const EnableNotebooks = "EnableNotebooks";
|
||||
export const EnableStorageAnalytics = "EnableStorageAnalytics";
|
||||
export const EnableMongo = "EnableMongo";
|
||||
export const EnableServerless = "EnableServerless";
|
||||
9
src/Common/Constants/CassandraBackend.ts
Normal file
9
src/Common/Constants/CassandraBackend.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// TODO: 435619 Add default endpoints per cloud and use regional only when available
|
||||
export const createOrDeleteApi = "api/cassandra/createordelete";
|
||||
export const guestCreateOrDeleteApi = "api/guest/cassandra/createordelete";
|
||||
export const queryApi = "api/cassandra";
|
||||
export const guestQueryApi = "api/guest/cassandra";
|
||||
export const keysApi = "api/cassandra/keys";
|
||||
export const guestKeysApi = "api/guest/cassandra/keys";
|
||||
export const schemaApi = "api/cassandra/schema";
|
||||
export const guestSchemaApi = "api/guest/cassandra/schema";
|
||||
9
src/Common/Constants/ClientDefaults.ts
Normal file
9
src/Common/Constants/ClientDefaults.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export const requestTimeoutMs = 60000;
|
||||
export const portalCacheTimeoutMs = 10000;
|
||||
export const errorNotificationTimeoutMs = 5000;
|
||||
export const copyHelperTimeoutMs = 2000;
|
||||
export const waitForDOMElementMs = 500;
|
||||
export const cacheBustingTimeoutMs = 10 /** minutes **/ * 60 /** to seconds **/ * 1000; /** to milliseconds **/
|
||||
export const databaseThroughputIncreaseFactor = 100;
|
||||
export const arcadiaTokenRefreshInterval = 20 /** minutes **/ * 60 /** to seconds **/ * 1000; /** to milliseconds **/
|
||||
export const arcadiaTokenRefreshIntervalPaddingMs = 2000;
|
||||
3
src/Common/Constants/CodeOfConductEndpoints.ts
Normal file
3
src/Common/Constants/CodeOfConductEndpoints.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const privacyStatement = "https://aka.ms/ms-privacy-policy";
|
||||
export const codeOfConduct = "https://aka.ms/cosmos-code-of-conduct";
|
||||
export const termsOfUse = "https://aka.ms/ms-terms-of-use";
|
||||
1
src/Common/Constants/ConfigurationOverridesValues.ts
Normal file
1
src/Common/Constants/ConfigurationOverridesValues.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const IsBsonSchemaV2 = "true";
|
||||
1
src/Common/Constants/CorrelationBackend.ts
Normal file
1
src/Common/Constants/CorrelationBackend.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const Url = "https://aka.ms/cosmosdbanalytics";
|
||||
8
src/Common/Constants/DocumentsGridMetrics.ts
Normal file
8
src/Common/Constants/DocumentsGridMetrics.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const DocumentsPerPage = 100;
|
||||
export const IndividualRowHeight = 34;
|
||||
export const BufferHeight = 28;
|
||||
export const SplitterMinWidth = 200;
|
||||
export const SplitterMaxWidth = 360;
|
||||
|
||||
export const DocumentEditorMinWidthRatio = 0.2;
|
||||
export const DocumentEditorMaxWidthRatio = 0.4;
|
||||
8
src/Common/Constants/EndpointsRegex.ts
Normal file
8
src/Common/Constants/EndpointsRegex.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const cassandra = [
|
||||
"AccountEndpoint=(.*).cassandra.cosmosdb.azure.com",
|
||||
"HostName=(.*).cassandra.cosmos.azure.com",
|
||||
];
|
||||
export const mongo = "mongodb://.*:(.*)@(.*).documents.azure.com";
|
||||
export const mongoCompute = "mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com";
|
||||
export const sql = "AccountEndpoint=https://(.*).documents.azure.com";
|
||||
export const table = "TableEndpoint=https://(.*).table.cosmosdb.azure.com";
|
||||
8
src/Common/Constants/Flights.ts
Normal file
8
src/Common/Constants/Flights.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// flight names returned from the portal are always lowercase
|
||||
export const SettingsV2 = "settingsv2";
|
||||
export const MongoIndexEditor = "mongoindexeditor";
|
||||
export const MongoIndexing = "mongoindexing";
|
||||
export const AutoscaleTest = "autoscaletest";
|
||||
export const PartitionKeyTest = "partitionkeytest";
|
||||
export const PKPartitionKeyTest = "pkpartitionkeytest";
|
||||
export const Phoenix = "phoenix";
|
||||
40
src/Common/Constants/HashRoutePrefixes.ts
Normal file
40
src/Common/Constants/HashRoutePrefixes.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
export const databases = "/dbs/{db_id}";
|
||||
export const collections = "/dbs/{db_id}/colls/{coll_id}";
|
||||
export const sprocHash = "/sprocs/";
|
||||
export const sprocs = collections + sprocHash + "{sproc_id}";
|
||||
export const docs = collections + "/docs/{doc_id}/";
|
||||
export const conflicts = collections + "/conflicts";
|
||||
|
||||
export const databasesWithId = (databaseId: string) => {
|
||||
return databases.replace("{db_id}", databaseId).replace("/", ""); // strip the first slash since hasher adds it
|
||||
};
|
||||
|
||||
export const collectionsWithIds = (databaseId: string, collectionId: string) => {
|
||||
const transformedDatabasePrefix = collections.replace("{db_id}", databaseId);
|
||||
|
||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it
|
||||
};
|
||||
|
||||
export const sprocWithIds = (databaseId: string, collectionId: string, sprocId: string, stripFirstSlash = true) => {
|
||||
const transformedDatabasePrefix = sprocs.replace("{db_id}", databaseId);
|
||||
|
||||
const transformedSprocRoute = transformedDatabasePrefix
|
||||
.replace("{coll_id}", collectionId)
|
||||
.replace("{sproc_id}", sprocId);
|
||||
if (stripFirstSlash) {
|
||||
return transformedSprocRoute.replace("/", ""); // strip the first slash since hasher adds it
|
||||
}
|
||||
|
||||
return transformedSprocRoute;
|
||||
};
|
||||
|
||||
export const conflictsWithIds = (databaseId: string, collectionId: string) => {
|
||||
const transformedDatabasePrefix = conflicts.replace("{db_id}", databaseId);
|
||||
|
||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it;
|
||||
};
|
||||
|
||||
export const docsWithIds = (databaseId: string, collectionId: string, docId: string): string => {
|
||||
const transformedDatabasePrefix = docs.replace("{db_id}", databaseId);
|
||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("{doc_id}", docId).replace("/", ""); // strip the first slash since hasher adds it
|
||||
};
|
||||
30
src/Common/Constants/HttpHeaders.ts
Normal file
30
src/Common/Constants/HttpHeaders.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export const activityId = "x-ms-activity-id";
|
||||
export const apiType = "x-ms-cosmos-apitype";
|
||||
export const authorization = "authorization";
|
||||
export const collectionIndexTransformationProgress = "x-ms-documentdb-collection-index-transformation-progress";
|
||||
export const continuation = "x-ms-continuation";
|
||||
export const correlationRequestId = "x-ms-correlation-request-id";
|
||||
export const enableScriptLogging = "x-ms-documentdb-script-enable-logging";
|
||||
export const guestAccessToken = "x-ms-encrypted-auth-token";
|
||||
export const getReadOnlyKey = "x-ms-get-read-only-key";
|
||||
export const connectionString = "x-ms-connection-string";
|
||||
export const msDate = "x-ms-date";
|
||||
export const location = "Location";
|
||||
export const contentType = "Content-Type";
|
||||
export const offerReplacePending = "x-ms-offer-replace-pending";
|
||||
export const user = "x-ms-user";
|
||||
export const populatePartitionStatistics = "x-ms-documentdb-populatepartitionstatistics";
|
||||
export const queryMetrics = "x-ms-documentdb-query-metrics";
|
||||
export const requestCharge = "x-ms-request-charge";
|
||||
export const resourceQuota = "x-ms-resource-quota";
|
||||
export const resourceUsage = "x-ms-resource-usage";
|
||||
export const retryAfterMs = "x-ms-retry-after-ms";
|
||||
export const scriptLogResults = "x-ms-documentdb-script-log-results";
|
||||
export const populateCollectionThroughputInfo = "x-ms-documentdb-populatecollectionthroughputinfo";
|
||||
export const supportSpatialLegacyCoordinates = "x-ms-documentdb-supportspatiallegacycoordinates";
|
||||
export const usePolygonsSmallerThanAHemisphere = "x-ms-documentdb-usepolygonssmallerthanahemisphere";
|
||||
export const autoPilotThroughput = "autoscaleSettings";
|
||||
export const autoPilotThroughputSDK = "x-ms-cosmos-offer-autopilot-settings";
|
||||
export const partitionKey = "x-ms-documentdb-partitionkey";
|
||||
export const migrateOfferToManualThroughput = "x-ms-cosmos-migrate-offer-to-manual-throughput";
|
||||
export const migrateOfferToAutopilot = "x-ms-cosmos-migrate-offer-to-autopilot";
|
||||
23
src/Common/Constants/HttpStatusCodes.ts
Normal file
23
src/Common/Constants/HttpStatusCodes.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export const OK = 200;
|
||||
export const Created = 201;
|
||||
export const Accepted = 202;
|
||||
export const NoContent = 204;
|
||||
export const NotModified = 304;
|
||||
export const Unauthorized = 401;
|
||||
export const Forbidden = 403;
|
||||
export const NotFound = 404;
|
||||
export const TooManyRequests = 429;
|
||||
export const Conflict = 409;
|
||||
|
||||
export const InternalServerError = 500;
|
||||
export const BadGateway = 502;
|
||||
export const ServiceUnavailable = 503;
|
||||
export const GatewayTimeout = 504;
|
||||
|
||||
export const RetryableStatusCodes: number[] = [
|
||||
TooManyRequests,
|
||||
InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
|
||||
BadGateway,
|
||||
ServiceUnavailable,
|
||||
GatewayTimeout,
|
||||
];
|
||||
8
src/Common/Constants/KeyCodes.ts
Normal file
8
src/Common/Constants/KeyCodes.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const Space = 32;
|
||||
export const Enter = 13;
|
||||
export const Escape = 27;
|
||||
export const UpArrow = 38;
|
||||
export const DownArrow = 40;
|
||||
export const LeftArrow = 37;
|
||||
export const RightArrow = 39;
|
||||
export const Tab = 9;
|
||||
2
src/Common/Constants/MongoDBAccounts.ts
Normal file
2
src/Common/Constants/MongoDBAccounts.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const protocol = "https";
|
||||
export const defaultPort = "10255";
|
||||
8
src/Common/Constants/NormalizedEventKey.ts
Normal file
8
src/Common/Constants/NormalizedEventKey.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// Normalized per: https://www.w3.org/TR/uievents-key/#named-key-attribute-values
|
||||
export const Space = " ";
|
||||
export const Enter = "Enter";
|
||||
export const Escape = "Escape";
|
||||
export const UpArrow = "ArrowUp";
|
||||
export const DownArrow = "ArrowDown";
|
||||
export const LeftArrow = "ArrowLeft";
|
||||
export const RightArrow = "ArrowRight";
|
||||
27
src/Common/Constants/Notebook.ts
Normal file
27
src/Common/Constants/Notebook.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export const defaultBasePath = "./notebooks";
|
||||
export const heartbeatDelayMs = 60000;
|
||||
export const kernelRestartInitialDelayMs = 1000;
|
||||
export const kernelRestartMaxDelayMs = 20000;
|
||||
export const autoSaveIntervalMs = 120000;
|
||||
export const memoryGuageToGB = 1048576;
|
||||
export const temporarilyDownMsg = "Notebooks is currently not available. We are working on it.";
|
||||
export const mongoShellTemporarilyDownMsg =
|
||||
"We have identified an issue with the Mongo Shell and it is unavailable right now. We are actively working on the mitigation.";
|
||||
export const cassandraShellTemporarilyDownMsg =
|
||||
"We have identified an issue with the Cassandra Shell and it is unavailable right now. We are actively working on the mitigation.";
|
||||
export const saveNotebookModalTitle = "Save Notebook in temporary workspace";
|
||||
export const saveNotebookModalContent =
|
||||
"This notebook will be saved in the temporary workspace and will be removed when the session expires. To save your work permanently, save your notebooks to a GitHub repository or download the notebooks to your local machine before the session ends.";
|
||||
export const newNotebookModalTitle = "Create Notebook in temporary workspace";
|
||||
export const newNotebookUploadModalTitle = "Upload Notebook in temporary workspace";
|
||||
export const newNotebookModalContent1 =
|
||||
"A temporary workspace will be created to enable you to work with notebooks. When the session expires, any notebooks in the workspace will be removed.";
|
||||
export const newNotebookModalContent2 =
|
||||
"To save your work permanently, save your notebooks to a GitHub repository or download the notebooks to your local machine before the session ends. ";
|
||||
export const galleryNotebookDownloadContent1 =
|
||||
"To download, run, and make changes to this sample notebook, a temporary workspace will be created. When the session expires, any notebooks in the workspace will be removed.";
|
||||
export const galleryNotebookDownloadContent2 =
|
||||
"To save your work permanently, save your notebooks to a GitHub repository or download the Notebooks to your local machine before the session ends. ";
|
||||
export const cosmosNotebookHomePageUrl = "https://aka.ms/cosmos-notebooks-limits";
|
||||
export const cosmosNotebookGitDocumentationUrl = "https://aka.ms/cosmos-notebooks-github";
|
||||
export const learnMore = "Learn more.";
|
||||
2
src/Common/Constants/OfferVersions.ts
Normal file
2
src/Common/Constants/OfferVersions.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const V1 = "V1";
|
||||
export const V2 = "V2";
|
||||
8
src/Common/Constants/Queries.ts
Normal file
8
src/Common/Constants/Queries.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const CustomPageOption = "custom";
|
||||
export const UnlimitedPageOption = "unlimited";
|
||||
export const itemsPerPage = 100;
|
||||
export const unlimitedItemsPerPage = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions
|
||||
|
||||
export const QueryEditorMinHeightRatio = 0.1;
|
||||
export const QueryEditorMaxHeightRatio = 0.4;
|
||||
export const DefaultMaxDegreeOfParallelism = 6;
|
||||
4
src/Common/Constants/SavedQueries.ts
Normal file
4
src/Common/Constants/SavedQueries.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const CollectionName = "___Query";
|
||||
export const DatabaseName = "___Cosmos";
|
||||
export const OfferThroughput = 400;
|
||||
export const PartitionKeyProperty = "id";
|
||||
6
src/Common/Constants/ServerIds.ts
Normal file
6
src/Common/Constants/ServerIds.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const localhost = "localhost";
|
||||
export const blackforest = "blackforest";
|
||||
export const fairfax = "fairfax";
|
||||
export const mooncake = "mooncake";
|
||||
export const productionPortal = "prod";
|
||||
export const dev = "dev";
|
||||
2
src/Common/Constants/SparkLibrary.ts
Normal file
2
src/Common/Constants/SparkLibrary.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const nameMinLength = 3;
|
||||
export const nameMaxLength = 63;
|
||||
1
src/Common/Constants/TagNames.ts
Normal file
1
src/Common/Constants/TagNames.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const defaultExperience = "defaultExperience";
|
||||
5
src/Common/Constants/TerminalQueryParams.ts
Normal file
5
src/Common/Constants/TerminalQueryParams.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const Terminal = "terminal";
|
||||
export const Server = "server";
|
||||
export const Token = "token";
|
||||
export const SubscriptionId = "subscriptionId";
|
||||
export const TerminalEndpoint = "terminalEndpoint";
|
||||
5
src/Common/Constants/TryCosmosExperience.ts
Normal file
5
src/Common/Constants/TryCosmosExperience.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const extendUrl = "https://trycosmosdb.azure.com/api/resource/extendportal?userId={0}";
|
||||
export const deleteUrl = "https://trycosmosdb.azure.com/api/resource/deleteportal?userId={0}";
|
||||
export const collectionsPerAccount = 3;
|
||||
export const maxRU = 5000;
|
||||
export const defaultRU = 3000;
|
||||
4
src/Common/Constants/Urls.ts
Normal file
4
src/Common/Constants/Urls.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const feedbackEmail = "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Data%20Explorer%20Feedback";
|
||||
export const autoscaleMigration = "https://aka.ms/cosmos-autoscale-migration";
|
||||
export const freeTierInformation = "https://aka.ms/cosmos-free-tier";
|
||||
export const cosmosPricing = "https://aka.ms/azure-cosmos-db-pricing";
|
||||
105
src/Common/Constants/index.ts
Normal file
105
src/Common/Constants/index.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import * as AfecFeatures from "./AfecFeatures";
|
||||
import * as AnalyticalStorageTtl from "./AnalyticalStorageTtl";
|
||||
import * as ApiEndpoints from "./ApiEndpoints";
|
||||
import * as ApiType from "./ApiType";
|
||||
import * as Areas from "./Areas";
|
||||
import * as ArmApiVersions from "./ArmApiVersions";
|
||||
import * as ArmResourceTypes from "./ArmResourceTypes";
|
||||
import * as BackendDefaults from "./BackendDefaults";
|
||||
import * as CapabilityNames from "./CapabilityNames";
|
||||
import * as CassandraBackend from "./CassandraBackend";
|
||||
import * as ClientDefaults from "./ClientDefaults";
|
||||
import * as CodeOfConductEndpoints from "./CodeOfConductEndpoints";
|
||||
import * as ConfigurationOverridesValues from "./ConfigurationOverridesValues";
|
||||
import * as CorrelationBackend from "./CorrelationBackend";
|
||||
import * as DocumentsGridMetrics from "./DocumentsGridMetrics";
|
||||
import * as EndpointsRegex from "./EndpointsRegex";
|
||||
import * as Flights from "./Flights";
|
||||
import * as HashRoutePrefixes from "./HashRoutePrefixes";
|
||||
import * as HttpHeaders from "./HttpHeaders";
|
||||
import * as HttpStatusCodes from "./HttpStatusCodes";
|
||||
import * as KeyCodes from "./KeyCodes";
|
||||
import * as MongoDBAccounts from "./MongoDBAccounts";
|
||||
import * as NormalizedEventKey from "./NormalizedEventKey";
|
||||
import * as Notebook from "./Notebook";
|
||||
import * as OfferVersions from "./OfferVersions";
|
||||
import * as Queries from "./Queries";
|
||||
import * as SavedQueries from "./SavedQueries";
|
||||
import * as ServerIds from "./ServerIds";
|
||||
import * as SparkLibrary from "./SparkLibrary";
|
||||
import * as TagNames from "./TagNames";
|
||||
import * as TerminalQueryParams from "./TerminalQueryParams";
|
||||
import * as TryCosmosExperience from "./TryCosmosExperience";
|
||||
import * as Urls from "./Urls";
|
||||
|
||||
const StyleConstants = require("less-vars-loader!../../../less/Common/Constants.less");
|
||||
|
||||
export {
|
||||
StyleConstants,
|
||||
SparkLibrary,
|
||||
ConfigurationOverridesValues,
|
||||
OfferVersions,
|
||||
AnalyticalStorageTtl,
|
||||
Notebook,
|
||||
TryCosmosExperience,
|
||||
NormalizedEventKey,
|
||||
KeyCodes,
|
||||
HashRoutePrefixes,
|
||||
Urls,
|
||||
HttpStatusCodes,
|
||||
ApiType,
|
||||
HttpHeaders,
|
||||
Areas,
|
||||
DocumentsGridMetrics,
|
||||
SavedQueries,
|
||||
Queries,
|
||||
CassandraBackend,
|
||||
MongoDBAccounts,
|
||||
TagNames,
|
||||
AfecFeatures,
|
||||
Flights,
|
||||
CorrelationBackend,
|
||||
CapabilityNames,
|
||||
ClientDefaults,
|
||||
BackendDefaults,
|
||||
ArmResourceTypes,
|
||||
ArmApiVersions,
|
||||
TerminalQueryParams,
|
||||
CodeOfConductEndpoints,
|
||||
ApiEndpoints,
|
||||
EndpointsRegex,
|
||||
ServerIds,
|
||||
};
|
||||
|
||||
export enum ConnectionStatusType {
|
||||
Connect = "Connect",
|
||||
Connecting = "Connecting",
|
||||
Connected = "Connected",
|
||||
Failed = "Connection Failed",
|
||||
ReConnect = "Reconnect",
|
||||
}
|
||||
|
||||
export enum ConflictOperationType {
|
||||
Replace = "replace",
|
||||
Create = "create",
|
||||
Delete = "delete",
|
||||
}
|
||||
|
||||
export const EmulatorMasterKey =
|
||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||
|
||||
// A variable @MyVariable defined in Constants.less is accessible as StyleConstants.MyVariable
|
||||
|
||||
export enum AccountKind {
|
||||
DocumentDB = "DocumentDB",
|
||||
MongoDB = "MongoDB",
|
||||
Parse = "Parse",
|
||||
GlobalDocumentDB = "GlobalDocumentDB",
|
||||
Default = "DocumentDB",
|
||||
}
|
||||
|
||||
export enum MongoBackendEndpointType {
|
||||
local,
|
||||
remote,
|
||||
}
|
||||
@@ -3,8 +3,16 @@ import { resetConfigContext, updateConfigContext } from "../ConfigContext";
|
||||
import { DatabaseAccount } from "../Contracts/DataModels";
|
||||
import { Collection } from "../Contracts/ViewModels";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import { extractFeatures } from "../Platform/Hosted/extractFeatures";
|
||||
import { updateUserContext } from "../UserContext";
|
||||
import { deleteDocument, getEndpoint, queryDocuments, readDocument, updateDocument } from "./MongoProxyClient";
|
||||
import {
|
||||
deleteDocument,
|
||||
getEndpoint,
|
||||
getFeatureEndpointOrDefault,
|
||||
queryDocuments,
|
||||
readDocument,
|
||||
updateDocument,
|
||||
} from "./MongoProxyClient";
|
||||
|
||||
const databaseId = "testDB";
|
||||
|
||||
@@ -246,4 +254,31 @@ describe("MongoProxyClient", () => {
|
||||
expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/guest/mongo/explorer");
|
||||
});
|
||||
});
|
||||
describe("getFeatureEndpointOrDefault", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigContext();
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
});
|
||||
const params = new URLSearchParams({
|
||||
"feature.mongoProxyEndpoint": "https://localhost:12901",
|
||||
"feature.mongoProxyAPIs": "readDocument|createDocument",
|
||||
});
|
||||
const features = extractFeatures(params);
|
||||
updateUserContext({
|
||||
authType: AuthType.AAD,
|
||||
features: features,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns a local endpoint", () => {
|
||||
const endpoint = getFeatureEndpointOrDefault("readDocument");
|
||||
expect(endpoint).toEqual("https://localhost:12901/api/mongo/explorer");
|
||||
});
|
||||
|
||||
it("returns a production endpoint", () => {
|
||||
const endpoint = getFeatureEndpointOrDefault("deleteDocument");
|
||||
expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/mongo/explorer");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as DataModels from "../Contracts/DataModels";
|
||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||
import { Collection } from "../Contracts/ViewModels";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import { hasFlag } from "../Platform/Hosted/extractFeatures";
|
||||
import { userContext } from "../UserContext";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
|
||||
@@ -78,9 +79,8 @@ export function queryDocuments(
|
||||
: "",
|
||||
};
|
||||
|
||||
const endpoint = getEndpoint() || "";
|
||||
|
||||
const headers = {
|
||||
const endpoint = getFeatureEndpointOrDefault("resourcelist") || "";
|
||||
const headers: HeadersInit = {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[CosmosSDKConstants.HttpHeaders.IsQuery]: "true",
|
||||
@@ -141,7 +141,8 @@ export function readDocument(
|
||||
: "",
|
||||
};
|
||||
|
||||
const endpoint = getEndpoint();
|
||||
const endpoint = getFeatureEndpointOrDefault("readDocument");
|
||||
|
||||
return window
|
||||
.fetch(`${endpoint}?${queryString.stringify(params)}`, {
|
||||
method: "GET",
|
||||
@@ -181,7 +182,7 @@ export function createDocument(
|
||||
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
|
||||
};
|
||||
|
||||
const endpoint = getEndpoint();
|
||||
const endpoint = getFeatureEndpointOrDefault("createDocument");
|
||||
|
||||
return window
|
||||
.fetch(`${endpoint}/resourcelist?${queryString.stringify(params)}`, {
|
||||
@@ -225,7 +226,7 @@ export function updateDocument(
|
||||
? documentId.partitionKeyProperty
|
||||
: "",
|
||||
};
|
||||
const endpoint = getEndpoint();
|
||||
const endpoint = getFeatureEndpointOrDefault("updateDocument");
|
||||
|
||||
return window
|
||||
.fetch(`${endpoint}?${queryString.stringify(params)}`, {
|
||||
@@ -266,7 +267,7 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
|
||||
? documentId.partitionKeyProperty
|
||||
: "",
|
||||
};
|
||||
const endpoint = getEndpoint();
|
||||
const endpoint = getFeatureEndpointOrDefault("deleteDocument");
|
||||
|
||||
return window
|
||||
.fetch(`${endpoint}?${queryString.stringify(params)}`, {
|
||||
@@ -309,7 +310,7 @@ export function createMongoCollectionWithProxy(
|
||||
autoPilotThroughput: params.autoPilotMaxThroughput?.toString(),
|
||||
};
|
||||
|
||||
const endpoint = getEndpoint();
|
||||
const endpoint = getFeatureEndpointOrDefault("createCollectionWithProxy");
|
||||
|
||||
return window
|
||||
.fetch(
|
||||
@@ -333,8 +334,15 @@ export function createMongoCollectionWithProxy(
|
||||
});
|
||||
}
|
||||
|
||||
export function getEndpoint(): string {
|
||||
let url = (configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT) + "/api/mongo/explorer";
|
||||
export function getFeatureEndpointOrDefault(feature: string): string {
|
||||
return hasFlag(userContext.features.mongoProxyAPIs, feature)
|
||||
? getEndpoint(userContext.features.mongoProxyEndpoint)
|
||||
: getEndpoint();
|
||||
}
|
||||
|
||||
export function getEndpoint(customEndpoint?: string): string {
|
||||
let url = customEndpoint ? customEndpoint : configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
|
||||
url += "/api/mongo/explorer";
|
||||
|
||||
if (userContext.authType === AuthType.EncryptedToken) {
|
||||
url = url.replace("api/mongo", "api/guest/mongo");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
|
||||
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
|
||||
import refreshImg from "../../images/refresh-cosmos.svg";
|
||||
import { AuthType } from "../AuthType";
|
||||
@@ -6,6 +6,7 @@ import Explorer from "../Explorer/Explorer";
|
||||
import { ResourceTokenTree } from "../Explorer/Tree/ResourceTokenTree";
|
||||
import { ResourceTree } from "../Explorer/Tree/ResourceTree";
|
||||
import { userContext } from "../UserContext";
|
||||
import { NormalizedEventKey } from "./Constants";
|
||||
|
||||
export interface ResourceTreeContainerProps {
|
||||
toggleLeftPaneExpanded: () => void;
|
||||
@@ -18,6 +19,22 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
|
||||
isLeftPaneExpanded,
|
||||
container,
|
||||
}: ResourceTreeContainerProps): JSX.Element => {
|
||||
const focusButton = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
||||
|
||||
useEffect(() => {
|
||||
if (isLeftPaneExpanded) {
|
||||
if (focusButton.current) {
|
||||
focusButton.current.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const onKeyPressToggleLeftPaneExpanded = (event: React.KeyboardEvent) => {
|
||||
if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
|
||||
toggleLeftPaneExpanded();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div id="main" className={isLeftPaneExpanded ? "main" : "hiddenMain"}>
|
||||
{/* Collections Window - - Start */}
|
||||
@@ -43,9 +60,11 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
|
||||
id="expandToggleLeftPaneButton"
|
||||
role="button"
|
||||
onClick={toggleLeftPaneExpanded}
|
||||
onKeyPress={onKeyPressToggleLeftPaneExpanded}
|
||||
tabIndex={0}
|
||||
aria-label="Collapse Tree"
|
||||
title="Collapse Tree"
|
||||
ref={focusButton}
|
||||
>
|
||||
<img className="refreshcol1" src={arrowLeftImg} alt="Hide" />
|
||||
</span>
|
||||
|
||||
@@ -9,7 +9,7 @@ export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children }:
|
||||
return (
|
||||
<span>
|
||||
<TooltipHost content={children}>
|
||||
<Icon iconName="Info" ariaLabel="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" ariaLabel="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Icon, Label, Stack } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { NormalizedEventKey } from "../../../Common/Constants";
|
||||
import { accordionStackTokens } from "../Settings/SettingsRenderUtils";
|
||||
|
||||
export interface CollapsibleSectionProps {
|
||||
@@ -30,6 +31,13 @@ export class CollapsibleSectionComponent extends React.Component<CollapsibleSect
|
||||
}
|
||||
}
|
||||
|
||||
private onKeyPress = (event: React.KeyboardEvent) => {
|
||||
if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
|
||||
this.toggleCollapsed();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
@@ -39,6 +47,11 @@ export class CollapsibleSectionComponent extends React.Component<CollapsibleSect
|
||||
verticalAlign="center"
|
||||
tokens={accordionStackTokens}
|
||||
onClick={this.toggleCollapsed}
|
||||
onKeyPress={this.onKeyPress}
|
||||
tabIndex={0}
|
||||
aria-name="Advanced"
|
||||
role="button"
|
||||
aria-expanded={this.state.isExpanded}
|
||||
>
|
||||
<Icon iconName={this.state.isExpanded ? "ChevronDown" : "ChevronRight"} />
|
||||
<Label>{this.props.title}</Label>
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
exports[`CollapsibleSectionComponent renders 1`] = `
|
||||
<Fragment>
|
||||
<Stack
|
||||
aria-expanded={true}
|
||||
aria-name="Advanced"
|
||||
className="collapsibleSection"
|
||||
horizontal={true}
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 10,
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
Link,
|
||||
PrimaryButton,
|
||||
ProgressIndicator,
|
||||
Text,
|
||||
TextField,
|
||||
} from "@fluentui/react";
|
||||
import React, { FC } from "react";
|
||||
@@ -30,6 +31,7 @@ export interface DialogState {
|
||||
onOk: () => void,
|
||||
cancelLabel: string,
|
||||
onCancel: () => void,
|
||||
contentHtml?: JSX.Element,
|
||||
choiceGroupProps?: IChoiceGroupProps,
|
||||
textFieldProps?: TextFieldProps,
|
||||
primaryButtonDisabled?: boolean
|
||||
@@ -58,6 +60,7 @@ export const useDialog: UseStore<DialogState> = create((set, get) => ({
|
||||
onOk: () => void,
|
||||
cancelLabel: string,
|
||||
onCancel: () => void,
|
||||
contentHtml?: JSX.Element,
|
||||
choiceGroupProps?: IChoiceGroupProps,
|
||||
textFieldProps?: TextFieldProps,
|
||||
primaryButtonDisabled?: boolean
|
||||
@@ -76,6 +79,7 @@ export const useDialog: UseStore<DialogState> = create((set, get) => ({
|
||||
get().closeDialog();
|
||||
onCancel && onCancel();
|
||||
},
|
||||
contentHtml,
|
||||
choiceGroupProps,
|
||||
textFieldProps,
|
||||
primaryButtonDisabled,
|
||||
@@ -124,6 +128,7 @@ export interface DialogProps {
|
||||
type?: DialogType;
|
||||
showCloseButton?: boolean;
|
||||
onDismiss?: () => void;
|
||||
contentHtml?: JSX.Element;
|
||||
}
|
||||
|
||||
const DIALOG_MIN_WIDTH = "400px";
|
||||
@@ -150,6 +155,7 @@ export const Dialog: FC = () => {
|
||||
type,
|
||||
showCloseButton,
|
||||
onDismiss,
|
||||
contentHtml,
|
||||
} = props || {};
|
||||
|
||||
const dialogProps: IDialogProps = {
|
||||
@@ -191,6 +197,7 @@ export const Dialog: FC = () => {
|
||||
{linkProps.linkText} <FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
)}
|
||||
{contentHtml && <Text>{contentHtml}</Text>}
|
||||
{progressIndicatorProps && <ProgressIndicator {...progressIndicatorProps} />}
|
||||
<DialogFooter>
|
||||
<PrimaryButton {...primaryButtonProps} />
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
.input-type-head-text-field {
|
||||
width: 100%;
|
||||
}
|
||||
.input-query-form {
|
||||
width: 100%;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
|
||||
@@ -50,7 +50,7 @@ export interface InputTypeaheadComponentProps {
|
||||
* Override default jquery-typeahead options
|
||||
* WARNING: do not override input, source or callback to avoid breaking the components behavior.
|
||||
*/
|
||||
typeaheadOverrideOptions?: { dynamic: boolean };
|
||||
typeaheadOverrideOptions?: any;
|
||||
|
||||
/**
|
||||
* This function gets called when pressing ENTER on the input box
|
||||
@@ -132,7 +132,6 @@ export class InputTypeaheadComponent extends React.Component<
|
||||
|
||||
private filterChoiceByValue = (choices: Item[], searchKeyword: string): Item[] => {
|
||||
return choices.filter((choice) =>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
Object.keys(choice).some((key) => choice[key].toLowerCase().includes(searchKeyword.toLowerCase()))
|
||||
);
|
||||
@@ -161,9 +160,11 @@ export class InputTypeaheadComponent extends React.Component<
|
||||
return (
|
||||
<div className="input-typeahead-container">
|
||||
<Stack horizontal>
|
||||
<form aria-labelledby="input" className="input-query-form">
|
||||
<TextField
|
||||
multiline={useTextarea}
|
||||
rows={1}
|
||||
id="input"
|
||||
defaultValue={defaultValue}
|
||||
ariaLabel="Input query"
|
||||
placeholder={placeholder}
|
||||
@@ -173,6 +174,7 @@ export class InputTypeaheadComponent extends React.Component<
|
||||
onFocus={() => this.setState({ isSuggestionVisible: true })}
|
||||
onChange={(_event, newValue?: string) => this.handleChange(newValue)}
|
||||
/>
|
||||
</form>
|
||||
{this.props.showCancelButton && (
|
||||
<IconButton
|
||||
styles={iconButtonStyles}
|
||||
|
||||
@@ -6,10 +6,15 @@ exports[`inputTypeahead renders <input /> 1`] = `
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<form
|
||||
aria-labelledby="input"
|
||||
className="input-query-form"
|
||||
>
|
||||
<StyledTextFieldBase
|
||||
ariaLabel="Input query"
|
||||
className="input-type-head-text-field"
|
||||
id="input"
|
||||
multiline={false}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -17,6 +22,7 @@ exports[`inputTypeahead renders <input /> 1`] = `
|
||||
placeholder="placeholder"
|
||||
rows={1}
|
||||
/>
|
||||
</form>
|
||||
</Stack>
|
||||
</div>
|
||||
`;
|
||||
@@ -27,10 +33,15 @@ exports[`inputTypeahead renders <textarea /> 1`] = `
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<form
|
||||
aria-labelledby="input"
|
||||
className="input-query-form"
|
||||
>
|
||||
<StyledTextFieldBase
|
||||
ariaLabel="Input query"
|
||||
className="input-type-head-text-field"
|
||||
id="input"
|
||||
multiline={true}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -38,6 +49,7 @@ exports[`inputTypeahead renders <textarea /> 1`] = `
|
||||
placeholder="placeholder"
|
||||
rows={1}
|
||||
/>
|
||||
</form>
|
||||
</Stack>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -17,6 +17,8 @@ import Explorer from "../../Explorer";
|
||||
import { NotebookClientV2 } from "../../Notebook/NotebookClientV2";
|
||||
import { NotebookComponentBootstrapper } from "../../Notebook/NotebookComponent/NotebookComponentBootstrapper";
|
||||
import NotebookReadOnlyRenderer from "../../Notebook/NotebookRenderer/NotebookReadOnlyRenderer";
|
||||
import { NotebookUtil } from "../../Notebook/NotebookUtil";
|
||||
import { useNotebook } from "../../Notebook/useNotebook";
|
||||
import { Dialog, TextFieldProps, useDialog } from "../Dialog";
|
||||
import { NotebookMetadataComponent } from "./NotebookMetadataComponent";
|
||||
import "./NotebookViewerComponent.less";
|
||||
@@ -146,7 +148,9 @@ export class NotebookViewerComponent
|
||||
<NotebookMetadataComponent
|
||||
data={this.state.galleryItem}
|
||||
isFavorite={this.state.isFavorite}
|
||||
downloadButtonText={this.props.container && "Download to my notebooks"}
|
||||
downloadButtonText={
|
||||
this.props.container && NotebookUtil.getNotebookBtnTitle(useNotebook.getState().notebookFolderName)
|
||||
}
|
||||
onTagClick={this.props.onTagClick}
|
||||
onFavoriteClick={this.favoriteItem}
|
||||
onUnfavoriteClick={this.unfavoriteItem}
|
||||
@@ -264,6 +268,6 @@ export class NotebookViewerComponent
|
||||
};
|
||||
|
||||
private reportAbuse = (): void => {
|
||||
GalleryUtils.reportAbuse(this.props.junoClient, this.state.galleryItem, this, () => ({}));
|
||||
GalleryUtils.reportAbuse(this.props.junoClient, this.state.galleryItem, this, () => {});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
import * as React from "react";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import { AutopilotDocumentation, hoursInAMonth } from "../../../Shared/Constants";
|
||||
import { Urls, StyleConstants } from "../../../Common/Constants";
|
||||
import {
|
||||
getPriceCurrency,
|
||||
getCurrencySign,
|
||||
getAutoscalePricePerRu,
|
||||
getMultimasterMultiplier,
|
||||
computeRUUsagePriceHourly,
|
||||
getPricePerRu,
|
||||
estimatedCostDisclaimer,
|
||||
} from "../../../Utils/PricingUtils";
|
||||
import {
|
||||
ITextFieldStyles,
|
||||
DetailsList,
|
||||
DetailsListLayoutMode,
|
||||
DetailsRow,
|
||||
ICheckboxStyles,
|
||||
IStackProps,
|
||||
IStackTokens,
|
||||
IChoiceGroupStyles,
|
||||
Link,
|
||||
Text,
|
||||
IMessageBarStyles,
|
||||
ITextStyles,
|
||||
IDetailsRowStyles,
|
||||
IStackStyles,
|
||||
IColumn,
|
||||
IDetailsColumnStyles,
|
||||
IDetailsListStyles,
|
||||
IDetailsRowProps,
|
||||
IDetailsRowStyles,
|
||||
IDropdownStyles,
|
||||
IMessageBarStyles,
|
||||
ISeparatorStyles,
|
||||
IStackProps,
|
||||
IStackStyles,
|
||||
IStackTokens,
|
||||
ITextFieldStyles,
|
||||
ITextStyles,
|
||||
Link,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
Stack,
|
||||
SelectionMode,
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
DetailsList,
|
||||
IColumn,
|
||||
SelectionMode,
|
||||
DetailsListLayoutMode,
|
||||
IDetailsRowProps,
|
||||
DetailsRow,
|
||||
IDetailsColumnStyles,
|
||||
Stack,
|
||||
Text,
|
||||
} from "@fluentui/react";
|
||||
import { isDirtyTypes, isDirty } from "./SettingsUtils";
|
||||
import * as React from "react";
|
||||
import { StyleConstants, Urls } from "../../../Common/Constants";
|
||||
import { AutopilotDocumentation, hoursInAMonth } from "../../../Shared/Constants";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import {
|
||||
computeRUUsagePriceHourly,
|
||||
estimatedCostDisclaimer,
|
||||
getAutoscalePricePerRu,
|
||||
getCurrencySign,
|
||||
getMultimasterMultiplier,
|
||||
getPriceCurrency,
|
||||
getPricePerRu,
|
||||
} from "../../../Utils/PricingUtils";
|
||||
import { isDirty, isDirtyTypes } from "./SettingsUtils";
|
||||
|
||||
export interface EstimatedSpendingDisplayProps {
|
||||
costType: JSX.Element;
|
||||
@@ -65,7 +65,7 @@ export interface PriceBreakdown {
|
||||
currencySign: string;
|
||||
}
|
||||
|
||||
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14 } };
|
||||
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14, color: "windowtext" } };
|
||||
|
||||
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
|
||||
label: {
|
||||
@@ -223,14 +223,15 @@ export const getRuPriceBreakdown = (
|
||||
multimasterEnabled: isMultimaster,
|
||||
isAutoscale: isAutoscale,
|
||||
});
|
||||
const basePricePerRu: number = isAutoscale
|
||||
? getAutoscalePricePerRu(serverId, getMultimasterMultiplier(numberOfRegions, isMultimaster))
|
||||
: getPricePerRu(serverId);
|
||||
const multimasterMultiplier = getMultimasterMultiplier(numberOfRegions, isMultimaster);
|
||||
const pricePerRu: number = isAutoscale
|
||||
? getAutoscalePricePerRu(serverId, multimasterMultiplier)
|
||||
: getPricePerRu(serverId, multimasterMultiplier);
|
||||
return {
|
||||
hourlyPrice: hourlyPrice,
|
||||
hourlyPrice,
|
||||
dailyPrice: hourlyPrice * 24,
|
||||
monthlyPrice: hourlyPrice * hoursInAMonth,
|
||||
pricePerRu: basePricePerRu * getMultimasterMultiplier(numberOfRegions, isMultimaster),
|
||||
pricePerRu,
|
||||
currency: getPriceCurrency(serverId),
|
||||
currencySign: getCurrencySign(serverId),
|
||||
};
|
||||
@@ -271,7 +272,7 @@ export const manualToAutoscaleDisclaimerElement: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle} id="manualToAutoscaleDisclaimerElement">
|
||||
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings
|
||||
and storage of your resource. After autoscale has been enabled, you can change the max RU/s.{" "}
|
||||
<a href={Urls.autoscaleMigration}>Learn more</a>
|
||||
<Link href={Urls.autoscaleMigration}>Learn more</Link>
|
||||
</Text>
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ exports[`IndexingPolicyRefreshComponent renders 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -39,6 +40,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -73,6 +75,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -80,11 +83,11 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
>
|
||||
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s.
|
||||
|
||||
<a
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cosmos-autoscale-migration"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
</StyledMessageBar>
|
||||
<StyledChoiceGroup
|
||||
@@ -186,6 +189,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -460,6 +464,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ exports[`ScaleComponent renders with correct initial notification 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -412,6 +413,7 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -952,6 +954,7 @@ exports[`SubSettingsComponent renders 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -1228,6 +1231,7 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -166,16 +167,17 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
>
|
||||
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s.
|
||||
|
||||
<a
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cosmos-autoscale-migration"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
<Text
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -195,6 +197,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -207,6 +210,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -219,6 +223,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -230,6 +235,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -249,6 +255,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -264,6 +271,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -278,6 +286,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -291,6 +300,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -302,6 +312,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -321,6 +332,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -363,6 +375,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -378,6 +391,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
@@ -394,6 +408,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@import "../../../../less/Common/Constants";
|
||||
|
||||
.tabComponentContainer {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
|
||||
@@ -6,6 +6,7 @@ import { userContext } from "../../../../UserContext";
|
||||
import {
|
||||
calculateEstimateNumber,
|
||||
computeRUUsagePriceHourly,
|
||||
estimatedCostDisclaimer,
|
||||
getAutoscalePricePerRu,
|
||||
getCurrencySign,
|
||||
getMultimasterMultiplier,
|
||||
@@ -42,11 +43,9 @@ export const CostEstimateText: FunctionComponent<CostEstimateTextProps> = ({
|
||||
const currency: string = getPriceCurrency(serverId);
|
||||
const currencySign: string = getCurrencySign(serverId);
|
||||
const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
|
||||
const pricePerRu = isAutoscale
|
||||
? getAutoscalePricePerRu(serverId, multiplier) * multiplier
|
||||
: getPricePerRu(serverId) * multiplier;
|
||||
const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier);
|
||||
|
||||
const iconWithEstimatedCostDisclaimer: JSX.Element = <InfoTooltip>PricingUtils.estimatedCostDisclaimer</InfoTooltip>;
|
||||
const iconWithEstimatedCostDisclaimer: JSX.Element = <InfoTooltip>{estimatedCostDisclaimer}</InfoTooltip>;
|
||||
|
||||
if (isAutoscale) {
|
||||
return (
|
||||
|
||||
@@ -118,6 +118,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
<input
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Autoscale mode"
|
||||
aria-required={true}
|
||||
checked={isAutoscaleSelected}
|
||||
type="radio"
|
||||
role="radio"
|
||||
@@ -131,6 +132,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
aria-label="Manual mode"
|
||||
checked={!isAutoscaleSelected}
|
||||
type="radio"
|
||||
aria-required={true}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={(e) => handleOnChangeMode(e, "Manual")}
|
||||
|
||||
@@ -345,12 +345,14 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
ariaLabel="Info"
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
>
|
||||
<IconBase
|
||||
ariaLabel="Info"
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
styles={[Function]}
|
||||
tabIndex={0}
|
||||
theme={
|
||||
Object {
|
||||
"disableGlobalClassNames": false,
|
||||
@@ -630,6 +632,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
className="panelInfoIcon root-57"
|
||||
data-icon-name="Info"
|
||||
role="img"
|
||||
tabIndex={0}
|
||||
>
|
||||
|
||||
</i>
|
||||
@@ -651,6 +654,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
>
|
||||
<input
|
||||
aria-label="Autoscale mode"
|
||||
aria-required={true}
|
||||
checked={true}
|
||||
className="throughputInputRadioBtn"
|
||||
key=".0:$.0"
|
||||
@@ -667,6 +671,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
</span>
|
||||
<input
|
||||
aria-label="Manual mode"
|
||||
aria-required={true}
|
||||
checked={false}
|
||||
className="throughputInputRadioBtn"
|
||||
key=".0:$.2"
|
||||
@@ -1327,12 +1332,14 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
ariaLabel="Info"
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
>
|
||||
<IconBase
|
||||
ariaLabel="Info"
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
styles={[Function]}
|
||||
tabIndex={0}
|
||||
theme={
|
||||
Object {
|
||||
"disableGlobalClassNames": false,
|
||||
@@ -1612,6 +1619,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
className="panelInfoIcon root-57"
|
||||
data-icon-name="Info"
|
||||
role="img"
|
||||
tabIndex={0}
|
||||
>
|
||||
|
||||
</i>
|
||||
|
||||
@@ -243,6 +243,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
||||
<div ref={this.contextMenuRef} onContextMenu={this.onRightClick} onKeyPress={this.onMoreButtonKeyPress}>
|
||||
<IconButton
|
||||
name="More"
|
||||
title="More"
|
||||
className="treeMenuEllipsis"
|
||||
ariaLabel={menuItemLabel}
|
||||
menuIconProps={{
|
||||
|
||||
@@ -211,6 +211,7 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
|
||||
},
|
||||
}
|
||||
}
|
||||
title="More"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -423,6 +424,7 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
|
||||
},
|
||||
}
|
||||
}
|
||||
title="More"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Link } from "@fluentui/react/lib/Link";
|
||||
import * as ko from "knockout";
|
||||
import React from "react";
|
||||
import _ from "underscore";
|
||||
import { AuthType } from "../AuthType";
|
||||
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
||||
import * as Constants from "../Common/Constants";
|
||||
import { ConnectionStatusType } from "../Common/Constants";
|
||||
import { ConnectionStatusType, HttpStatusCodes, Notebook } from "../Common/Constants";
|
||||
import { readCollection } from "../Common/dataAccess/readCollection";
|
||||
import { readDatabases } from "../Common/dataAccess/readDatabases";
|
||||
import { isPublicInternetAccessAllowed } from "../Common/DatabaseAccountUtility";
|
||||
@@ -12,6 +13,7 @@ import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHand
|
||||
import * as Logger from "../Common/Logger";
|
||||
import { QueriesClient } from "../Common/QueriesClient";
|
||||
import * as DataModels from "../Contracts/DataModels";
|
||||
import { ContainerConnectionInfo } from "../Contracts/DataModels";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
|
||||
import { useSidePanel } from "../hooks/useSidePanel";
|
||||
@@ -164,23 +166,10 @@ export default class Explorer {
|
||||
|
||||
useNotebook.subscribe(
|
||||
async () => {
|
||||
if (!this.notebookManager) {
|
||||
const NotebookManager = await (
|
||||
await import(/* webpackChunkName: "NotebookManager" */ "./Notebook/NotebookManager")
|
||||
).default;
|
||||
this.notebookManager = new NotebookManager();
|
||||
this.notebookManager.initialize({
|
||||
container: this,
|
||||
resourceTree: this.resourceTree,
|
||||
refreshCommandBarButtons: () => this.refreshCommandBarButtons(),
|
||||
refreshNotebookList: () => this.refreshNotebookList(),
|
||||
});
|
||||
}
|
||||
|
||||
this.refreshCommandBarButtons();
|
||||
this.refreshNotebookList();
|
||||
this.initiateAndRefreshNotebookList();
|
||||
useNotebook.getState().setIsRefreshed(false);
|
||||
},
|
||||
(state) => state.isNotebookEnabled
|
||||
(state) => state.isNotebookEnabled || state.isRefreshed
|
||||
);
|
||||
|
||||
this.resourceTree = new ResourceTreeAdapter(this);
|
||||
@@ -213,6 +202,23 @@ export default class Explorer {
|
||||
this.refreshExplorer();
|
||||
}
|
||||
|
||||
public async initiateAndRefreshNotebookList(): Promise<void> {
|
||||
if (!this.notebookManager) {
|
||||
const NotebookManager = (await import(/* webpackChunkName: "NotebookManager" */ "./Notebook/NotebookManager"))
|
||||
.default;
|
||||
this.notebookManager = new NotebookManager();
|
||||
this.notebookManager.initialize({
|
||||
container: this,
|
||||
resourceTree: this.resourceTree,
|
||||
refreshCommandBarButtons: () => this.refreshCommandBarButtons(),
|
||||
refreshNotebookList: () => this.refreshNotebookList(),
|
||||
});
|
||||
}
|
||||
|
||||
this.refreshCommandBarButtons();
|
||||
this.refreshNotebookList();
|
||||
}
|
||||
|
||||
public openEnableSynapseLinkDialog(): void {
|
||||
const addSynapseLinkDialogProps: DialogProps = {
|
||||
linkProps: {
|
||||
@@ -346,30 +352,7 @@ export default class Explorer {
|
||||
return;
|
||||
}
|
||||
this._isInitializingNotebooks = true;
|
||||
if (userContext.features.phoenix) {
|
||||
const connectionStatus: DataModels.ContainerConnectionInfo = {
|
||||
status: ConnectionStatusType.Allocating,
|
||||
};
|
||||
useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||
const provisionData = {
|
||||
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
|
||||
resourceId: userContext.databaseAccount.id,
|
||||
dbAccountName: userContext.databaseAccount.name,
|
||||
aadToken: userContext.authorizationToken,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
subscriptionId: userContext.subscriptionId,
|
||||
};
|
||||
const connectionInfo = await this.phoenixClient.containerConnectionInfo(provisionData);
|
||||
if (connectionInfo.data && connectionInfo.data.notebookServerUrl) {
|
||||
connectionStatus.status = ConnectionStatusType.Connected;
|
||||
useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||
|
||||
useNotebook.getState().setNotebookServerInfo({
|
||||
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.data.notebookServerUrl,
|
||||
authToken: userContext.features.notebookServerToken || connectionInfo.data.notebookAuthToken,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (userContext.features.phoenix === false) {
|
||||
await this.ensureNotebookWorkspaceRunning();
|
||||
const connectionInfo = await listConnectionInfo(
|
||||
userContext.subscriptionId,
|
||||
@@ -384,13 +367,59 @@ export default class Explorer {
|
||||
});
|
||||
}
|
||||
|
||||
useNotebook.getState().initializeNotebooksTree(this.notebookManager);
|
||||
|
||||
this.refreshNotebookList();
|
||||
|
||||
this._isInitializingNotebooks = false;
|
||||
}
|
||||
|
||||
public async allocateContainer(): Promise<void> {
|
||||
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||
const isAllocating = useNotebook.getState().isAllocating;
|
||||
if (isAllocating === false && notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined) {
|
||||
const provisionData = {
|
||||
aadToken: userContext.authorizationToken,
|
||||
subscriptionId: userContext.subscriptionId,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
dbAccountName: userContext.databaseAccount.name,
|
||||
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
|
||||
};
|
||||
const connectionStatus: ContainerConnectionInfo = {
|
||||
status: ConnectionStatusType.Connecting,
|
||||
};
|
||||
useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||
try {
|
||||
useNotebook.getState().setIsAllocating(true);
|
||||
const connectionInfo = await this.phoenixClient.containerConnectionInfo(provisionData);
|
||||
if (
|
||||
connectionInfo.status === HttpStatusCodes.OK &&
|
||||
connectionInfo.data &&
|
||||
connectionInfo.data.notebookServerUrl
|
||||
) {
|
||||
connectionStatus.status = ConnectionStatusType.Connected;
|
||||
useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||
useNotebook.getState().setNotebookServerInfo({
|
||||
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.data.notebookServerUrl,
|
||||
authToken: userContext.features.notebookServerToken || connectionInfo.data.notebookAuthToken,
|
||||
});
|
||||
this.notebookManager?.notebookClient
|
||||
.getMemoryUsage()
|
||||
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo));
|
||||
useNotebook.getState().setIsAllocating(false);
|
||||
} else {
|
||||
connectionStatus.status = ConnectionStatusType.Failed;
|
||||
useNotebook.getState().resetConatinerConnection(connectionStatus);
|
||||
}
|
||||
} catch (error) {
|
||||
connectionStatus.status = ConnectionStatusType.Failed;
|
||||
useNotebook.getState().resetConatinerConnection(connectionStatus);
|
||||
throw error;
|
||||
}
|
||||
this.refreshNotebookList();
|
||||
|
||||
this._isInitializingNotebooks = false;
|
||||
}
|
||||
}
|
||||
|
||||
public resetNotebookWorkspace(): void {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookClient) {
|
||||
handleError(
|
||||
@@ -662,6 +691,9 @@ export default class Explorer {
|
||||
if (!notebookContentItem || !notebookContentItem.path) {
|
||||
throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`);
|
||||
}
|
||||
if (notebookContentItem.type === NotebookContentItemType.Notebook && NotebookUtil.isPhoenixEnabled()) {
|
||||
this.allocateContainer();
|
||||
}
|
||||
|
||||
const notebookTabs = useTabs
|
||||
.getState()
|
||||
@@ -883,9 +915,51 @@ export default class Explorer {
|
||||
handleError(error, "Explorer/onNewNotebookClicked");
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const isPhoenixEnabled = NotebookUtil.isPhoenixEnabled();
|
||||
if (isPhoenixEnabled) {
|
||||
if (isGithubTree) {
|
||||
async () => {
|
||||
await this.allocateContainer();
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.createNewNoteBook(parent, isGithubTree);
|
||||
};
|
||||
} else {
|
||||
useDialog.getState().showOkCancelModalDialog(
|
||||
Notebook.newNotebookModalTitle,
|
||||
undefined,
|
||||
"Create",
|
||||
async () => {
|
||||
await this.allocateContainer();
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.createNewNoteBook(parent, isGithubTree);
|
||||
},
|
||||
"Cancel",
|
||||
undefined,
|
||||
this.getNewNoteWarningText()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.createNewNoteBook(parent, isGithubTree);
|
||||
}
|
||||
}
|
||||
|
||||
private getNewNoteWarningText(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<p>{Notebook.newNotebookModalContent1}</p>
|
||||
<br />
|
||||
<p>
|
||||
{Notebook.newNotebookModalContent2}
|
||||
<Link href={Notebook.cosmosNotebookHomePageUrl} target="_blank">
|
||||
{Notebook.learnMore}
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private createNewNoteBook(parent?: NotebookContentItem, isGithubTree?: boolean): void {
|
||||
const clearInProgressMessage = logConsoleProgress(`Creating new notebook in ${parent.path}`);
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.CreateNewNotebook, {
|
||||
dataExplorerArea: Constants.Areas.Notebook,
|
||||
@@ -932,7 +1006,26 @@ export default class Explorer {
|
||||
await this.notebookManager?.notebookContentClient.updateItemChildrenInPlace(item);
|
||||
}
|
||||
|
||||
public openNotebookTerminal(kind: ViewModels.TerminalKind): void {
|
||||
public async openNotebookTerminal(kind: ViewModels.TerminalKind): Promise<void> {
|
||||
if (NotebookUtil.isPhoenixEnabled()) {
|
||||
await this.allocateContainer();
|
||||
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||
if (notebookServerInfo && notebookServerInfo.notebookServerEndpoint !== undefined) {
|
||||
this.connectToNotebookTerminal(kind);
|
||||
} else {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog(
|
||||
"Failed to Connect",
|
||||
"Failed to connect temporary workspace, this could happen because of network issue please refresh and try again."
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.connectToNotebookTerminal(kind);
|
||||
}
|
||||
}
|
||||
|
||||
private connectToNotebookTerminal(kind: ViewModels.TerminalKind): void {
|
||||
let title: string;
|
||||
|
||||
switch (kind) {
|
||||
@@ -983,7 +1076,7 @@ export default class Explorer {
|
||||
notebookUrl?: string,
|
||||
galleryItem?: IGalleryItem,
|
||||
isFavorite?: boolean
|
||||
) {
|
||||
): Promise<void> {
|
||||
const title = "Gallery";
|
||||
const GalleryTab = await (await import(/* webpackChunkName: "GalleryTab" */ "./Tabs/GalleryTab")).default;
|
||||
const galleryTab = useTabs
|
||||
@@ -1087,7 +1180,27 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
public openUploadFilePanel(parent?: NotebookContentItem): void {
|
||||
if (NotebookUtil.isPhoenixEnabled()) {
|
||||
useDialog.getState().showOkCancelModalDialog(
|
||||
Notebook.newNotebookUploadModalTitle,
|
||||
undefined,
|
||||
"Upload",
|
||||
async () => {
|
||||
await this.allocateContainer();
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.uploadFilePanel(parent);
|
||||
},
|
||||
"Cancel",
|
||||
undefined,
|
||||
this.getNewNoteWarningText()
|
||||
);
|
||||
} else {
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.uploadFilePanel(parent);
|
||||
}
|
||||
}
|
||||
|
||||
private uploadFilePanel(parent?: NotebookContentItem): void {
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
@@ -1096,6 +1209,24 @@ export default class Explorer {
|
||||
);
|
||||
}
|
||||
|
||||
public getDownloadModalConent(fileName: string): JSX.Element {
|
||||
if (NotebookUtil.isPhoenixEnabled()) {
|
||||
return (
|
||||
<>
|
||||
<p>{Notebook.galleryNotebookDownloadContent1}</p>
|
||||
<br />
|
||||
<p>
|
||||
{Notebook.galleryNotebookDownloadContent2}
|
||||
<Link href={Notebook.cosmosNotebookGitDocumentationUrl} target="_blank">
|
||||
{Notebook.learnMore}
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <p> Download {fileName} from gallery as a copy to your notebooks to run and/or edit the notebook. </p>;
|
||||
}
|
||||
|
||||
public async refreshExplorer(): Promise<void> {
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
? this.refreshDatabaseForResourceToken()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import * as Q from "q";
|
||||
import * as React from "react";
|
||||
@@ -294,8 +296,6 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
this.setGremlinParams();
|
||||
}
|
||||
|
||||
const selectedNode = this.state.highlightedNode;
|
||||
|
||||
props.onGraphAccessorCreated({
|
||||
applyFilter: this.submitQuery.bind(this),
|
||||
addVertex: this.addVertex.bind(this),
|
||||
@@ -303,7 +303,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
});
|
||||
} // constructor
|
||||
|
||||
public shareIGraphConfig(igraphConfig: IGraphConfig) {
|
||||
public shareIGraphConfig(igraphConfig: IGraphConfig): void {
|
||||
this.setState({
|
||||
igraphConfig: { ...igraphConfig },
|
||||
});
|
||||
@@ -422,7 +422,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
* Called from ko binding
|
||||
* @param id
|
||||
*/
|
||||
public selectNode(id: string) {
|
||||
public selectNode(id: string): void {
|
||||
if (!this.d3ForceGraph) {
|
||||
console.warn("Attempting to select node, but d3ForceGraph not initialized, yet.");
|
||||
return;
|
||||
@@ -431,7 +431,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
this.d3ForceGraph.selectNode(id);
|
||||
}
|
||||
|
||||
public deleteHighlightedNode() {
|
||||
public deleteHighlightedNode(): void {
|
||||
if (!this.state.highlightedNode) {
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, "No highlighted node to remove.");
|
||||
return;
|
||||
@@ -467,7 +467,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
* Is of type: {e: GremlinEdge, v: GremlinVertex}[]
|
||||
* @param data
|
||||
*/
|
||||
public static isEdgeVertexPairArray(data: any) {
|
||||
public static isEdgeVertexPairArray(data: any): boolean {
|
||||
if (!(data instanceof Array)) {
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Info, "Query result not an array", data);
|
||||
return false;
|
||||
@@ -477,13 +477,13 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
for (let i = 0; i < pairs.length; i++) {
|
||||
const item = pairs[i];
|
||||
if (
|
||||
!item.hasOwnProperty("e") ||
|
||||
!item.hasOwnProperty("v") ||
|
||||
!item["e"].hasOwnProperty("id") ||
|
||||
!item["e"].hasOwnProperty("type") ||
|
||||
!Object.prototype.hasOwnProperty.call(item, "e") ||
|
||||
!Object.prototype.hasOwnProperty.call(item, "v") ||
|
||||
!Object.prototype.hasOwnProperty.call(item["e"], "id") ||
|
||||
!Object.prototype.hasOwnProperty.call(item["e"], "type") ||
|
||||
item["e"].type !== "edge" ||
|
||||
!item["v"].hasOwnProperty("id") ||
|
||||
!item["v"].hasOwnProperty("type") ||
|
||||
!Object.prototype.hasOwnProperty.call(item["v"], "id") ||
|
||||
!Object.prototype.hasOwnProperty.call(item["e"], "type") ||
|
||||
item["v"].type !== "vertex"
|
||||
) {
|
||||
return false;
|
||||
@@ -514,7 +514,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
// Try hitting cache first
|
||||
const cache = outE ? this.outECache : this.inECache;
|
||||
const pairs = cache.retrieve(vertex.id, startIndex, pageSize);
|
||||
if (pairs != null && pairs.length === pageSize) {
|
||||
if (pairs !== null && pairs.length === pageSize) {
|
||||
const msg = `Retrieved ${pairs.length} ${outE ? "outE" : "inE"} edges from cache for vertex id: ${vertex.id}`;
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Info, msg);
|
||||
return Q.resolve(pairs);
|
||||
@@ -588,7 +588,6 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
vertex._outEAllLoaded &&
|
||||
vertex._inEAllLoaded
|
||||
) {
|
||||
console.info("No more edges to load for vertex " + vertex.id);
|
||||
updateGraphData();
|
||||
return Q.resolve(graphData);
|
||||
}
|
||||
@@ -668,7 +667,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
}
|
||||
);
|
||||
|
||||
return promise.then((nbPairsFetched: number) => {
|
||||
return promise.then(() => {
|
||||
if (offsetIndex >= GraphExplorer.LOAD_PAGE_SIZE || !vertex._outEAllLoaded || !vertex._inEAllLoaded) {
|
||||
vertex._pagination = {
|
||||
total:
|
||||
@@ -754,7 +753,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
* Create a new edge in docdb and update graph
|
||||
* @param e
|
||||
*/
|
||||
public createNewEdge(e: GraphNewEdgeData): Q.Promise<any> {
|
||||
public createNewEdge(e: GraphNewEdgeData): Q.Promise<unknown> {
|
||||
const q = `g.V('${GraphUtil.escapeSingleQuotes(e.inputOutV)}').addE('${GraphUtil.escapeSingleQuotes(
|
||||
e.label
|
||||
)}').To(g.V('${GraphUtil.escapeSingleQuotes(e.inputInV)}'))`;
|
||||
@@ -800,7 +799,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
* Manually update in-memory graph.
|
||||
* @param edgeId
|
||||
*/
|
||||
public removeEdge(edgeId: string): Q.Promise<any> {
|
||||
public removeEdge(edgeId: string): Q.Promise<unknown> {
|
||||
return this.submitToBackend(`g.E('${GraphUtil.escapeSingleQuotes(edgeId)}').drop()`).then(
|
||||
() => {
|
||||
const graphData = this.originalGraphData;
|
||||
@@ -829,7 +828,11 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
const vertices: any[] = data;
|
||||
if (vertices.length > 0) {
|
||||
const v0 = vertices[0];
|
||||
if (!v0.hasOwnProperty("id") || !v0.hasOwnProperty("type") || v0.type !== "vertex") {
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(v0, "id") ||
|
||||
!Object.prototype.hasOwnProperty.call(v0, "type") ||
|
||||
v0.type !== "vertex"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -837,7 +840,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
}
|
||||
|
||||
public processGremlinQueryResults(result: GremlinClient.GremlinRequestResult): void {
|
||||
const data = result.data as any;
|
||||
const data = result.data as GraphData.GremlinVertex[];
|
||||
this.setFilterQueryStatus(FilterQueryStatus.GraphEmptyResult);
|
||||
|
||||
if (data === null) {
|
||||
@@ -927,7 +930,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
throw { title: err };
|
||||
}
|
||||
|
||||
if (vertices == null || vertices.length < 1) {
|
||||
if (vertices === null || vertices.length < 1) {
|
||||
const err = "Failed to create vertex (no vertex in response)";
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, err, vertices);
|
||||
throw { title: err };
|
||||
@@ -1022,7 +1025,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
this.gremlinClient.destroy();
|
||||
}
|
||||
public componentDidMount(): void {
|
||||
if (this.props.onLoadStartKey != null && this.props.onLoadStartKey != undefined) {
|
||||
if (this.props.onLoadStartKey !== null && this.props.onLoadStartKey !== undefined) {
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.Tab,
|
||||
{
|
||||
@@ -1161,12 +1164,15 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
)}"`
|
||||
).then(
|
||||
(documents: DataModels.DocumentId[]) => {
|
||||
$.each(documents, (index: number, doc: any) => {
|
||||
$.each(
|
||||
documents,
|
||||
(index: number, doc: { _graph_icon_property_value: string; icon: string; format: string }) => {
|
||||
newIconsMap[doc["_graph_icon_property_value"]] = {
|
||||
data: doc["icon"],
|
||||
format: doc["format"],
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Update graph configuration
|
||||
this.setState({
|
||||
@@ -1223,7 +1229,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
const key = this.state.igraphConfig.nodeCaption;
|
||||
return $.map(
|
||||
this.state.rootMap,
|
||||
(value: any, index: number): LeftPane.CaptionId => {
|
||||
(value: any): LeftPane.CaptionId => {
|
||||
const result = GraphData.GraphData.getNodePropValue(value, key);
|
||||
return {
|
||||
caption: result !== undefined ? result : value.id,
|
||||
@@ -1237,7 +1243,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
* Selecting a root node means
|
||||
* @param node
|
||||
*/
|
||||
private selectRootNode(id: string): Q.Promise<any> {
|
||||
private selectRootNode(id: string): Q.Promise<unknown> {
|
||||
if (!this.d3ForceGraph) {
|
||||
console.warn("Attempting to reset zoom, but d3ForceGraph not initialized, yet.");
|
||||
} else {
|
||||
@@ -1282,7 +1288,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
this.collectNodeProperties(this.originalGraphData.vertices);
|
||||
this.updatePropertiesPane(id);
|
||||
},
|
||||
(reason: any) => {
|
||||
(reason: string) => {
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, `Failed to select root node. Reason:${reason}`);
|
||||
}
|
||||
);
|
||||
@@ -1349,10 +1355,10 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
private getPkIdFromVertex(v: GraphData.GremlinVertex): string {
|
||||
if (
|
||||
this.props.collectionPartitionKeyProperty &&
|
||||
v.hasOwnProperty("properties") &&
|
||||
v.properties.hasOwnProperty(this.props.collectionPartitionKeyProperty) &&
|
||||
Object.prototype.hasOwnProperty.call(v, "properties") &&
|
||||
Object.prototype.hasOwnProperty.call(v.properties, this.props.collectionPartitionKeyProperty) &&
|
||||
v.properties[this.props.collectionPartitionKeyProperty].length > 0 &&
|
||||
v.properties[this.props.collectionPartitionKeyProperty][0].hasOwnProperty("value")
|
||||
Object.prototype.hasOwnProperty.call(v.properties[this.props.collectionPartitionKeyProperty][0], "value")
|
||||
) {
|
||||
const pk = v.properties[this.props.collectionPartitionKeyProperty][0].value;
|
||||
return GraphExplorer.generatePkIdPair(pk, v.id);
|
||||
@@ -1370,8 +1376,8 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
private getPkIdFromNodeData(v: GraphHighlightedNodeData): string {
|
||||
if (
|
||||
this.props.collectionPartitionKeyProperty &&
|
||||
v.hasOwnProperty("properties") &&
|
||||
v.properties.hasOwnProperty(this.props.collectionPartitionKeyProperty)
|
||||
Object.prototype.hasOwnProperty.call(v, "properties") &&
|
||||
Object.prototype.hasOwnProperty.call(v.properties, this.props.collectionPartitionKeyProperty)
|
||||
) {
|
||||
const pk = v.properties[this.props.collectionPartitionKeyProperty];
|
||||
return GraphExplorer.generatePkIdPair(pk[0] as PartitionKeyValueType, v.id);
|
||||
@@ -1395,7 +1401,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
if (collectionPartitionKeyProperty && d.hasOwnProperty(collectionPartitionKeyProperty)) {
|
||||
if (collectionPartitionKeyProperty && Object.prototype.hasOwnProperty.call(d, collectionPartitionKeyProperty)) {
|
||||
let pk = (d as any)[collectionPartitionKeyProperty];
|
||||
if (typeof pk !== "string" && typeof pk !== "number" && typeof pk !== "boolean") {
|
||||
if (Array.isArray(pk) && pk.length > 0) {
|
||||
@@ -1439,7 +1445,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
caption: item.p,
|
||||
});
|
||||
} else {
|
||||
if (item.hasOwnProperty("p")) {
|
||||
if (Object.prototype.hasOwnProperty.call(item, "p")) {
|
||||
possibleVertices.push({
|
||||
value: item.id,
|
||||
caption: item.p[0]["_value"],
|
||||
@@ -1462,7 +1468,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
* @param addedEdges
|
||||
* @return promise when done
|
||||
*/
|
||||
private editGraphEdges(editedEdges: EditedEdges): Q.Promise<any> {
|
||||
private editGraphEdges(editedEdges: EditedEdges): Q.Promise<unknown> {
|
||||
const promises = [];
|
||||
// Drop edges
|
||||
for (let i = 0; i < editedEdges.droppedIds.length; i++) {
|
||||
@@ -1525,7 +1531,9 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
/**
|
||||
* For unit testing purposes
|
||||
*/
|
||||
public onGraphUpdated(timestamp: number): void {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public onGraphUpdated(_timestamp: number): void {}
|
||||
|
||||
/**
|
||||
* Get node properties for styling purposes. Result is the union of all properties of all nodes.
|
||||
@@ -1540,7 +1548,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
}
|
||||
}
|
||||
// Inspect properties
|
||||
if (item.hasOwnProperty("properties")) {
|
||||
if (Object.prototype.hasOwnProperty.call(item, "properties")) {
|
||||
// TODO This is DocDB-graph specific
|
||||
// Assume each property value is [{value:... }]
|
||||
for (const f in item.properties) {
|
||||
@@ -1660,7 +1668,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
|
||||
this.setState({
|
||||
possibleEdgeLabels: Object.keys(possibleEdgeLabels).map(
|
||||
(value: string, index: number, array: string[]): InputTypeaheadComponent.Item => {
|
||||
(value: string): InputTypeaheadComponent.Item => {
|
||||
return { caption: value, value: value };
|
||||
}
|
||||
),
|
||||
@@ -1685,7 +1693,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
if (this.originalGraphData.hasVertexId(updatedVertex.id)) {
|
||||
const currentVertex = this.originalGraphData.getVertexById(updatedVertex.id);
|
||||
// Copy updated properties
|
||||
if (currentVertex.hasOwnProperty("properties")) {
|
||||
if (Object.prototype.hasOwnProperty.call(currentVertex, "properties")) {
|
||||
delete currentVertex["properties"];
|
||||
}
|
||||
for (const p in updatedVertex) {
|
||||
@@ -1873,7 +1881,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
|
||||
promise
|
||||
.then((result: GremlinClient.GremlinRequestResult) => this.processGremlinQueryResults(result))
|
||||
.catch((error: any) => {
|
||||
.catch((error: Error) => {
|
||||
const errorMsg = `Failed to process query result: ${getErrorMessage(error)}`;
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
||||
this.setState({
|
||||
|
||||
@@ -58,7 +58,7 @@ export class LeftPaneComponent extends React.Component<LeftPaneComponentProps> {
|
||||
className={className}
|
||||
as="tr"
|
||||
aria-label={node.caption}
|
||||
onActivated={() => this.props.onRootNodeSelected(node.id)}
|
||||
onActivated={(e) => this.props.onRootNodeSelected(node.id)}
|
||||
key={node.id}
|
||||
>
|
||||
<td className="resultItem">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from "react";
|
||||
import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
|
||||
import CollapseArrowIcon from "../../../../images/Collapse_arrow_14x14.svg";
|
||||
import ExpandIcon from "../../../../images/Expand_14x14.svg";
|
||||
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
|
||||
import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
|
||||
|
||||
interface MiddlePaneComponentProps {
|
||||
isTabsContentExpanded: boolean;
|
||||
@@ -17,7 +17,14 @@ export class MiddlePaneComponent extends React.Component<MiddlePaneComponentProp
|
||||
<div className="middlePane">
|
||||
<div className="graphTitle">
|
||||
<span className="paneTitle">Graph</span>
|
||||
<span className="graphExpandCollapseBtn pull-right" onClick={this.props.toggleExpandGraph}>
|
||||
<span
|
||||
className="graphExpandCollapseBtn pull-right"
|
||||
onClick={this.props.toggleExpandGraph}
|
||||
role="button"
|
||||
aria-expanded={this.props.isTabsContentExpanded}
|
||||
aria-name="View graph in full screen"
|
||||
tabIndex={0}
|
||||
>
|
||||
<img
|
||||
src={this.props.isTabsContentExpanded ? CollapseArrowIcon : ExpandIcon}
|
||||
alt={this.props.isTabsContentExpanded ? "collapse graph content" : "expand graph content"}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
import * as Q from "q";
|
||||
import React from "react";
|
||||
import { GraphHighlightedNodeData, PossibleVertex } from "./GraphExplorer";
|
||||
import { Mode, NodePropertiesComponent, NodePropertiesComponentProps } from "./NodePropertiesComponent";
|
||||
import { NodePropertiesComponent, NodePropertiesComponentProps, Mode } from "./NodePropertiesComponent";
|
||||
import { GraphHighlightedNodeData, EditedProperties, EditedEdges, PossibleVertex } from "./GraphExplorer";
|
||||
|
||||
describe("Property pane", () => {
|
||||
const title = "My Title";
|
||||
@@ -37,25 +37,17 @@ describe("Property pane", () => {
|
||||
return {
|
||||
expandedTitle: title,
|
||||
isCollapsed: false,
|
||||
onCollapsedChanged: (): void => {
|
||||
("");
|
||||
},
|
||||
onCollapsedChanged: (newValue: boolean): void => {},
|
||||
node: highlightedNode,
|
||||
getPkIdFromNodeData: (): string => undefined,
|
||||
collectionPartitionKeyProperty: undefined,
|
||||
updateVertexProperties: (): Q.Promise<void> => Q.resolve(),
|
||||
selectNode: (): void => {
|
||||
("");
|
||||
},
|
||||
updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(undefined),
|
||||
possibleEdgeLabels: undefined,
|
||||
editGraphEdges: (): Q.Promise<unknown> => Q.resolve(),
|
||||
deleteHighlightedNode: (): void => {
|
||||
("");
|
||||
},
|
||||
onModeChanged: (): void => {
|
||||
("");
|
||||
},
|
||||
getPkIdFromNodeData: (v: GraphHighlightedNodeData): string => null,
|
||||
collectionPartitionKeyProperty: null,
|
||||
updateVertexProperties: (editedProperties: EditedProperties): Q.Promise<void> => Q.resolve(),
|
||||
selectNode: (id: string): void => {},
|
||||
updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(null),
|
||||
possibleEdgeLabels: null,
|
||||
editGraphEdges: (editedEdges: EditedEdges): Q.Promise<any> => Q.resolve(),
|
||||
deleteHighlightedNode: (): void => {},
|
||||
onModeChanged: (newMode: Mode): void => {},
|
||||
viewMode: Mode.READONLY_PROP,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ export interface NodePropertiesComponentProps {
|
||||
selectNode: (id: string) => void;
|
||||
updatePossibleVertices: () => Q.Promise<PossibleVertex[]>;
|
||||
possibleEdgeLabels: Item[];
|
||||
editGraphEdges: (editedEdges: EditedEdges) => Q.Promise<unknown>;
|
||||
editGraphEdges: (editedEdges: EditedEdges) => Q.Promise<any>;
|
||||
deleteHighlightedNode: () => void;
|
||||
onModeChanged: (newMode: Mode) => void;
|
||||
viewMode: Mode; // If viewMode is specified in parent, keep state in sync with it
|
||||
@@ -72,7 +72,7 @@ export class NodePropertiesComponent extends React.Component<
|
||||
super(props);
|
||||
this.state = {
|
||||
editedProperties: {
|
||||
pkId: undefined,
|
||||
pkId: null,
|
||||
readOnlyProperties: [],
|
||||
existingProperties: [],
|
||||
addedProperties: [],
|
||||
@@ -98,12 +98,15 @@ export class NodePropertiesComponent extends React.Component<
|
||||
};
|
||||
}
|
||||
|
||||
public static getDerivedStateFromProps(props: NodePropertiesComponentProps): Partial<NodePropertiesComponentState> {
|
||||
public static getDerivedStateFromProps(
|
||||
props: NodePropertiesComponentProps,
|
||||
state: NodePropertiesComponentState
|
||||
): Partial<NodePropertiesComponentState> {
|
||||
if (props.viewMode !== Mode.READONLY_PROP) {
|
||||
return { isDeleteConfirm: false };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
@@ -134,14 +137,11 @@ export class NodePropertiesComponent extends React.Component<
|
||||
* Get type option. Limit to string, number or boolean
|
||||
* @param value
|
||||
*/
|
||||
private static getTypeOption(
|
||||
value: null | string | number | undefined | boolean
|
||||
): ViewModels.InputPropertyValueTypeString {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
if (value === null) {
|
||||
private static getTypeOption(value: any): ViewModels.InputPropertyValueTypeString {
|
||||
if (value == null) {
|
||||
return "null";
|
||||
}
|
||||
const type = typeof value;
|
||||
let type = typeof value;
|
||||
switch (type) {
|
||||
case "number":
|
||||
case "boolean":
|
||||
@@ -173,10 +173,9 @@ export class NodePropertiesComponent extends React.Component<
|
||||
|
||||
const existingProps: ViewModels.InputProperty[] = [];
|
||||
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (this.props.node.hasOwnProperty("properties")) {
|
||||
const hProps = this.props.node["properties"];
|
||||
for (const p in hProps) {
|
||||
for (let p in hProps) {
|
||||
const propValues = hProps[p];
|
||||
(p === partitionKeyProperty ? readOnlyProps : existingProps).push({
|
||||
key: p,
|
||||
@@ -438,7 +437,7 @@ export class NodePropertiesComponent extends React.Component<
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
|
||||
.graphExplorerContainer {
|
||||
margin-left: @SmallSpace;
|
||||
overflow: hidden;
|
||||
height:100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: clip;
|
||||
height: 100%;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
|
||||
@@ -60,7 +61,7 @@
|
||||
.loadGraphHelper {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
height:50%;
|
||||
height: 50%;
|
||||
|
||||
img {
|
||||
width: @LoadGraphHelperWidth;
|
||||
@@ -72,7 +73,7 @@
|
||||
margin: @DefaultSpace;
|
||||
}
|
||||
|
||||
.loadGraphBtn{
|
||||
.loadGraphBtn {
|
||||
margin-top: @LargeSpace;
|
||||
}
|
||||
}
|
||||
@@ -94,17 +95,17 @@
|
||||
}
|
||||
|
||||
/* Override bootstrap's nav pills */
|
||||
.nav-pills>li.active>a,
|
||||
.nav-pills>li.active>a:focus,
|
||||
.nav-pills>li.active>a:hover {
|
||||
.nav-pills > li.active > a,
|
||||
.nav-pills > li.active > a:focus,
|
||||
.nav-pills > li.active > a:hover {
|
||||
background-color: @AccentMediumHigh;
|
||||
}
|
||||
|
||||
.nav-pills>li>a {
|
||||
.nav-pills > li > a {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.nav>li>a {
|
||||
.nav > li > a {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
@@ -114,16 +115,16 @@
|
||||
|
||||
.typeahead__cancel-button {
|
||||
top: 5px !important;
|
||||
right: .4em !important;
|
||||
right: 0.4em !important;
|
||||
}
|
||||
|
||||
|
||||
.queryMetricsSummary {
|
||||
margin: @LargeSpace @LargeSpace 0px @DefaultSpace;
|
||||
table-layout: fixed;
|
||||
display: block;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
min-height: 100px;
|
||||
|
||||
.queryMetricsSummaryHead {
|
||||
.flex-display();
|
||||
@@ -144,7 +145,8 @@
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
.flex-display();
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
padding: @DefaultSpace;
|
||||
|
||||
&:nth-child(1) {
|
||||
@@ -168,7 +170,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.graphContainer {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
@@ -176,7 +177,6 @@
|
||||
padding-top: (2 * @MediumSpace);
|
||||
.flex-display();
|
||||
|
||||
|
||||
.leftPane {
|
||||
width: 200px;
|
||||
padding: 0px 0px 0px @DefaultSpace;
|
||||
@@ -224,8 +224,8 @@
|
||||
.graphTitle {
|
||||
margin-top: @DefaultSpace;
|
||||
|
||||
.graphExpandCollapseBtn{
|
||||
padding:2px 6px 5px 6px;
|
||||
.graphExpandCollapseBtn {
|
||||
padding: 2px 6px 5px 6px;
|
||||
margin-right: @SmallSpace;
|
||||
|
||||
&:hover {
|
||||
@@ -236,7 +236,7 @@
|
||||
.active();
|
||||
}
|
||||
|
||||
img{
|
||||
img {
|
||||
.dataExplorerIcons();
|
||||
}
|
||||
}
|
||||
@@ -249,7 +249,7 @@
|
||||
height: 100%;
|
||||
|
||||
.graphModal {
|
||||
background-color: rgba(255, 255, 255, .7);
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
@@ -282,7 +282,6 @@
|
||||
min-height: 0px;
|
||||
margin-top: 0px;
|
||||
|
||||
|
||||
.rightPaneHeader {
|
||||
/* TODO: Hack to align the trashbox with the header for now. */
|
||||
margin-top: -28px;
|
||||
@@ -406,16 +405,16 @@
|
||||
|
||||
.propertyTableHeader {
|
||||
font-weight: bold;
|
||||
color: @DefaultFontColor ;
|
||||
color: @DefaultFontColor;
|
||||
border-bottom: 1px solid @BaseMediumHigh;
|
||||
}
|
||||
|
||||
/* Override autocomplete stuff */
|
||||
.typeahead__container {
|
||||
font: @mediumFontSize 'Segoe UI' !important;
|
||||
font: @mediumFontSize "Segoe UI" !important;
|
||||
|
||||
input {
|
||||
font: @mediumFontSize 'Segoe UI' !important;
|
||||
font: @mediumFontSize "Segoe UI" !important;
|
||||
padding: 0px @SmallSpace !important;
|
||||
}
|
||||
}
|
||||
@@ -426,12 +425,12 @@
|
||||
|
||||
.typeahead__cancel-button {
|
||||
top: 5px !important;
|
||||
right: .4em !important;
|
||||
right: 0.4em !important;
|
||||
}
|
||||
|
||||
.rightPaneAddPropertyBtn {
|
||||
padding: @DefaultSpace;
|
||||
margin-left: -8px;/* TODO: Hack to align the addproperty button with the section content for now. */
|
||||
margin-left: -8px; /* TODO: Hack to align the addproperty button with the section content for now. */
|
||||
|
||||
img {
|
||||
margin: 0px @SmallSpace @SmallSpace 0px;
|
||||
@@ -450,6 +449,8 @@
|
||||
.flex-direction();
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.paneTitle {
|
||||
@@ -586,7 +587,7 @@
|
||||
|
||||
.loadmore {
|
||||
use.loadMoreIcon {
|
||||
fill:@GraphNodeDefaultColor;
|
||||
fill: @GraphNodeDefaultColor;
|
||||
|
||||
&.active {
|
||||
fill: @AccentMediumHigh;
|
||||
@@ -608,8 +609,6 @@
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* scroll for leftpane, rightpane and newvertex pane*/
|
||||
|
||||
@@ -9,8 +9,10 @@ import create, { UseStore } from "zustand";
|
||||
import { StyleConstants } from "../../../Common/Constants";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { useTabs } from "../../../hooks/useTabs";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../../Explorer";
|
||||
import { NotebookUtil } from "../../Notebook/NotebookUtil";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
||||
import * as CommandBarUtil from "./CommandBarUtil";
|
||||
@@ -54,9 +56,15 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
|
||||
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||
|
||||
uiFabricControlButtons.unshift(CommandBarUtil.createConnectionStatus("connectionStatus"));
|
||||
if (NotebookUtil.isPhoenixEnabled()) {
|
||||
uiFabricControlButtons.unshift(CommandBarUtil.createConnectionStatus(container, "connectionStatus"));
|
||||
}
|
||||
|
||||
if (useTabs.getState().activeTab?.tabKind === ViewModels.CollectionTabKind.NotebookV2) {
|
||||
if (
|
||||
userContext.features.phoenix === false &&
|
||||
userContext.features.notebooksTemporarilyDown === false &&
|
||||
useTabs.getState().activeTab?.tabKind === ViewModels.CollectionTabKind.NotebookV2
|
||||
) {
|
||||
uiFabricControlButtons.unshift(CommandBarUtil.createMemoryTracker("memoryTracker"));
|
||||
}
|
||||
|
||||
|
||||
@@ -307,11 +307,18 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
|
||||
|
||||
function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "New " + getDatabaseName();
|
||||
const newDatabaseButton = document.activeElement as HTMLElement;
|
||||
|
||||
return {
|
||||
iconSrc: AddDatabaseIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () =>
|
||||
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />),
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"New " + getDatabaseName(),
|
||||
<AddDatabasePanel explorer={container} buttonElement={newDatabaseButton} />
|
||||
),
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
@@ -596,7 +603,7 @@ function createManageGitHubAccountButton(container: Explorer): CommandButtonComp
|
||||
return {
|
||||
iconSrc: GitHubIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () =>
|
||||
onCommandClick: () => {
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
@@ -606,7 +613,8 @@ function createManageGitHubAccountButton(container: Explorer): CommandButtonComp
|
||||
gitHubClientProp={container.notebookManager.gitHubClient}
|
||||
junoClientProp={junoClient}
|
||||
/>
|
||||
),
|
||||
);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
|
||||
@@ -13,6 +13,7 @@ import { StyleConstants } from "../../../Common/Constants";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../../Explorer";
|
||||
import { ConnectionStatus } from "./ConnectionStatusComponent";
|
||||
import { MemoryTracker } from "./MemoryTrackerComponent";
|
||||
|
||||
@@ -203,9 +204,9 @@ export const createMemoryTracker = (key: string): ICommandBarItemProps => {
|
||||
};
|
||||
};
|
||||
|
||||
export const createConnectionStatus = (key: string): ICommandBarItemProps => {
|
||||
export const createConnectionStatus = (container: Explorer, key: string): ICommandBarItemProps => {
|
||||
return {
|
||||
key,
|
||||
onRender: () => <ConnectionStatus />,
|
||||
onRender: () => <ConnectionStatus container={container} />,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,77 +3,182 @@
|
||||
.connectionStatusContainer {
|
||||
cursor: default;
|
||||
align-items: center;
|
||||
margin: 0 9px;
|
||||
border: 1px;
|
||||
min-height: 44px;
|
||||
|
||||
> span {
|
||||
padding-right: 12px;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-family: @DataExplorerFont;
|
||||
color: @DefaultFontColor;
|
||||
}
|
||||
&:focus{
|
||||
outline: 0px;
|
||||
}
|
||||
}
|
||||
.connectionStatusFailed{
|
||||
color: #bd1919;
|
||||
.commandReactBtn {
|
||||
&:hover {
|
||||
background-color: rgb(238, 247, 255);
|
||||
color: rgb(32, 31, 30);
|
||||
cursor: pointer;
|
||||
}
|
||||
&:focus{
|
||||
outline: 1px dashed #605e5c;
|
||||
}
|
||||
}
|
||||
.ring-container {
|
||||
.connectedReactBtn {
|
||||
&:hover {
|
||||
background-color: rgb(238, 247, 255);
|
||||
color: rgb(32, 31, 30);
|
||||
cursor: pointer;
|
||||
}
|
||||
&:focus{
|
||||
outline: 0px;
|
||||
}
|
||||
}
|
||||
.connectIcon{
|
||||
margin: 0px 4px;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
color: rgb(0, 120, 212);
|
||||
}
|
||||
.status {
|
||||
position: relative;
|
||||
}
|
||||
display: block;
|
||||
margin-right: 8px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
font-size: 9px!important;
|
||||
padding: 0px!important;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.ringringGreen {
|
||||
border: 3px solid green;
|
||||
border-radius: 30px;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
.status::before,
|
||||
.status::after {
|
||||
position: absolute;
|
||||
margin: .4285em 0em 0em 0.07477em;
|
||||
animation: pulsate 3s ease-out;
|
||||
animation-iteration-count: infinite;
|
||||
opacity: 0.0
|
||||
}
|
||||
.ringringYellow{
|
||||
border: 3px solid #ffbf00;
|
||||
border-radius: 30px;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
position: absolute;
|
||||
margin: .4285em 0em 0em 0.07477em;
|
||||
animation: pulsate 3s ease-out;
|
||||
animation-iteration-count: infinite;
|
||||
opacity: 0.0
|
||||
}
|
||||
.ringringRed{
|
||||
border: 3px solid #bd1919;
|
||||
border-radius: 30px;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
position: absolute;
|
||||
margin: .4285em 0em 0em 0.07477em;
|
||||
animation: pulsate 3s ease-out;
|
||||
animation-iteration-count: infinite;
|
||||
opacity: 0.0
|
||||
}
|
||||
@keyframes pulsate {
|
||||
0% {-webkit-transform: scale(0.1, 0.1); opacity: 0.0;}
|
||||
15% {opacity: 0.8;}
|
||||
25% {opacity: 0.6;}
|
||||
45% {opacity: 0.4;}
|
||||
70% {opacity: 0.3;}
|
||||
100% {-webkit-transform: scale(.7, .7); opacity: 0.1;}
|
||||
}
|
||||
.locationGreenDot{
|
||||
font-size: 20px;
|
||||
margin-right: 0.07em;
|
||||
color: green;
|
||||
}
|
||||
.locationYellowDot{
|
||||
font-size: 20px;
|
||||
margin-right: 0.07em;
|
||||
color: #ffbf00;
|
||||
}
|
||||
.locationRedDot{
|
||||
font-size: 20px;
|
||||
margin-right: 0.07em;
|
||||
color: #bd1919;
|
||||
}
|
||||
content: "";
|
||||
}
|
||||
|
||||
.status::before {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
background-color: rgba(#fff, 0.1);
|
||||
border-radius: 100%;
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale(0);
|
||||
}
|
||||
|
||||
.connected{
|
||||
background-color: green;
|
||||
box-shadow:
|
||||
0 0 0 0em rgba(green, 0),
|
||||
0em 0.05em 0.1em rgba(#000000, 0.2);
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
}
|
||||
.connecting{
|
||||
background-color:#ffbf00;
|
||||
box-shadow:
|
||||
0 0 0 0em rgba(#ffbf00, 0),
|
||||
0em 0.05em 0.1em rgba(#000000, 0.2);
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
}
|
||||
.failed{
|
||||
background-color:#bd1919;
|
||||
box-shadow:
|
||||
0 0 0 0em rgba(#bd1919, 0),
|
||||
0em 0.05em 0.1em rgba(#000000, 0.2);
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
}
|
||||
|
||||
.status.connecting.is-animating {
|
||||
animation: status-outer-connecting 3000ms infinite;
|
||||
}
|
||||
.status.failed.is-animating {
|
||||
animation: status-outer-failed 3000ms infinite;
|
||||
}
|
||||
.status.connected.is-animating {
|
||||
animation: status-outer-connected 3000ms infinite;
|
||||
}
|
||||
@keyframes status-outer-connected {
|
||||
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em #008000, 0em 0.05em 0.1em rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
20% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(0, 128, 0, 0.6), 0em 0.05em 0.1em rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
40% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(0, 128, 0, 0.5), 0em 0.05em 0.1em rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
60% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(0, 128, 0, 0.3), 0em 0.05em 0.1em rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
80% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0.5em rgba(0, 128, 0, 0.1), 0em 0.05em 0.1em rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
85% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(0, 128, 0, 0), 0em 0.05em 0.1em rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@keyframes status-outer-failed {
|
||||
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em #bd1919, 0em 0.05em 0.1em rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
20% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em #c52d2d, 0em 0.05em 0.1em rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
40% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em #b47b7b, 0em 0.05em 0.1em rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
60% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(0, 128, 0, 0.3), 0em 0.05em 0.1em rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
80% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0.5em rgba(0, 128, 0, 0.1), 0em 0.05em 0.1em rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
85% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(0, 128, 0, 0), 0em 0.05em 0.1em rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@keyframes status-outer-connecting {
|
||||
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em #ffbf00, 0em 0.05em 0.1em rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
20% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em #f0dfad, 0em 0.05em 0.1em rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
40% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(198, 243, 198, 0.5), 0em 0.05em 0.1em rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
60% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(213, 241, 213, 0.3), 0em 0.05em 0.1em rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
80% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0.5em rgba(0, 128, 0, 0.1), 0em 0.05em 0.1em rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
85% {
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
box-shadow: 0 0 0 0em rgba(0, 128, 0, 0), 0em 0.05em 0.1em rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
import { Icon, ProgressIndicator, Spinner, SpinnerSize, Stack, TooltipHost } from "@fluentui/react";
|
||||
import { Icon, ProgressIndicator, Stack, TooltipHost } from "@fluentui/react";
|
||||
import { ActionButton } from "@fluentui/react/lib/Button";
|
||||
import * as React from "react";
|
||||
import { ConnectionStatusType } from "../../../Common/Constants";
|
||||
import "../../../../less/hostedexplorer.less";
|
||||
import { ConnectionStatusType, Notebook } from "../../../Common/Constants";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useNotebook } from "../../Notebook/useNotebook";
|
||||
import "../CommandBar/ConnectionStatusComponent.less";
|
||||
|
||||
export const ConnectionStatus: React.FC = (): JSX.Element => {
|
||||
interface Props {
|
||||
container: Explorer;
|
||||
}
|
||||
export const ConnectionStatus: React.FC<Props> = ({ container }: Props): JSX.Element => {
|
||||
const [second, setSecond] = React.useState("00");
|
||||
const [minute, setMinute] = React.useState("00");
|
||||
const [isActive, setIsActive] = React.useState(false);
|
||||
const [counter, setCounter] = React.useState(0);
|
||||
const [statusColor, setStatusColor] = React.useState("locationYellowDot");
|
||||
const [statusColorAnimation, setStatusColorAnimation] = React.useState("ringringYellow");
|
||||
const toolTipContent = "Hosted runtime status.";
|
||||
const [statusColor, setStatusColor] = React.useState("");
|
||||
const [toolTipContent, setToolTipContent] = React.useState("Connect to temporary workspace.");
|
||||
React.useEffect(() => {
|
||||
let intervalId: NodeJS.Timeout;
|
||||
|
||||
@@ -39,39 +43,65 @@ export const ConnectionStatus: React.FC = (): JSX.Element => {
|
||||
};
|
||||
|
||||
const connectionInfo = useNotebook((state) => state.connectionInfo);
|
||||
if (!connectionInfo) {
|
||||
return (
|
||||
<Stack className="connectionStatusContainer" horizontal>
|
||||
<span>Connecting</span>
|
||||
<Spinner size={SpinnerSize.medium} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
if (connectionInfo && connectionInfo.status === ConnectionStatusType.Allocating && isActive === false) {
|
||||
setIsActive(true);
|
||||
} else if (connectionInfo && connectionInfo.status === ConnectionStatusType.Connected && isActive === true) {
|
||||
stopTimer();
|
||||
setStatusColor("locationGreenDot");
|
||||
setStatusColorAnimation("ringringGreen");
|
||||
} else if (connectionInfo && connectionInfo.status === ConnectionStatusType.Failed && isActive === true) {
|
||||
stopTimer();
|
||||
setStatusColor("locationRedDot");
|
||||
setStatusColorAnimation("ringringRed");
|
||||
}
|
||||
const memoryUsageInfo = useNotebook((state) => state.memoryUsageInfo);
|
||||
|
||||
const totalGB = memoryUsageInfo ? memoryUsageInfo.totalKB / Notebook.memoryGuageToGB : 0;
|
||||
const usedGB = totalGB > 0 ? totalGB - memoryUsageInfo.freeKB / Notebook.memoryGuageToGB : 0;
|
||||
|
||||
if (
|
||||
connectionInfo &&
|
||||
(connectionInfo.status === ConnectionStatusType.Connect || connectionInfo.status === ConnectionStatusType.ReConnect)
|
||||
) {
|
||||
return (
|
||||
<ActionButton className="commandReactBtn" onClick={() => container.allocateContainer()}>
|
||||
<TooltipHost content={toolTipContent}>
|
||||
<Stack className="connectionStatusContainer" horizontal>
|
||||
<div className="ring-container">
|
||||
<div className={statusColorAnimation}></div>
|
||||
<Icon iconName="LocationDot" className={statusColor} />
|
||||
</div>
|
||||
<Icon iconName="ConnectVirtualMachine" className="connectIcon" />
|
||||
<span>{connectionInfo.status}</span>
|
||||
</Stack>
|
||||
</TooltipHost>
|
||||
</ActionButton>
|
||||
);
|
||||
}
|
||||
|
||||
if (connectionInfo && connectionInfo.status === ConnectionStatusType.Connecting && isActive === false) {
|
||||
setIsActive(true);
|
||||
setStatusColor("status connecting is-animating");
|
||||
setToolTipContent("Connecting to temporary workspace.");
|
||||
} else if (connectionInfo && connectionInfo.status === ConnectionStatusType.Connected && isActive === true) {
|
||||
stopTimer();
|
||||
setStatusColor("status connected is-animating");
|
||||
setToolTipContent("Connected to temporary workspace.");
|
||||
} else if (connectionInfo && connectionInfo.status === ConnectionStatusType.Failed && isActive === true) {
|
||||
stopTimer();
|
||||
setStatusColor("status failed is-animating");
|
||||
setToolTipContent("Click here to Reconnect to temporary workspace.");
|
||||
}
|
||||
return (
|
||||
<ActionButton
|
||||
className={connectionInfo.status === ConnectionStatusType.Failed ? "commandReactBtn" : "connectedReactBtn"}
|
||||
onClick={(e: React.MouseEvent<HTMLSpanElement>) =>
|
||||
connectionInfo.status === ConnectionStatusType.Failed ? container.allocateContainer() : e.preventDefault()
|
||||
}
|
||||
>
|
||||
<TooltipHost content={toolTipContent}>
|
||||
<Stack className="connectionStatusContainer" horizontal>
|
||||
<i className={statusColor}></i>
|
||||
<span className={connectionInfo.status === ConnectionStatusType.Failed ? "connectionStatusFailed" : ""}>
|
||||
{connectionInfo.status}
|
||||
</span>
|
||||
{connectionInfo.status === ConnectionStatusType.Allocating && isActive && (
|
||||
{connectionInfo.status === ConnectionStatusType.Connecting && isActive && (
|
||||
<ProgressIndicator description={minute + ":" + second} />
|
||||
)}
|
||||
{connectionInfo.status === ConnectionStatusType.Connected && !isActive && (
|
||||
<ProgressIndicator
|
||||
className={usedGB / totalGB > 0.8 ? "lowMemory" : ""}
|
||||
description={usedGB.toFixed(1) + " of " + totalGB.toFixed(1) + " GB"}
|
||||
percentComplete={usedGB / totalGB}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</TooltipHost>
|
||||
</ActionButton>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -129,7 +129,7 @@ export class NotificationConsoleComponent extends React.Component<
|
||||
className="expandCollapseButton"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={"console button" + (this.props.isConsoleExpanded ? " collapsed" : " expanded")}
|
||||
aria-label={"console button" + (this.props.isConsoleExpanded ? " expanded" : " collapsed")}
|
||||
aria-expanded={!this.props.isConsoleExpanded}
|
||||
>
|
||||
<img
|
||||
@@ -205,7 +205,9 @@ export class NotificationConsoleComponent extends React.Component<
|
||||
{item.type === ConsoleDataType.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />}
|
||||
{item.type === ConsoleDataType.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />}
|
||||
<span className="date">{item.date}</span>
|
||||
<span className="message">{item.message}</span>
|
||||
<span className="message" role="alert" aria-live="assertive">
|
||||
{item.message}
|
||||
</span>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-expanded={true}
|
||||
aria-label="console button expanded"
|
||||
aria-label="console button collapsed"
|
||||
className="expandCollapseButton"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
@@ -236,7 +236,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-expanded={true}
|
||||
aria-label="console button expanded"
|
||||
aria-label="console button collapsed"
|
||||
className="expandCollapseButton"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
@@ -340,7 +340,9 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
||||
date
|
||||
</span>
|
||||
<span
|
||||
aria-live="assertive"
|
||||
className="message"
|
||||
role="alert"
|
||||
>
|
||||
message
|
||||
</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { CellId, CellType, ImmutableNotebook } from "@nteract/commutable";
|
||||
// Vendor modules
|
||||
import {
|
||||
@@ -30,6 +31,19 @@ export interface NotebookComponentBootstrapperOptions {
|
||||
contentRef: ContentRef;
|
||||
}
|
||||
|
||||
interface IWrapModel {
|
||||
name: string;
|
||||
path: string;
|
||||
last_modified: Date;
|
||||
created: string;
|
||||
content: unknown;
|
||||
format: string;
|
||||
mimetype: unknown;
|
||||
size: number;
|
||||
writeable: boolean;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export class NotebookComponentBootstrapper {
|
||||
public contentRef: ContentRef;
|
||||
protected renderExtraComponent: () => JSX.Element;
|
||||
@@ -41,7 +55,7 @@ export class NotebookComponentBootstrapper {
|
||||
this.contentRef = options.contentRef;
|
||||
}
|
||||
|
||||
protected static wrapModelIntoContent(name: string, path: string, content: any) {
|
||||
protected static wrapModelIntoContent(name: string, path: string, content: unknown): IWrapModel {
|
||||
return {
|
||||
name,
|
||||
path,
|
||||
@@ -49,7 +63,7 @@ export class NotebookComponentBootstrapper {
|
||||
created: "",
|
||||
content,
|
||||
format: "json",
|
||||
mimetype: null as any,
|
||||
mimetype: undefined,
|
||||
size: 0,
|
||||
writeable: false,
|
||||
type: "notebook",
|
||||
@@ -85,7 +99,7 @@ export class NotebookComponentBootstrapper {
|
||||
};
|
||||
}
|
||||
|
||||
public setContent(name: string, content: any): void {
|
||||
public setContent(name: string, content: unknown): void {
|
||||
this.getStore().dispatch(
|
||||
actions.fetchContentFulfilled({
|
||||
filepath: undefined,
|
||||
@@ -270,7 +284,6 @@ export class NotebookComponentBootstrapper {
|
||||
public isContentDirty(): boolean {
|
||||
const content = selectors.content(this.getStore().getState(), { contentRef: this.contentRef });
|
||||
if (!content) {
|
||||
console.log("No error");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
* Notebook container related stuff
|
||||
*/
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { ConnectionStatusType } from "../../Common/Constants";
|
||||
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||
import * as Logger from "../../Common/Logger";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { ContainerConnectionInfo } from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { createOrUpdate, destroy } from "../../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { NotebookUtil } from "./NotebookUtil";
|
||||
import { useNotebook } from "./useNotebook";
|
||||
|
||||
export class NotebookContainerClient {
|
||||
@@ -42,7 +45,7 @@ export class NotebookContainerClient {
|
||||
}, delayMs);
|
||||
}
|
||||
|
||||
private async getMemoryUsage(): Promise<DataModels.MemoryUsageInfo> {
|
||||
public async getMemoryUsage(): Promise<DataModels.MemoryUsageInfo> {
|
||||
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||
if (!notebookServerInfo || !notebookServerInfo.notebookServerEndpoint) {
|
||||
const error = "No server endpoint detected";
|
||||
@@ -75,6 +78,12 @@ export class NotebookContainerClient {
|
||||
freeKB: memoryUsageInfo.free,
|
||||
};
|
||||
}
|
||||
} else if (NotebookUtil.isPhoenixEnabled()) {
|
||||
const connectionStatus: ContainerConnectionInfo = {
|
||||
status: ConnectionStatusType.ReConnect,
|
||||
};
|
||||
useNotebook.getState().resetConatinerConnection(connectionStatus);
|
||||
useNotebook.getState().setIsRefreshed(true);
|
||||
}
|
||||
return undefined;
|
||||
} catch (error) {
|
||||
@@ -84,6 +93,13 @@ export class NotebookContainerClient {
|
||||
"Connection lost with Notebook server. Attempting to reconnect..."
|
||||
);
|
||||
}
|
||||
if (NotebookUtil.isPhoenixEnabled()) {
|
||||
const connectionStatus: ContainerConnectionInfo = {
|
||||
status: ConnectionStatusType.Failed,
|
||||
};
|
||||
useNotebook.getState().resetConatinerConnection(connectionStatus);
|
||||
useNotebook.getState().setIsRefreshed(true);
|
||||
}
|
||||
this.onConnectionLost();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -212,6 +212,7 @@ export default class NotebookManager {
|
||||
"Cancel",
|
||||
() => reject(new Error("Commit dialog canceled")),
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
label: "Commit message",
|
||||
autoAdjustHeight: true,
|
||||
|
||||
@@ -16,9 +16,10 @@ import "./NotebookReadOnlyRenderer.less";
|
||||
import SandboxOutputs from "./outputs/SandboxOutputs";
|
||||
|
||||
export interface NotebookRendererProps {
|
||||
contentRef: any;
|
||||
contentRef: ContentRef;
|
||||
hideInputs?: boolean;
|
||||
hidePrompts?: boolean;
|
||||
addTransform: (component: React.ComponentType & { MIMETYPE: string }) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +28,7 @@ export interface NotebookRendererProps {
|
||||
class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
||||
componentDidMount() {
|
||||
if (!userContext.features.sandboxNotebookOutputs) {
|
||||
loadTransform(this.props as any);
|
||||
loadTransform(this.props as NotebookRendererProps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +60,7 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
||||
<div className="NotebookReadOnlyRender">
|
||||
<Cells contentRef={this.props.contentRef}>
|
||||
{{
|
||||
code: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => (
|
||||
code: ({ id, contentRef }: { id: string; contentRef: ContentRef }) => (
|
||||
<CodeCell id={id} contentRef={contentRef}>
|
||||
{{
|
||||
prompt: (props: { id: string; contentRef: string }) => this.renderPrompt(props.id, props.contentRef),
|
||||
@@ -73,14 +74,14 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
||||
}}
|
||||
</CodeCell>
|
||||
),
|
||||
markdown: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => (
|
||||
markdown: ({ id, contentRef }: { id: string; contentRef: ContentRef }) => (
|
||||
<MarkdownCell id={id} contentRef={contentRef} cell_type="markdown">
|
||||
{{
|
||||
editor: {},
|
||||
}}
|
||||
</MarkdownCell>
|
||||
),
|
||||
raw: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => (
|
||||
raw: ({ id, contentRef }: { id: string; contentRef: ContentRef }) => (
|
||||
<RawCell id={id} contentRef={contentRef} cell_type="raw">
|
||||
{{
|
||||
editor: {
|
||||
@@ -98,6 +99,7 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const makeMapDispatchToProps = (initialDispatch: Dispatch, initialProps: NotebookRendererProps) => {
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => {
|
||||
return {
|
||||
@@ -114,4 +116,4 @@ const makeMapDispatchToProps = (initialDispatch: Dispatch, initialProps: Noteboo
|
||||
return mapDispatchToProps;
|
||||
};
|
||||
|
||||
export default connect(null, makeMapDispatchToProps)(NotebookReadOnlyRenderer);
|
||||
export default connect(undefined, makeMapDispatchToProps)(NotebookReadOnlyRenderer);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { AppState, selectors } from "@nteract/core";
|
||||
import domtoimage from "dom-to-image";
|
||||
import Html2Canvas from "html2canvas";
|
||||
import path from "path";
|
||||
import { userContext } from "../../UserContext";
|
||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||
import * as StringUtils from "../../Utils/StringUtils";
|
||||
import { SnapshotFragment } from "./NotebookComponent/types";
|
||||
@@ -328,4 +329,16 @@ export class NotebookUtil {
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
public static getNotebookBtnTitle(fileName: string): string {
|
||||
if (this.isPhoenixEnabled()) {
|
||||
return `Download to ${fileName}`;
|
||||
} else {
|
||||
return `Download to my notebooks`;
|
||||
}
|
||||
}
|
||||
|
||||
public static isPhoenixEnabled(): boolean {
|
||||
return userContext.features.notebooksTemporarilyDown === false && userContext.features.phoenix === true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ import { cloneDeep } from "lodash";
|
||||
import create, { UseStore } from "zustand";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { ConnectionStatusType } from "../../Common/Constants";
|
||||
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||
import * as Logger from "../../Common/Logger";
|
||||
import { configContext } from "../../ConfigContext";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { ContainerConnectionInfo } from "../../Contracts/DataModels";
|
||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
@@ -14,6 +16,7 @@ import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
|
||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
|
||||
import NotebookManager from "./NotebookManager";
|
||||
import { NotebookUtil } from "./NotebookUtil";
|
||||
|
||||
interface NotebookState {
|
||||
isNotebookEnabled: boolean;
|
||||
@@ -28,8 +31,10 @@ interface NotebookState {
|
||||
myNotebooksContentRoot: NotebookContentItem;
|
||||
gitHubNotebooksContentRoot: NotebookContentItem;
|
||||
galleryContentRoot: NotebookContentItem;
|
||||
connectionInfo: DataModels.ContainerConnectionInfo;
|
||||
NotebookFolderName: string;
|
||||
connectionInfo: ContainerConnectionInfo;
|
||||
notebookFolderName: string;
|
||||
isAllocating: boolean;
|
||||
isRefreshed: boolean;
|
||||
setIsNotebookEnabled: (isNotebookEnabled: boolean) => void;
|
||||
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => void;
|
||||
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => void;
|
||||
@@ -38,6 +43,7 @@ interface NotebookState {
|
||||
setMemoryUsageInfo: (memoryUsageInfo: DataModels.MemoryUsageInfo) => void;
|
||||
setIsShellEnabled: (isShellEnabled: boolean) => void;
|
||||
setNotebookBasePath: (notebookBasePath: string) => void;
|
||||
setNotebookFolderName: (notebookFolderName: string) => void;
|
||||
refreshNotebooksEnabledStateForAccount: () => Promise<void>;
|
||||
findItem: (root: NotebookContentItem, item: NotebookContentItem) => NotebookContentItem;
|
||||
insertNotebookItem: (parent: NotebookContentItem, item: NotebookContentItem, isGithubTree?: boolean) => void;
|
||||
@@ -45,7 +51,10 @@ interface NotebookState {
|
||||
deleteNotebookItem: (item: NotebookContentItem, isGithubTree?: boolean) => void;
|
||||
initializeNotebooksTree: (notebookManager: NotebookManager) => Promise<void>;
|
||||
initializeGitHubRepos: (pinnedRepos: IPinnedRepo[]) => void;
|
||||
setConnectionInfo: (connectionInfo: DataModels.ContainerConnectionInfo) => void;
|
||||
setConnectionInfo: (connectionInfo: ContainerConnectionInfo) => void;
|
||||
setIsAllocating: (isAllocating: boolean) => void;
|
||||
resetConatinerConnection: (connectionStatus: ContainerConnectionInfo) => void;
|
||||
setIsRefreshed: (isAllocating: boolean) => void;
|
||||
}
|
||||
|
||||
export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
@@ -68,8 +77,12 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
myNotebooksContentRoot: undefined,
|
||||
gitHubNotebooksContentRoot: undefined,
|
||||
galleryContentRoot: undefined,
|
||||
connectionInfo: undefined,
|
||||
NotebookFolderName: userContext.features.phoenix ? "My Notebooks Scratch" : "My Notebooks",
|
||||
connectionInfo: {
|
||||
status: ConnectionStatusType.Connect,
|
||||
},
|
||||
notebookFolderName: undefined,
|
||||
isAllocating: false,
|
||||
isRefreshed: false,
|
||||
setIsNotebookEnabled: (isNotebookEnabled: boolean) => set({ isNotebookEnabled }),
|
||||
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => set({ isNotebooksEnabledForAccount }),
|
||||
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) =>
|
||||
@@ -80,6 +93,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
setMemoryUsageInfo: (memoryUsageInfo: DataModels.MemoryUsageInfo) => set({ memoryUsageInfo }),
|
||||
setIsShellEnabled: (isShellEnabled: boolean) => set({ isShellEnabled }),
|
||||
setNotebookBasePath: (notebookBasePath: string) => set({ notebookBasePath }),
|
||||
setNotebookFolderName: (notebookFolderName: string) => set({ notebookFolderName }),
|
||||
refreshNotebooksEnabledStateForAccount: async (): Promise<void> => {
|
||||
const { databaseAccount, authType } = userContext;
|
||||
if (
|
||||
@@ -173,8 +187,10 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
isGithubTree ? set({ gitHubNotebooksContentRoot: root }) : set({ myNotebooksContentRoot: root });
|
||||
},
|
||||
initializeNotebooksTree: async (notebookManager: NotebookManager): Promise<void> => {
|
||||
const notebookFolderName = NotebookUtil.isPhoenixEnabled() === true ? "Temporary Notebooks" : "My Notebooks";
|
||||
set({ notebookFolderName });
|
||||
const myNotebooksContentRoot = {
|
||||
name: get().NotebookFolderName,
|
||||
name: get().notebookFolderName,
|
||||
path: get().notebookBasePath,
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
@@ -183,11 +199,14 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
path: "Gallery",
|
||||
type: NotebookContentItemType.File,
|
||||
};
|
||||
const gitHubNotebooksContentRoot = {
|
||||
const gitHubNotebooksContentRoot = notebookManager?.gitHubOAuthService?.isLoggedIn()
|
||||
? {
|
||||
name: "GitHub repos",
|
||||
path: "PsuedoDir",
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
}
|
||||
: undefined;
|
||||
|
||||
set({
|
||||
myNotebooksContentRoot,
|
||||
galleryContentRoot,
|
||||
@@ -249,5 +268,15 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
set({ gitHubNotebooksContentRoot });
|
||||
}
|
||||
},
|
||||
setConnectionInfo: (connectionInfo: DataModels.ContainerConnectionInfo) => set({ connectionInfo }),
|
||||
setConnectionInfo: (connectionInfo: ContainerConnectionInfo) => set({ connectionInfo }),
|
||||
setIsAllocating: (isAllocating: boolean) => set({ isAllocating }),
|
||||
resetConatinerConnection: (connectionStatus: ContainerConnectionInfo): void => {
|
||||
useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||
useNotebook.getState().setNotebookServerInfo({
|
||||
notebookServerEndpoint: undefined,
|
||||
authToken: undefined,
|
||||
});
|
||||
useNotebook.getState().setIsAllocating(false);
|
||||
},
|
||||
setIsRefreshed: (isRefreshed: boolean) => set({ isRefreshed }),
|
||||
}));
|
||||
|
||||
@@ -13,21 +13,21 @@ import {
|
||||
Text,
|
||||
TooltipHost,
|
||||
} from "@fluentui/react";
|
||||
import * as Constants from "Common/Constants";
|
||||
import { createCollection } from "Common/dataAccess/createCollection";
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { configContext, Platform } from "ConfigContext";
|
||||
import * as DataModels from "Contracts/DataModels";
|
||||
import { SubscriptionType } from "Contracts/SubscriptionType";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import React from "react";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { createCollection } from "../../Common/dataAccess/createCollection";
|
||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||
import { configContext, Platform } from "../../ConfigContext";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { SubscriptionType } from "../../Contracts/SubscriptionType";
|
||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||
import { CollectionCreation } from "../../Shared/Constants";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getCollectionName } from "../../Utils/APITypeUtils";
|
||||
import { isCapabilityEnabled, isServerlessAccount } from "../../Utils/CapabilityUtils";
|
||||
import { getUpsellMessage } from "../../Utils/PricingUtils";
|
||||
import { CollectionCreation } from "Shared/Constants";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { getCollectionName } from "Utils/APITypeUtils";
|
||||
import { isCapabilityEnabled, isServerlessAccount } from "Utils/CapabilityUtils";
|
||||
import { getUpsellMessage } from "Utils/PricingUtils";
|
||||
import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||
import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput";
|
||||
import Explorer from "../Explorer";
|
||||
@@ -161,7 +161,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
true
|
||||
).toLocaleLowerCase()}.`}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -210,6 +210,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
className="panelTextField"
|
||||
aria-label="New database id"
|
||||
autoFocus
|
||||
tabIndex={0}
|
||||
value={this.state.newDatabaseId}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
this.setState({ newDatabaseId: event.target.value })
|
||||
@@ -236,7 +237,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
true
|
||||
).toLocaleLowerCase()} within the database.`}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -279,7 +280,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -362,7 +363,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
"Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data."
|
||||
}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -409,7 +410,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content={this.getPartitionKeyTooltipText()}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -467,7 +468,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
does not count towards the throughput you provisioned for the database. This throughput amount will be
|
||||
billed in addition to the throughput amount you provisioned at the database level.`}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -497,7 +498,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
creating a unique key policy when a container is created, you ensure the uniqueness of one or more values
|
||||
per partition key."
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -560,7 +561,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content={this.getAnalyticalStorageTooltipContent()}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -637,7 +638,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content="The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development."
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -998,7 +999,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
|
||||
const collectionId: string = this.state.collectionId.trim();
|
||||
let databaseId = this.state.createNewDatabase ? this.state.newDatabaseId.trim() : this.state.selectedDatabaseId;
|
||||
let partitionKeyString = this.state.partitionKey.trim();
|
||||
let partitionKeyString = this.state.isSharded ? this.state.partitionKey.trim() : undefined;
|
||||
|
||||
if (userContext.apiType === "Tables") {
|
||||
// Table require fixed Database: TablesDB, and fixed Partition Key: /'$pk'
|
||||
|
||||
@@ -23,10 +23,12 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
|
||||
|
||||
export interface AddDatabasePaneProps {
|
||||
explorer: Explorer;
|
||||
buttonElement?: HTMLElement;
|
||||
}
|
||||
|
||||
export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
||||
explorer: container,
|
||||
buttonElement,
|
||||
}: AddDatabasePaneProps) => {
|
||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||
let throughput: number;
|
||||
@@ -77,6 +79,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||
};
|
||||
TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage);
|
||||
buttonElement.focus();
|
||||
}, []);
|
||||
|
||||
const onSubmit = () => {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Checkbox, Dropdown, IDropdownOption, Link, Stack, Text, TextField } from "@fluentui/react";
|
||||
import * as Constants from "Common/Constants";
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import * as SharedConstants from "../../../Shared/Constants";
|
||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { isServerlessAccount } from "../../../Utils/CapabilityUtils";
|
||||
import * as SharedConstants from "Shared/Constants";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
||||
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
|
||||
import Explorer from "../../Explorer";
|
||||
import { CassandraAPIDataClient } from "../../Tables/TableDataClient";
|
||||
@@ -198,6 +198,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
|
||||
<Stack className="panelGroupSpacing">
|
||||
<TextField
|
||||
aria-required="true"
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
styles={getTextFieldStyles()}
|
||||
pattern="[^/?#\\]*[^/?# \\]"
|
||||
@@ -285,6 +286,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
|
||||
underlined
|
||||
styles={getTextFieldStyles({ fontSize: 12, width: 150 })}
|
||||
aria-required="true"
|
||||
required={true}
|
||||
ariaLabel="addCollection-tableId"
|
||||
autoComplete="off"
|
||||
pattern="[^/?#\\]*[^/?# \\]"
|
||||
|
||||
@@ -77,7 +77,7 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
|
||||
selectedLocation.repo
|
||||
)} - ${selectedLocation.branch}`;
|
||||
} else if (selectedLocation.type === "MyNotebooks" && userContext.features.phoenix) {
|
||||
destination = "My Notebooks Scratch";
|
||||
destination = useNotebook.getState().notebookFolderName;
|
||||
}
|
||||
|
||||
clearMessage = NotificationConsoleUtils.logConsoleProgress(`Copying ${name} to ${destination}`);
|
||||
|
||||
@@ -49,8 +49,8 @@ export const CopyNotebookPaneComponent: FunctionComponent<CopyNotebookPaneProps>
|
||||
const options: IDropdownOption[] = [];
|
||||
options.push({
|
||||
key: "MyNotebooks-Item",
|
||||
text: useNotebook.getState().NotebookFolderName,
|
||||
title: useNotebook.getState().NotebookFolderName,
|
||||
text: useNotebook.getState().notebookFolderName,
|
||||
title: useNotebook.getState().notebookFolderName,
|
||||
data: {
|
||||
type: "MyNotebooks",
|
||||
} as Location,
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Text, TextField } from "@fluentui/react";
|
||||
import { Areas } from "Common/Constants";
|
||||
import { deleteCollection } from "Common/dataAccess/deleteCollection";
|
||||
import DeleteFeedback from "Common/DeleteFeedback";
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { Collection } from "Contracts/ViewModels";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import { Areas } from "../../../Common/Constants";
|
||||
import { deleteCollection } from "../../../Common/dataAccess/deleteCollection";
|
||||
import DeleteFeedback from "../../../Common/DeleteFeedback";
|
||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||
import { Collection } from "../../../Contracts/ViewModels";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { useTabs } from "../../../hooks/useTabs";
|
||||
import { DefaultExperienceUtility } from "../../../Shared/DefaultExperienceUtility";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { getCollectionName } from "../../../Utils/APITypeUtils";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { getCollectionName } from "Utils/APITypeUtils";
|
||||
import * as NotificationConsoleUtils from "Utils/NotificationConsoleUtils";
|
||||
import { useDatabases } from "../../useDatabases";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
@@ -108,6 +108,8 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
||||
submitButtonText: "OK",
|
||||
onSubmit,
|
||||
};
|
||||
const confirmContainer = `Confirm by typing the ${collectionName.toLowerCase()} id`;
|
||||
const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${collectionName}?`;
|
||||
return (
|
||||
<RightPaneForm {...props}>
|
||||
<div className="panelFormWrapper">
|
||||
@@ -123,6 +125,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
||||
onChange={(event, newInput?: string) => {
|
||||
setInputCollectionName(newInput);
|
||||
}}
|
||||
ariaLabel={confirmContainer}
|
||||
/>
|
||||
</div>
|
||||
{shouldRecordFeedback() && (
|
||||
@@ -142,6 +145,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
||||
onChange={(event, newInput?: string) => {
|
||||
setDeleteCollectionFeedback(newInput);
|
||||
}}
|
||||
ariaLabel={reasonInfo}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -40,6 +40,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
||||
</span>
|
||||
</Text>
|
||||
<StyledTextFieldBase
|
||||
ariaLabel="Confirm by typing the container id"
|
||||
autoFocus={true}
|
||||
id="confirmCollectionId"
|
||||
onChange={[Function]}
|
||||
@@ -53,6 +54,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
||||
value=""
|
||||
>
|
||||
<TextFieldBase
|
||||
ariaLabel="Confirm by typing the container id"
|
||||
autoFocus={true}
|
||||
deferredValidationTime={200}
|
||||
id="confirmCollectionId"
|
||||
@@ -346,6 +348,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
||||
>
|
||||
<input
|
||||
aria-invalid={false}
|
||||
aria-label="Confirm by typing the container id"
|
||||
autoFocus={true}
|
||||
className="ms-TextField-field field-57"
|
||||
id="confirmCollectionId"
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Text, TextField } from "@fluentui/react";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import { Areas } from "Common/Constants";
|
||||
import { deleteDatabase } from "Common/dataAccess/deleteDatabase";
|
||||
import DeleteFeedback from "Common/DeleteFeedback";
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { Collection, Database } from "Contracts/ViewModels";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import { Areas } from "../../Common/Constants";
|
||||
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
|
||||
import DeleteFeedback from "../../Common/DeleteFeedback";
|
||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||
import { Collection, Database } from "../../Contracts/ViewModels";
|
||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
||||
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { PanelInfoErrorComponent, PanelInfoErrorProps } from "./PanelInfoErrorComponent";
|
||||
@@ -118,7 +118,8 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
||||
message:
|
||||
"Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources.",
|
||||
};
|
||||
|
||||
const confirmDatabase = "Confirm by typing the database id";
|
||||
const reasonInfo = "Help us improve Azure Cosmos DB! What is the reason why you are deleting this database?";
|
||||
return (
|
||||
<RightPaneForm {...props}>
|
||||
{!formError && <PanelInfoErrorComponent {...errorProps} />}
|
||||
@@ -133,6 +134,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
||||
onChange={(event, newInput?: string) => {
|
||||
setDatabaseInput(newInput);
|
||||
}}
|
||||
ariaLabel={confirmDatabase}
|
||||
/>
|
||||
</div>
|
||||
{isLastNonEmptyDatabase() && (
|
||||
@@ -151,6 +153,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
||||
onChange={(event, newInput?: string) => {
|
||||
setDatabaseFeedbackInput(newInput);
|
||||
}}
|
||||
ariaLabel={reasonInfo}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -153,7 +153,7 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
|
||||
selectedKey={paramKeyValue && paramKeyValue.key}
|
||||
/>
|
||||
))}
|
||||
<Stack horizontal onClick={addNewParamAtLastIndex}>
|
||||
<Stack horizontal onClick={addNewParamAtLastIndex} tabIndex={0}>
|
||||
<Image {...imageProps} src={AddPropertyIcon} alt="Add param" />
|
||||
<Text className="addNewParamStyle">Add New Param</Text>
|
||||
</Stack>
|
||||
|
||||
@@ -59,30 +59,36 @@ export const InputParameter: FunctionComponent<InputParameterProps> = ({
|
||||
onChange={onParamKeyChange}
|
||||
options={options}
|
||||
styles={dropdownStyles}
|
||||
tabIndex={0}
|
||||
/>
|
||||
<TextField
|
||||
label={inputLabel && inputLabel}
|
||||
id="confirmCollectionId"
|
||||
autoFocus
|
||||
value={paramValue}
|
||||
onChange={onParamValueChange}
|
||||
/>
|
||||
{isAddRemoveVisible && (
|
||||
<>
|
||||
<div tabIndex={0}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
src={EntityCancelIcon}
|
||||
alt="Delete param"
|
||||
id="deleteparam"
|
||||
role="button"
|
||||
onClick={onDeleteParamKeyPress}
|
||||
/>
|
||||
</div>
|
||||
<div tabIndex={0}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
src={AddPropertyIcon}
|
||||
alt="Add param"
|
||||
id="addparam"
|
||||
role="button"
|
||||
onClick={onAddNewParamKeyPress}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -345,6 +345,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
},
|
||||
}
|
||||
}
|
||||
tabIndex={0}
|
||||
>
|
||||
<DropdownBase
|
||||
label="Key"
|
||||
@@ -363,6 +364,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
}
|
||||
selectedKey="string"
|
||||
styles={[Function]}
|
||||
tabIndex={0}
|
||||
theme={
|
||||
Object {
|
||||
"disableGlobalClassNames": false,
|
||||
@@ -664,6 +666,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
responsiveMode={3}
|
||||
selectedKey="string"
|
||||
styles={[Function]}
|
||||
tabIndex={0}
|
||||
theme={
|
||||
Object {
|
||||
"disableGlobalClassNames": false,
|
||||
@@ -1562,14 +1565,12 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
</DropdownBase>
|
||||
</Dropdown>
|
||||
<StyledTextFieldBase
|
||||
autoFocus={true}
|
||||
id="confirmCollectionId"
|
||||
key=".0:$.1"
|
||||
label="Value"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<TextFieldBase
|
||||
autoFocus={true}
|
||||
deferredValidationTime={200}
|
||||
id="confirmCollectionId"
|
||||
label="Value"
|
||||
@@ -2155,7 +2156,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
<input
|
||||
aria-invalid={false}
|
||||
aria-labelledby="TextFieldLabel3"
|
||||
autoFocus={true}
|
||||
className="ms-TextField-field field-77"
|
||||
id="confirmCollectionId"
|
||||
onBlur={[Function]}
|
||||
@@ -2500,6 +2500,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
},
|
||||
}
|
||||
}
|
||||
tabIndex={0}
|
||||
>
|
||||
<DropdownBase
|
||||
label="Key"
|
||||
@@ -2518,6 +2519,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
}
|
||||
selectedKey="string"
|
||||
styles={[Function]}
|
||||
tabIndex={0}
|
||||
theme={
|
||||
Object {
|
||||
"disableGlobalClassNames": false,
|
||||
@@ -2819,6 +2821,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
responsiveMode={3}
|
||||
selectedKey="string"
|
||||
styles={[Function]}
|
||||
tabIndex={0}
|
||||
theme={
|
||||
Object {
|
||||
"disableGlobalClassNames": false,
|
||||
@@ -3717,7 +3720,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
</DropdownBase>
|
||||
</Dropdown>
|
||||
<StyledTextFieldBase
|
||||
autoFocus={true}
|
||||
id="confirmCollectionId"
|
||||
key=".0:$.1"
|
||||
label="Param"
|
||||
@@ -3725,7 +3727,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
value=""
|
||||
>
|
||||
<TextFieldBase
|
||||
autoFocus={true}
|
||||
deferredValidationTime={200}
|
||||
id="confirmCollectionId"
|
||||
label="Param"
|
||||
@@ -4312,7 +4313,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
<input
|
||||
aria-invalid={false}
|
||||
aria-labelledby="TextFieldLabel7"
|
||||
autoFocus={true}
|
||||
className="ms-TextField-field field-77"
|
||||
id="confirmCollectionId"
|
||||
onBlur={[Function]}
|
||||
@@ -4327,12 +4327,16 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
</div>
|
||||
</TextFieldBase>
|
||||
</StyledTextFieldBase>
|
||||
<div
|
||||
tabIndex={0}
|
||||
>
|
||||
<StyledImageBase
|
||||
alt="Delete param"
|
||||
className="addRemoveIconLabel"
|
||||
height={30}
|
||||
id="deleteparam"
|
||||
onClick={[Function]}
|
||||
role="button"
|
||||
src=""
|
||||
width={20}
|
||||
>
|
||||
@@ -4342,6 +4346,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
height={30}
|
||||
id="deleteparam"
|
||||
onClick={[Function]}
|
||||
role="button"
|
||||
src=""
|
||||
styles={[Function]}
|
||||
theme={
|
||||
@@ -4636,17 +4641,23 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
onClick={[Function]}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
role="button"
|
||||
src=""
|
||||
/>
|
||||
</div>
|
||||
</ImageBase>
|
||||
</StyledImageBase>
|
||||
</div>
|
||||
<div
|
||||
tabIndex={0}
|
||||
>
|
||||
<StyledImageBase
|
||||
alt="Add param"
|
||||
className="addRemoveIconLabel"
|
||||
height={30}
|
||||
id="addparam"
|
||||
onClick={[Function]}
|
||||
role="button"
|
||||
src=""
|
||||
width={20}
|
||||
>
|
||||
@@ -4656,6 +4667,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
height={30}
|
||||
id="addparam"
|
||||
onClick={[Function]}
|
||||
role="button"
|
||||
src=""
|
||||
styles={[Function]}
|
||||
theme={
|
||||
@@ -4950,21 +4962,25 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
onClick={[Function]}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
role="button"
|
||||
src=""
|
||||
/>
|
||||
</div>
|
||||
</ImageBase>
|
||||
</StyledImageBase>
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
</InputParameter>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
onClick={[Function]}
|
||||
tabIndex={0}
|
||||
>
|
||||
<div
|
||||
className="ms-Stack css-54"
|
||||
onClick={[Function]}
|
||||
tabIndex={0}
|
||||
>
|
||||
<StyledImageBase
|
||||
alt="Add param"
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
.panelFormWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
.panelMainContent {
|
||||
flex-grow: 1;
|
||||
padding: 0 34px;
|
||||
margin: 20px 0;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
& > :not(.collapsibleSection) {
|
||||
margin-bottom: @DefaultSpace;
|
||||
|
||||
@@ -33,7 +33,13 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
|
||||
<Stack className="panelInfoErrorContainer" horizontal verticalAlign="center">
|
||||
{icon}
|
||||
<span className="panelWarningErrorDetailsLinkContainer">
|
||||
<Text className="panelWarningErrorMessage" variant="small" aria-label="message">
|
||||
<Text
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-label={message}
|
||||
className="panelWarningErrorMessage"
|
||||
variant="small"
|
||||
>
|
||||
{message}
|
||||
{link && linkText && (
|
||||
<Link target="_blank" href={link}>
|
||||
|
||||
@@ -34,6 +34,6 @@ describe("Right Pane Form", () => {
|
||||
it("should render error in header", () => {
|
||||
render(<RightPaneForm {...props} formError="file already Exist" />);
|
||||
expect(screen.getByLabelText("error")).toBeDefined();
|
||||
expect(screen.getByLabelText("message").innerHTML).toEqual("file already Exist");
|
||||
expect(screen.getByRole("alert").innerHTML).toEqual("file already Exist");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Checkbox, ChoiceGroup, IChoiceGroupOption, SpinButton } from "@fluentui/react";
|
||||
import * as Constants from "Common/Constants";
|
||||
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
||||
import { configContext } from "ConfigContext";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import React, { FunctionComponent, MouseEvent, useState } from "react";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
|
||||
import { configContext } from "../../../ConfigContext";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { LocalStorageUtility, StorageKey } from "../../../Shared/StorageUtility";
|
||||
import * as StringUtility from "../../../Shared/StringUtility";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { logConsoleInfo } from "../../../Utils/NotificationConsoleUtils";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import * as StringUtility from "Shared/StringUtility";
|
||||
import { userContext } from "UserContext";
|
||||
import { logConsoleInfo } from "Utils/NotificationConsoleUtils";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
|
||||
export const SettingsPane: FunctionComponent = () => {
|
||||
@@ -195,7 +195,6 @@ export const SettingsPane: FunctionComponent = () => {
|
||||
step={1}
|
||||
className="textfontclr"
|
||||
role="textbox"
|
||||
tabIndex={0}
|
||||
id="max-degree"
|
||||
value={"" + maxDegreeOfParallelism}
|
||||
onIncrement={(newValue) => setMaxDegreeOfParallelism(parseInt(newValue) + 1 || maxDegreeOfParallelism)}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user