diff --git a/images/close-black.svg b/images/close-black.svg index 9b87397e1..a17e75636 100644 --- a/images/close-black.svg +++ b/images/close-black.svg @@ -1,16 +1,8 @@ - - + \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 889b544e0..57ec3f489 100644 --- a/jest.config.js +++ b/jest.config.js @@ -37,8 +37,8 @@ module.exports = { global: { branches: 25, functions: 25, - lines: 30, - statements: 30, + lines: 29.5, + statements: 29.5, }, }, diff --git a/less/documentDB.less b/less/documentDB.less index 271f53992..79e9435c3 100644 --- a/less/documentDB.less +++ b/less/documentDB.less @@ -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 { diff --git a/src/Common/CollapsedResourceTree.tsx b/src/Common/CollapsedResourceTree.tsx index 8a91fd5d3..9a9e1661d 100644 --- a/src/Common/CollapsedResourceTree.tsx +++ b/src/Common/CollapsedResourceTree.tsx @@ -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 { + const focusButton = useRef() as MutableRefObject; + + 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 (
@@ -21,11 +37,14 @@ export const CollapsedResourceTree: FunctionComponent - + Expand - + {userContext.apiType} API diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index 6e13f07b5..bb8a2787b 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -340,7 +340,6 @@ export enum ConflictOperationType { export enum ConnectionStatusType { Connecting = "Connecting", - Allocating = "Allocating", Connected = "Connected", Failed = "Connection Failed", } diff --git a/src/Common/MongoProxyClient.test.ts b/src/Common/MongoProxyClient.test.ts index 3a5a02365..1c49141a0 100644 --- a/src/Common/MongoProxyClient.test.ts +++ b/src/Common/MongoProxyClient.test.ts @@ -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"); + }); + }); }); diff --git a/src/Common/MongoProxyClient.ts b/src/Common/MongoProxyClient.ts index 2945f1288..668a0ab16 100644 --- a/src/Common/MongoProxyClient.ts +++ b/src/Common/MongoProxyClient.ts @@ -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,7 +79,7 @@ export function queryDocuments( : "", }; - const endpoint = getEndpoint() || ""; + const endpoint = getFeatureEndpointOrDefault("resourcelist") || ""; const headers = { ...defaultHeaders, @@ -141,7 +142,8 @@ export function readDocument( : "", }; - const endpoint = getEndpoint(); + const endpoint = getFeatureEndpointOrDefault("readDocument"); + return window .fetch(`${endpoint}?${queryString.stringify(params)}`, { method: "GET", @@ -181,7 +183,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 +227,7 @@ export function updateDocument( ? documentId.partitionKeyProperty : "", }; - const endpoint = getEndpoint(); + const endpoint = getFeatureEndpointOrDefault("updateDocument"); return window .fetch(`${endpoint}?${queryString.stringify(params)}`, { @@ -266,7 +268,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 +311,7 @@ export function createMongoCollectionWithProxy( autoPilotThroughput: params.autoPilotMaxThroughput?.toString(), }; - const endpoint = getEndpoint(); + const endpoint = getFeatureEndpointOrDefault("createCollectionWithProxy"); return window .fetch( @@ -333,8 +335,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"); diff --git a/src/Common/ResourceTreeContainer.tsx b/src/Common/ResourceTreeContainer.tsx index 18a769b12..00693a2c0 100644 --- a/src/Common/ResourceTreeContainer.tsx +++ b/src/Common/ResourceTreeContainer.tsx @@ -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 { + const focusButton = useRef() as MutableRefObject; + + 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 (
{/* Collections Window - - Start */} @@ -43,9 +60,11 @@ export const ResourceTreeContainer: FunctionComponent Hide diff --git a/src/Common/Tooltip/InfoTooltip.tsx b/src/Common/Tooltip/InfoTooltip.tsx index 480aa9020..3ce33ca93 100644 --- a/src/Common/Tooltip/InfoTooltip.tsx +++ b/src/Common/Tooltip/InfoTooltip.tsx @@ -9,7 +9,7 @@ export const InfoTooltip: React.FunctionComponent = ({ children }: return ( - + ); diff --git a/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx b/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx index d662db58b..76d710cb7 100644 --- a/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx +++ b/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx @@ -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 { + 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 diff --git a/src/Explorer/Controls/CollapsiblePanel/__snapshots__/CollapsibleSectionComponent.test.tsx.snap b/src/Explorer/Controls/CollapsiblePanel/__snapshots__/CollapsibleSectionComponent.test.tsx.snap index 95d3c46bf..675a79239 100644 --- a/src/Explorer/Controls/CollapsiblePanel/__snapshots__/CollapsibleSectionComponent.test.tsx.snap +++ b/src/Explorer/Controls/CollapsiblePanel/__snapshots__/CollapsibleSectionComponent.test.tsx.snap @@ -3,9 +3,14 @@ exports[`CollapsibleSectionComponent renders 1`] = ` - this.setState({ isSuggestionVisible: true })} - onChange={(_event, newValue?: string) => this.handleChange(newValue)} - /> +
+ this.setState({ isSuggestionVisible: true })} + onChange={(_event, newValue?: string) => this.handleChange(newValue)} + /> + {this.props.showCancelButton && ( 1`] = ` - +
+ +
`; @@ -28,16 +34,22 @@ exports[`inputTypeahead renders