Compare commits

..

4 Commits

Author SHA1 Message Date
hardiknai-techm
0897c75114 resolve_eslint_ReadOnlyNodePropertiesComponent 2021-10-14 11:13:35 +05:30
Hardikkumar Nai
271256bffb resolve_eslint_NodePropertiesComponent (#921)
* resolve_eslint_NodePropertiesComponent

* address commit

* Open new screen: Screen reader does not pass the 'Copied' information after selecting 'Copy' button.

* resolve lint error
2021-10-12 08:43:35 -07:00
hardiknai-techm
b9e06858c5 resolve master code conflict 2021-10-12 20:59:27 +05:30
hardiknai-techm
043541b0dc resolve eslint ReadOnlyNodePropertiesComponent 2021-08-20 08:10:58 +05:30
23 changed files with 161 additions and 184 deletions

View File

@@ -2,8 +2,6 @@
src/**/__mocks__/**/* src/**/__mocks__/**/*
dist/ dist/
Contracts/ Contracts/
src/Api/Apis.ts
src/AuthType.ts
src/Bindings/BindingHandlersRegisterer.ts src/Bindings/BindingHandlersRegisterer.ts
src/Bindings/ReactBindingHandler.ts src/Bindings/ReactBindingHandler.ts
src/Common/Constants.ts src/Common/Constants.ts
@@ -37,7 +35,6 @@ src/Definitions/svg.d.ts
src/Explorer/ComponentRegisterer.test.ts src/Explorer/ComponentRegisterer.test.ts
src/Explorer/ComponentRegisterer.ts src/Explorer/ComponentRegisterer.ts
src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts
src/Explorer/Controls/Editor/EditorComponent.ts src/Explorer/Controls/Editor/EditorComponent.ts
src/Explorer/Controls/JsonEditor/JsonEditorComponent.ts src/Explorer/Controls/JsonEditor/JsonEditorComponent.ts
src/Explorer/DataSamples/ContainerSampleGenerator.test.ts src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
@@ -81,18 +78,15 @@ src/Explorer/Tables/DataTable/DataTableBindingManager.ts
src/Explorer/Tables/DataTable/DataTableBuilder.ts src/Explorer/Tables/DataTable/DataTableBuilder.ts
src/Explorer/Tables/DataTable/DataTableContextMenu.ts src/Explorer/Tables/DataTable/DataTableContextMenu.ts
src/Explorer/Tables/DataTable/DataTableOperationManager.ts src/Explorer/Tables/DataTable/DataTableOperationManager.ts
# src/Explorer/Tables/DataTable/DataTableOperations.ts
src/Explorer/Tables/DataTable/DataTableViewModel.ts src/Explorer/Tables/DataTable/DataTableViewModel.ts
# src/Explorer/Tables/DataTable/TableCommands.ts
# src/Explorer/Tables/DataTable/TableEntityCache.ts
src/Explorer/Tables/DataTable/TableEntityListViewModel.ts src/Explorer/Tables/DataTable/TableEntityListViewModel.ts
# src/Explorer/Tables/Entities.ts
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
src/Explorer/Tables/TableDataClient.ts src/Explorer/Tables/TableDataClient.ts
src/Explorer/Tables/TableEntityProcessor.ts src/Explorer/Tables/TableEntityProcessor.ts
src/Explorer/Tables/Utilities.ts src/Explorer/Tables/Utilities.ts
src/Explorer/Tabs/ConflictsTab.ts src/Explorer/Tabs/ConflictsTab.ts
src/Explorer/Tabs/DatabaseSettingsTab.ts src/Explorer/Tabs/DatabaseSettingsTab.ts
src/Explorer/Tabs/DocumentsTab.test.ts
src/Explorer/Tabs/DocumentsTab.ts src/Explorer/Tabs/DocumentsTab.ts
src/Explorer/Tabs/GraphTab.ts src/Explorer/Tabs/GraphTab.ts
src/Explorer/Tabs/MongoDocumentsTab.ts src/Explorer/Tabs/MongoDocumentsTab.ts
@@ -117,12 +111,10 @@ src/Index.ts
src/Platform/Hosted/Authorization.ts src/Platform/Hosted/Authorization.ts
src/ReactDevTools.ts src/ReactDevTools.ts
src/Shared/Constants.ts src/Shared/Constants.ts
src/Shared/appInsights.ts src/Shared/DefaultExperienceUtility.test.ts
src/SparkClusterManager/ArcadiaResourceManager.ts src/Shared/DefaultExperienceUtility.ts
src/SparkClusterManager/SparkClusterManager.ts
src/Terminal/JupyterLabAppFactory.ts src/Terminal/JupyterLabAppFactory.ts
src/Terminal/NotebookAppContracts.d.ts src/Terminal/NotebookAppContracts.d.ts
src/applyExplorerBindings.ts
src/global.d.ts src/global.d.ts
src/setupTests.ts src/setupTests.ts
src/Explorer/Controls/InputTypeahead/InputTypeaheadComponent.tsx src/Explorer/Controls/InputTypeahead/InputTypeaheadComponent.tsx
@@ -131,25 +123,15 @@ src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx
src/Explorer/Controls/TreeComponent/TreeComponent.tsx src/Explorer/Controls/TreeComponent/TreeComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.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/NodePropertiesComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx
; src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx
src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx
src/Explorer/Notebook/NotebookComponent/contents/index.tsx src/Explorer/Notebook/NotebookComponent/contents/index.tsx
; src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx
src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx
src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx
src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx
src/Explorer/Notebook/NotebookRenderer/decorators/kbd-shortcuts/index.tsx src/Explorer/Notebook/NotebookRenderer/decorators/kbd-shortcuts/index.tsx
src/Explorer/Notebook/temp/inputs/connected-editors/codemirror.tsx
src/Explorer/Tree/ResourceTreeAdapter.tsx src/Explorer/Tree/ResourceTreeAdapter.tsx
__mocks__/monaco-editor.ts __mocks__/monaco-editor.ts
src/Explorer/Tree/ResourceTree.tsx src/Explorer/Tree/ResourceTree.tsx

View File

@@ -1,4 +1,4 @@
/** /**
* Data Explorer version {major.minor.patch} * Data Explorer version {major.minor.patch}
*/ */
export const DataExplorer: string = "1.0.1"; export const DataExplorer = "1.0.1";

View File

@@ -58,7 +58,7 @@ export class LeftPaneComponent extends React.Component<LeftPaneComponentProps> {
className={className} className={className}
as="tr" as="tr"
aria-label={node.caption} aria-label={node.caption}
onActivated={(e) => this.props.onRootNodeSelected(node.id)} onActivated={() => this.props.onRootNodeSelected(node.id)}
key={node.id} key={node.id}
> >
<td className="resultItem"> <td className="resultItem">

View File

@@ -1,8 +1,8 @@
import React from "react";
import { mount, ReactWrapper } from "enzyme"; import { mount, ReactWrapper } from "enzyme";
import * as Q from "q"; import * as Q from "q";
import { NodePropertiesComponent, NodePropertiesComponentProps, Mode } from "./NodePropertiesComponent"; import React from "react";
import { GraphHighlightedNodeData, EditedProperties, EditedEdges, PossibleVertex } from "./GraphExplorer"; import { GraphHighlightedNodeData, PossibleVertex } from "./GraphExplorer";
import { Mode, NodePropertiesComponent, NodePropertiesComponentProps } from "./NodePropertiesComponent";
describe("Property pane", () => { describe("Property pane", () => {
const title = "My Title"; const title = "My Title";
@@ -37,17 +37,18 @@ describe("Property pane", () => {
return { return {
expandedTitle: title, expandedTitle: title,
isCollapsed: false, isCollapsed: false,
onCollapsedChanged: (newValue: boolean): void => {}, onCollapsedChanged: jest.fn(),
node: highlightedNode, node: highlightedNode,
getPkIdFromNodeData: (v: GraphHighlightedNodeData): string => null, getPkIdFromNodeData: (): string => undefined,
collectionPartitionKeyProperty: null, collectionPartitionKeyProperty: undefined,
updateVertexProperties: (editedProperties: EditedProperties): Q.Promise<void> => Q.resolve(), updateVertexProperties: (): Q.Promise<void> => Q.resolve(),
selectNode: (id: string): void => {}, selectNode: jest.fn(),
updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(null), updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(undefined),
possibleEdgeLabels: null, possibleEdgeLabels: undefined,
editGraphEdges: (editedEdges: EditedEdges): Q.Promise<any> => Q.resolve(), //eslint-disable-next-line
deleteHighlightedNode: (): void => {}, editGraphEdges: (): Q.Promise<any> => Q.resolve(),
onModeChanged: (newMode: Mode): void => {}, deleteHighlightedNode: jest.fn(),
onModeChanged: jest.fn(),
viewMode: Mode.READONLY_PROP, viewMode: Mode.READONLY_PROP,
}; };
}; };

View File

@@ -72,7 +72,7 @@ export class NodePropertiesComponent extends React.Component<
super(props); super(props);
this.state = { this.state = {
editedProperties: { editedProperties: {
pkId: null, pkId: undefined,
readOnlyProperties: [], readOnlyProperties: [],
existingProperties: [], existingProperties: [],
addedProperties: [], addedProperties: [],
@@ -98,15 +98,12 @@ export class NodePropertiesComponent extends React.Component<
}; };
} }
public static getDerivedStateFromProps( public static getDerivedStateFromProps(props: NodePropertiesComponentProps): Partial<NodePropertiesComponentState> {
props: NodePropertiesComponentProps,
state: NodePropertiesComponentState
): Partial<NodePropertiesComponentState> {
if (props.viewMode !== Mode.READONLY_PROP) { if (props.viewMode !== Mode.READONLY_PROP) {
return { isDeleteConfirm: false }; return { isDeleteConfirm: false };
} }
return null; return undefined;
} }
public render(): JSX.Element { public render(): JSX.Element {
@@ -138,10 +135,10 @@ export class NodePropertiesComponent extends React.Component<
* @param value * @param value
*/ */
private static getTypeOption(value: any): ViewModels.InputPropertyValueTypeString { private static getTypeOption(value: any): ViewModels.InputPropertyValueTypeString {
if (value == null) { if (value === undefined) {
return "null"; return "null";
} }
let type = typeof value; const type = typeof value;
switch (type) { switch (type) {
case "number": case "number":
case "boolean": case "boolean":
@@ -172,10 +169,9 @@ export class NodePropertiesComponent extends React.Component<
]; ];
const existingProps: ViewModels.InputProperty[] = []; const existingProps: ViewModels.InputProperty[] = [];
if (this.props.node.hasOwnProperty("properties")) { if (this.props.node.hasOwnProperty("properties")) {
const hProps = this.props.node["properties"]; const hProps = this.props.node["properties"];
for (let p in hProps) { for (const p in hProps) {
const propValues = hProps[p]; const propValues = hProps[p];
(p === partitionKeyProperty ? readOnlyProps : existingProps).push({ (p === partitionKeyProperty ? readOnlyProps : existingProps).push({
key: p, key: p,
@@ -437,7 +433,7 @@ export class NodePropertiesComponent extends React.Component<
</div> </div>
); );
} else { } else {
return null; return undefined;
} }
} }

View File

@@ -1,7 +1,6 @@
import React from "react";
import { shallow } from "enzyme"; import { shallow } from "enzyme";
import React from "react";
import { GraphHighlightedNodeData } from "./GraphExplorer"; import { GraphHighlightedNodeData } from "./GraphExplorer";
import { import {
ReadOnlyNodePropertiesComponent, ReadOnlyNodePropertiesComponent,
ReadOnlyNodePropertiesComponentProps, ReadOnlyNodePropertiesComponentProps,
@@ -44,7 +43,7 @@ describe("<ReadOnlyNodePropertiesComponent />", () => {
const mockNode2 = { const mockNode2 = {
...mockNode, ...mockNode,
properties: { properties: {
key3: ["abcd", 1234, true, false, undefined, null], key3: ["abcd", 1234, true, false, undefined],
}, },
}; };
const props: ReadOnlyNodePropertiesComponentProps = { node: mockNode2 }; const props: ReadOnlyNodePropertiesComponentProps = { node: mockNode2 };

View File

@@ -60,9 +60,7 @@ export class ReadOnlyNodePropertiesComponent extends React.Component<ReadOnlyNod
} }
public static singlePropertyValueToString(value: ViewModels.GremlinPropertyValueType): string { public static singlePropertyValueToString(value: ViewModels.GremlinPropertyValueType): string {
if (value === null) { if (typeof value === "undefined") {
return "null";
} else if (typeof value === "undefined") {
return "undefined"; return "undefined";
} else { } else {
return value.toString(); return value.toString();
@@ -71,11 +69,8 @@ export class ReadOnlyNodePropertiesComponent extends React.Component<ReadOnlyNod
public static renderSinglePropertyValue(value: ViewModels.GremlinPropertyValueType): JSX.Element { public static renderSinglePropertyValue(value: ViewModels.GremlinPropertyValueType): JSX.Element {
let singlePropValue = value; let singlePropValue = value;
let className = "propertyValue"; const className = "propertyValue";
if (singlePropValue === null) { if (typeof singlePropValue === "undefined") {
singlePropValue = "null";
className += " isNull";
} else if (typeof singlePropValue === "undefined") {
singlePropValue = "undefined"; singlePropValue = "undefined";
} else { } else {
singlePropValue = value.toString(); singlePropValue = value.toString();

View File

@@ -123,7 +123,7 @@ exports[`<ReadOnlyNodePropertiesComponent /> renders properties (with multiple v
</td> </td>
<td <td
className="valueCol" className="valueCol"
title="abcd, 1234, true, false, undefined, null" title="abcd, 1234, true, false, undefined"
> >
<div <div
className="propertyValue" className="propertyValue"
@@ -155,12 +155,6 @@ exports[`<ReadOnlyNodePropertiesComponent /> renders properties (with multiple v
> >
undefined undefined
</div> </div>
<div
className="propertyValue isNull"
key="null"
>
null
</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@@ -4,6 +4,8 @@ import * as React from "react";
import { useFullScreenURLs } from "../hooks/useFullScreenURLs"; import { useFullScreenURLs } from "../hooks/useFullScreenURLs";
export const OpenFullScreen: React.FunctionComponent = () => { export const OpenFullScreen: React.FunctionComponent = () => {
const [isReadUrlCopy, setIsReadUrlCopy] = React.useState<boolean>(false);
const [isReadWriteUrlCopy, setIsReadWriteUrlCopy] = React.useState<boolean>(false);
const result = useFullScreenURLs(); const result = useFullScreenURLs();
if (!result) { if (!result) {
return <Spinner label="Generating URLs..." ariaLive="assertive" labelPosition="right" />; return <Spinner label="Generating URLs..." ariaLive="assertive" labelPosition="right" />;
@@ -25,8 +27,9 @@ export const OpenFullScreen: React.FunctionComponent = () => {
<DefaultButton <DefaultButton
onClick={() => { onClick={() => {
copyToClipboard(readWriteUrl); copyToClipboard(readWriteUrl);
setIsReadWriteUrlCopy(true);
}} }}
text="Copy" text={isReadWriteUrlCopy ? "Copied" : "Copy"}
iconProps={{ iconName: "Copy" }} iconProps={{ iconName: "Copy" }}
/> />
<PrimaryButton <PrimaryButton
@@ -41,9 +44,10 @@ export const OpenFullScreen: React.FunctionComponent = () => {
<Stack horizontal tokens={{ childrenGap: 10 }}> <Stack horizontal tokens={{ childrenGap: 10 }}>
<DefaultButton <DefaultButton
onClick={() => { onClick={() => {
setIsReadUrlCopy(true);
copyToClipboard(readUrl); copyToClipboard(readUrl);
}} }}
text="Copy" text={isReadUrlCopy ? "Copied" : "Copy"}
iconProps={{ iconName: "Copy" }} iconProps={{ iconName: "Copy" }}
/> />
<PrimaryButton <PrimaryButton

View File

@@ -7,7 +7,7 @@ import { Collection } from "Contracts/ViewModels";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import { useTabs } from "hooks/useTabs"; import { useTabs } from "hooks/useTabs";
import React, { FunctionComponent, useState } from "react"; import React, { FunctionComponent, useState } from "react";
import * as DefaultExperienceUtility from "Shared/DefaultExperienceUtility"; import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext"; import { userContext } from "UserContext";

View File

@@ -8,7 +8,7 @@ import { Collection, Database } from "Contracts/ViewModels";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import { useTabs } from "hooks/useTabs"; import { useTabs } from "hooks/useTabs";
import React, { FunctionComponent, useState } from "react"; import React, { FunctionComponent, useState } from "react";
import * as DefaultExperienceUtility from "Shared/DefaultExperienceUtility"; import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext"; import { userContext } from "UserContext";

View File

@@ -10,7 +10,7 @@ describe("Documents tab", () => {
describe("buildQuery", () => { describe("buildQuery", () => {
it("should generate the right select query for SQL API", () => { it("should generate the right select query for SQL API", () => {
const documentsTab = new DocumentsTab({ const documentsTab = new DocumentsTab({
partitionKey: undefined, partitionKey: null,
documentIds: ko.observableArray<DocumentId>(), documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents, tabKind: ViewModels.CollectionTabKind.Documents,
title: "", title: "",
@@ -82,9 +82,9 @@ describe("Documents tab", () => {
container: mongoExplorer, container: mongoExplorer,
}); });
it("should be false for undefined or undefined collection", () => { it("should be false for null or undefined collection", () => {
const documentsTab = new DocumentsTab({ const documentsTab = new DocumentsTab({
partitionKey: undefined, partitionKey: null,
documentIds: ko.observableArray<DocumentId>(), documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents, tabKind: ViewModels.CollectionTabKind.Documents,
title: "", title: "",
@@ -94,10 +94,10 @@ describe("Documents tab", () => {
expect(documentsTab.showPartitionKey).toBe(false); expect(documentsTab.showPartitionKey).toBe(false);
}); });
it("should be false for undefined or undefined partitionKey", () => { it("should be false for null or undefined partitionKey", () => {
const documentsTab = new DocumentsTab({ const documentsTab = new DocumentsTab({
collection: collectionWithoutPartitionKey, collection: collectionWithoutPartitionKey,
partitionKey: undefined, partitionKey: null,
documentIds: ko.observableArray<DocumentId>(), documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents, tabKind: ViewModels.CollectionTabKind.Documents,
title: "", title: "",
@@ -110,7 +110,7 @@ describe("Documents tab", () => {
it("should be true for non-Mongo accounts with system partitionKey", () => { it("should be true for non-Mongo accounts with system partitionKey", () => {
const documentsTab = new DocumentsTab({ const documentsTab = new DocumentsTab({
collection: collectionWithSystemPartitionKey, collection: collectionWithSystemPartitionKey,
partitionKey: undefined, partitionKey: null,
documentIds: ko.observableArray<DocumentId>(), documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents, tabKind: ViewModels.CollectionTabKind.Documents,
title: "", title: "",
@@ -126,7 +126,7 @@ describe("Documents tab", () => {
}); });
const documentsTab = new DocumentsTab({ const documentsTab = new DocumentsTab({
collection: mongoCollectionWithSystemPartitionKey, collection: mongoCollectionWithSystemPartitionKey,
partitionKey: undefined, partitionKey: null,
documentIds: ko.observableArray<DocumentId>(), documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents, tabKind: ViewModels.CollectionTabKind.Documents,
title: "", title: "",
@@ -139,7 +139,7 @@ describe("Documents tab", () => {
it("should be true for non-system partitionKey", () => { it("should be true for non-system partitionKey", () => {
const documentsTab = new DocumentsTab({ const documentsTab = new DocumentsTab({
collection: collectionWithNonSystemPartitionKey, collection: collectionWithNonSystemPartitionKey,
partitionKey: undefined, partitionKey: null,
documentIds: ko.observableArray<DocumentId>(), documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents, tabKind: ViewModels.CollectionTabKind.Documents,
title: "", title: "",

View File

@@ -118,7 +118,7 @@ export default class NotebookTabV2 extends NotebookTabBase {
const saveButtonChildren = []; const saveButtonChildren = [];
if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) { if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
saveButtonChildren.push({ saveButtonChildren.push({
iconName: "Copy", iconName: copyToLabel,
onCommandClick: () => this.copyNotebook(), onCommandClick: () => this.copyNotebook(),
commandButtonLabel: copyToLabel, commandButtonLabel: copyToLabel,
hasPopup: false, hasPopup: false,

View File

@@ -1,6 +1,6 @@
import * as ko from "knockout"; import * as ko from "knockout";
import * as Constants from "../Common/Constants";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
import * as Constants from "../Common/Constants";
export abstract class WaitsForTemplateViewModel implements ViewModels.WaitsForTemplate { export abstract class WaitsForTemplateViewModel implements ViewModels.WaitsForTemplate {
public isTemplateReady: ko.Observable<boolean>; public isTemplateReady: ko.Observable<boolean>;
@@ -14,11 +14,11 @@ export abstract class WaitsForTemplateViewModel implements ViewModels.WaitsForTe
callback(value); callback(value);
}); });
document.addEventListener("keydown", (e: KeyboardEvent) => { document.addEventListener("keydown", function (e: KeyboardEvent) {
// To trap keyboard focus in AddCollection pane // To trap keyboard focus in AddCollection pane
const firstFocusableElement = document.getElementById("closeBtnAddCollection"); let firstFocusableElement = document.getElementById("closeBtnAddCollection");
const lastFocusableElement = document.getElementById("submitBtnAddCollection"); let lastFocusableElement = document.getElementById("submitBtnAddCollection");
const isTabPressed = e.keyCode === Constants.KeyCodes.Tab; var isTabPressed = e.keyCode === Constants.KeyCodes.Tab;
if (isTabPressed) { if (isTabPressed) {
if (e.shiftKey) { if (e.shiftKey) {
/* shift + tab */ if (document.activeElement === firstFocusableElement) { /* shift + tab */ if (document.activeElement === firstFocusableElement) {

View File

@@ -1,29 +1,31 @@
import * as Constants from "../../Common/Constants"; import * as Constants from "../../Common/Constants";
import { configContext } from "../../ConfigContext"; import { configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
import * as DefaultExperienceUtility from "../../Shared/DefaultExperienceUtility"; import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
export const _generateResourceUrl = (): string => { export default class AuthHeadersUtil {
const { databaseAccount, resourceGroup, subscriptionId } = userContext; public static async generateEncryptedToken(readOnly: boolean = false): Promise<DataModels.GenerateTokenResponse> {
const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType); const url = configContext.BACKEND_ENDPOINT + "/api/tokens/generateToken" + AuthHeadersUtil._generateResourceUrl();
const accountEndpoint = databaseAccount?.properties?.documentEndpoint || ""; const headers: any = { authorization: userContext.authorizationToken };
const sid = subscriptionId || ""; headers[Constants.HttpHeaders.getReadOnlyKey] = readOnly;
const rg = resourceGroup || "";
const dba = databaseAccount?.name || "";
const resourceUrl = encodeURIComponent(accountEndpoint);
const rid = "";
const rtype = "";
return `?resourceUrl=${resourceUrl}&rid=${rid}&rtype=${rtype}&sid=${sid}&rg=${rg}&dba=${dba}&api=${apiKind}`;
};
export const generateEncryptedToken = async (readOnly = false): Promise<DataModels.GenerateTokenResponse> => { const response = await fetch(url, { method: "POST", headers });
const url = configContext.BACKEND_ENDPOINT + "/api/tokens/generateToken" + _generateResourceUrl(); const result = await response.json();
const headers: any = { authorization: userContext.authorizationToken }; // This API has a quirk where the response must be parsed to JSON twice
headers[Constants.HttpHeaders.getReadOnlyKey] = readOnly; return JSON.parse(result);
}
const response = await fetch(url, { method: "POST", headers }); private static _generateResourceUrl(): string {
const result = await response.json(); const { databaseAccount, resourceGroup, subscriptionId } = userContext;
// This API has a quirk where the response must be parsed to JSON twice const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType);
return JSON.parse(result); const accountEndpoint = databaseAccount?.properties?.documentEndpoint || "";
}; const sid = subscriptionId || "";
const rg = resourceGroup || "";
const dba = databaseAccount?.name || "";
const resourceUrl = encodeURIComponent(accountEndpoint);
const rid = "";
const rtype = "";
return `?resourceUrl=${resourceUrl}&rid=${rid}&rtype=${rtype}&sid=${sid}&rg=${rg}&dba=${dba}&api=${apiKind}`;
}
}

View File

@@ -1,6 +1,6 @@
import { AccountKind, CapabilityNames } from "../../Common/Constants"; import { AccountKind, CapabilityNames } from "../../Common/Constants";
import { AccessInputMetadata, ApiKind } from "../../Contracts/DataModels"; import { AccessInputMetadata, ApiKind } from "../../Contracts/DataModels";
import * as DefaultExperienceUtility from "../../Shared/DefaultExperienceUtility"; import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): unknown { export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): unknown {

View File

@@ -1,13 +1,13 @@
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
import * as DefaultExperienceUtility from "./DefaultExperienceUtility"; import { DefaultExperienceUtility } from "./DefaultExperienceUtility";
describe("Default Experience Utility", () => { describe("Default Experience Utility", () => {
describe("getDefaultExperienceFromApiKind()", () => { describe("getDefaultExperienceFromApiKind()", () => {
const runScenario = (apiKind: number, expectedExperience: typeof userContext.apiType): void => { function runScenario(apiKind: number, expectedExperience: typeof userContext.apiType): void {
const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(apiKind); const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(apiKind);
expect(resolvedExperience).toEqual(expectedExperience); expect(resolvedExperience).toEqual(expectedExperience);
}; }
describe("On SQL", () => { describe("On SQL", () => {
it("should return SQL", () => runScenario(DataModels.ApiKind.SQL, "SQL")); it("should return SQL", () => runScenario(DataModels.ApiKind.SQL, "SQL"));
@@ -35,10 +35,10 @@ describe("Default Experience Utility", () => {
}); });
describe("getApiKindFromDefaultExperience()", () => { describe("getApiKindFromDefaultExperience()", () => {
const runScenario = (defaultExperience: typeof userContext.apiType | null, expectedApiKind: number): void => { function runScenario(defaultExperience: typeof userContext.apiType | null, expectedApiKind: number): void {
const resolvedApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience); const resolvedApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience);
expect(resolvedApiKind).toEqual(expectedApiKind); expect(resolvedApiKind).toEqual(expectedApiKind);
}; }
describe("On SQL", () => { describe("On SQL", () => {
it("should return SQL", () => runScenario("SQL", DataModels.ApiKind.SQL)); it("should return SQL", () => runScenario("SQL", DataModels.ApiKind.SQL));
@@ -60,8 +60,8 @@ describe("Default Experience Utility", () => {
it("should return Graph", () => runScenario("Gremlin", DataModels.ApiKind.Graph)); it("should return Graph", () => runScenario("Gremlin", DataModels.ApiKind.Graph));
}); });
describe("On undefined", () => { describe("On null", () => {
it("should return SQL", () => runScenario(undefined, DataModels.ApiKind.SQL)); it("should return SQL", () => runScenario(null, DataModels.ApiKind.SQL));
}); });
}); });
}); });

View File

@@ -1,45 +1,47 @@
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
export const getApiKindFromDefaultExperience = (defaultExperience: typeof userContext.apiType): DataModels.ApiKind => { export class DefaultExperienceUtility {
if (!defaultExperience) { public static getApiKindFromDefaultExperience(defaultExperience: typeof userContext.apiType): DataModels.ApiKind {
return DataModels.ApiKind.SQL; if (!defaultExperience) {
}
switch (defaultExperience) {
case "SQL":
return DataModels.ApiKind.SQL; return DataModels.ApiKind.SQL;
case "Mongo": }
return DataModels.ApiKind.MongoDB;
case "Tables":
return DataModels.ApiKind.Table;
case "Cassandra":
return DataModels.ApiKind.Cassandra;
case "Gremlin":
return DataModels.ApiKind.Graph;
default:
return DataModels.ApiKind.SQL;
}
};
export const getDefaultExperienceFromApiKind = (apiKind: DataModels.ApiKind): typeof userContext.apiType => { switch (defaultExperience) {
if (apiKind === undefined) { case "SQL":
return "SQL"; return DataModels.ApiKind.SQL;
case "Mongo":
return DataModels.ApiKind.MongoDB;
case "Tables":
return DataModels.ApiKind.Table;
case "Cassandra":
return DataModels.ApiKind.Cassandra;
case "Gremlin":
return DataModels.ApiKind.Graph;
default:
return DataModels.ApiKind.SQL;
}
} }
switch (apiKind) { public static getDefaultExperienceFromApiKind(apiKind: DataModels.ApiKind): typeof userContext.apiType {
case DataModels.ApiKind.SQL: if (apiKind == null) {
return "SQL";
case DataModels.ApiKind.MongoDB:
case DataModels.ApiKind.MongoDBCompute:
return "Mongo";
case DataModels.ApiKind.Table:
return "Tables";
case DataModels.ApiKind.Cassandra:
return "Cassandra";
case DataModels.ApiKind.Graph:
return "Gremlin";
default:
return "SQL"; return "SQL";
}
switch (apiKind) {
case DataModels.ApiKind.SQL:
return "SQL";
case DataModels.ApiKind.MongoDB:
case DataModels.ApiKind.MongoDBCompute:
return "Mongo";
case DataModels.ApiKind.Table:
return "Tables";
case DataModels.ApiKind.Cassandra:
return "Cassandra";
case DataModels.ApiKind.Graph:
return "Gremlin";
default:
return "SQL";
}
} }
}; }

View File

@@ -5,35 +5,37 @@ import { ServerConnection, TerminalManager } from "@jupyterlab/services";
import { Terminal } from "@jupyterlab/terminal"; import { Terminal } from "@jupyterlab/terminal";
import { Panel, Widget } from "@phosphor/widgets"; import { Panel, Widget } from "@phosphor/widgets";
export const createTerminalApp = async (serverSettings: ServerConnection.ISettings) => { export class JupyterLabAppFactory {
const manager = new TerminalManager({ public static async createTerminalApp(serverSettings: ServerConnection.ISettings) {
serverSettings: serverSettings, const manager = new TerminalManager({
}); serverSettings: serverSettings,
const session = await manager.startNew(); });
const term = new Terminal(session, { theme: "dark", shutdownOnClose: true }); const session = await manager.startNew();
const term = new Terminal(session, { theme: "dark", shutdownOnClose: true });
if (!term) { if (!term) {
console.error("Failed starting terminal"); console.error("Failed starting terminal");
return; return;
}
term.title.closable = false;
term.addClass("terminalWidget");
let panel = new Panel();
panel.addWidget(term as any);
panel.id = "main";
// Attach the widget to the dom.
Widget.attach(panel, document.body);
// Handle resize events.
window.addEventListener("resize", () => {
panel.update();
});
// Dispose terminal when unloading.
window.addEventListener("unload", () => {
panel.dispose();
});
} }
}
term.title.closable = false;
term.addClass("terminalWidget");
const panel = new Panel();
panel.addWidget(term as any);
panel.id = "main";
// Attach the widget to the dom.
Widget.attach(panel, document.body);
// Handle resize events.
window.addEventListener("resize", () => {
panel.update();
});
// Dispose terminal when unloading.
window.addEventListener("unload", () => {
panel.dispose();
});
};

View File

@@ -6,7 +6,7 @@ import { Action } from "../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
import { updateUserContext } from "../UserContext"; import { updateUserContext } from "../UserContext";
import "./index.css"; import "./index.css";
import { createTerminalApp } from "./JupyterLabAppFactory"; import { JupyterLabAppFactory } from "./JupyterLabAppFactory";
import { TerminalProps } from "./TerminalProps"; import { TerminalProps } from "./TerminalProps";
const createServerSettings = (props: TerminalProps): ServerConnection.ISettings => { const createServerSettings = (props: TerminalProps): ServerConnection.ISettings => {
@@ -54,7 +54,7 @@ const initTerminal = async (props: TerminalProps) => {
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data); const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data);
try { try {
await createTerminalApp(serverSettings); await JupyterLabAppFactory.createTerminalApp(serverSettings);
TelemetryProcessor.traceSuccess(Action.OpenTerminal, data, startTime); TelemetryProcessor.traceSuccess(Action.OpenTerminal, data, startTime);
} catch (error) { } catch (error) {
TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime); TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime);

View File

@@ -3,7 +3,7 @@ import { BindingHandlersRegisterer } from "./Bindings/BindingHandlersRegisterer"
import Explorer from "./Explorer/Explorer"; import Explorer from "./Explorer/Explorer";
export const applyExplorerBindings = (explorer: Explorer) => { export const applyExplorerBindings = (explorer: Explorer) => {
if (!!explorer) { if (explorer) {
window.dataExplorer = explorer; window.dataExplorer = explorer;
BindingHandlersRegisterer.registerBindingHandlers(); BindingHandlersRegisterer.registerBindingHandlers();
ko.applyBindings(explorer); ko.applyBindings(explorer);

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { GenerateTokenResponse } from "../Contracts/DataModels"; import { GenerateTokenResponse } from "../Contracts/DataModels";
import * as AuthHeadersUtil from "../Platform/Hosted/Authorization"; import AuthHeadersUtil from "../Platform/Hosted/Authorization";
export function useFullScreenURLs(): GenerateTokenResponse | undefined { export function useFullScreenURLs(): GenerateTokenResponse | undefined {
const [state, setState] = useState<GenerateTokenResponse>(); const [state, setState] = useState<GenerateTokenResponse>();

View File

@@ -26,7 +26,7 @@ import {
getDatabaseAccountPropertiesFromMetadata, getDatabaseAccountPropertiesFromMetadata,
} from "../Platform/Hosted/HostedUtils"; } from "../Platform/Hosted/HostedUtils";
import { CollectionCreation } from "../Shared/Constants"; import { CollectionCreation } from "../Shared/Constants";
import * as DefaultExperienceUtility from "../Shared/DefaultExperienceUtility"; import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
import { PortalEnv, updateUserContext, userContext } from "../UserContext"; import { PortalEnv, updateUserContext, userContext } from "../UserContext";
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts"; import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types"; import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";