mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-24 11:21:23 +00:00
Compare commits
1 Commits
move_add_D
...
jbunster/t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ba434a1ac |
@@ -54,6 +54,7 @@ src/Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.ts
|
||||
src/Explorer/Controls/InputTypeahead/InputTypeahead.ts
|
||||
src/Explorer/Controls/JsonEditor/JsonEditorComponent.ts
|
||||
src/Explorer/Controls/Notebook/NotebookAppMessageHandler.ts
|
||||
src/Explorer/Controls/ThroughputInput/ThroughputInput.test.ts
|
||||
src/Explorer/Controls/ThroughputInput/ThroughputInputComponent.ts
|
||||
src/Explorer/Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3.ts
|
||||
src/Explorer/Controls/Toolbar/IToolbarAction.ts
|
||||
@@ -108,8 +109,10 @@ src/Explorer/Notebook/NotebookUtil.ts
|
||||
src/Explorer/OpenActions.test.ts
|
||||
src/Explorer/OpenActions.ts
|
||||
src/Explorer/OpenActionsStubs.ts
|
||||
src/Explorer/Panes/AddDatabasePane.ts
|
||||
src/Explorer/Panes/AddCollectionPane.test.ts
|
||||
src/Explorer/Panes/AddCollectionPane.ts
|
||||
src/Explorer/Panes/AddDatabasePane.test.ts
|
||||
src/Explorer/Panes/AddDatabasePane.ts
|
||||
src/Explorer/Panes/BrowseQueriesPane.ts
|
||||
src/Explorer/Panes/CassandraAddCollectionPane.ts
|
||||
src/Explorer/Panes/ContextualPaneBase.ts
|
||||
|
||||
@@ -1757,7 +1757,7 @@ input::-webkit-calendar-picker-indicator {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.paneMainContent {
|
||||
.contextual-pane .paneMainContent {
|
||||
flex: 1;
|
||||
padding-left: 34px;
|
||||
padding-right: 34px;
|
||||
|
||||
4965
package-lock.json
generated
4965
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@@ -11,7 +11,7 @@
|
||||
"@azure/ms-rest-nodeauth": "3.0.7",
|
||||
"@babel/plugin-proposal-class-properties": "7.12.1",
|
||||
"@babel/plugin-proposal-decorators": "7.12.12",
|
||||
"@fluentui/react": "8.14.3",
|
||||
"@fluentui/react": "8.10.1",
|
||||
"@jupyterlab/services": "6.0.2",
|
||||
"@jupyterlab/terminal": "3.0.3",
|
||||
"@microsoft/applicationinsights-web": "2.6.1",
|
||||
@@ -43,6 +43,7 @@
|
||||
"@testing-library/jest-dom": "5.11.9",
|
||||
"@types/mkdirp": "1.0.1",
|
||||
"@types/node-fetch": "2.5.7",
|
||||
"@uifabric/react-cards": "0.109.110",
|
||||
"applicationinsights": "1.8.0",
|
||||
"bootstrap": "3.4.1",
|
||||
"canvas": "file:./canvas",
|
||||
@@ -100,10 +101,10 @@
|
||||
"utility-types": "3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.9.0",
|
||||
"@babel/preset-env": "7.9.0",
|
||||
"@babel/preset-react": "7.9.4",
|
||||
"@babel/preset-typescript": "7.9.0",
|
||||
"@babel/core": "7.14.0",
|
||||
"@babel/preset-env": "7.14.1",
|
||||
"@babel/preset-react": "7.13.13",
|
||||
"@babel/preset-typescript": "7.13.0",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@types/applicationinsights-js": "1.0.7",
|
||||
"@types/codemirror": "0.0.56",
|
||||
@@ -126,10 +127,10 @@
|
||||
"@types/sinon": "2.3.3",
|
||||
"@types/styled-components": "5.1.1",
|
||||
"@types/underscore": "1.7.36",
|
||||
"@typescript-eslint/eslint-plugin": "4.22.0",
|
||||
"@typescript-eslint/parser": "4.22.0",
|
||||
"babel-jest": "24.9.0",
|
||||
"babel-loader": "8.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.22.1",
|
||||
"@typescript-eslint/parser": "4.22.1",
|
||||
"babel-jest": "26.6.3",
|
||||
"babel-loader": "8.2.2",
|
||||
"buffer": "5.1.0",
|
||||
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
||||
"create-file-webpack": "1.0.2",
|
||||
@@ -137,7 +138,7 @@
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-adapter-react-16": "1.15.5",
|
||||
"enzyme-to-json": "3.6.1",
|
||||
"eslint": "7.8.1",
|
||||
"eslint": "7.25.0",
|
||||
"eslint-cli": "1.1.1",
|
||||
"eslint-plugin-no-null": "1.0.2",
|
||||
"eslint-plugin-prefer-arrow": "1.2.2",
|
||||
@@ -151,7 +152,7 @@
|
||||
"html-loader": "0.5.5",
|
||||
"html-loader-jest": "0.2.1",
|
||||
"html-webpack-plugin": "4.5.2",
|
||||
"jest": "25.5.4",
|
||||
"jest": "26.6.3",
|
||||
"jest-canvas-mock": "2.1.0",
|
||||
"jest-playwright-preset": "1.5.1",
|
||||
"jest-trx-results-processor": "0.0.7",
|
||||
@@ -168,10 +169,10 @@
|
||||
"rimraf": "3.0.0",
|
||||
"sinon": "3.2.1",
|
||||
"style-loader": "0.23.0",
|
||||
"ts-loader": "6.2.2",
|
||||
"tslint": "5.11.0",
|
||||
"tslint-microsoft-contrib": "6.0.0",
|
||||
"typescript": "4.2.4",
|
||||
"ts-loader": "9.1.2",
|
||||
"tslint": "6.1.3",
|
||||
"tslint-microsoft-contrib": "6.2.0",
|
||||
"typescript": "4.3.0-beta",
|
||||
"url-loader": "1.1.1",
|
||||
"wait-on": "4.0.2",
|
||||
"webpack": "4.46.0",
|
||||
|
||||
@@ -3,11 +3,11 @@ export class ObjectCache<T> extends Map<string, T> {
|
||||
super();
|
||||
}
|
||||
|
||||
public get(key: string): T | undefined {
|
||||
public override get(key: string): T | undefined {
|
||||
return this.touch(key);
|
||||
}
|
||||
|
||||
public set(key: string, value: T): this {
|
||||
public override set(key: string, value: T): this {
|
||||
if (this.size === this.limit) {
|
||||
this.delete(this.keys().next().value);
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Icon, TooltipHost } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
|
||||
export interface TooltipProps {
|
||||
children: string;
|
||||
}
|
||||
|
||||
export const InfoTooltip: React.FunctionComponent = ({ children }: TooltipProps) => {
|
||||
return (
|
||||
<span>
|
||||
<TooltipHost content={children}>
|
||||
<Icon iconName="Info" ariaLabel="Info" className="panelInfoIcon" />
|
||||
</TooltipHost>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
24
src/Common/Tooltip/Tooltip.tsx
Normal file
24
src/Common/Tooltip/Tooltip.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ITooltipHostStyles, TooltipHost } from "@fluentui/react";
|
||||
import { useId } from "@fluentui/react-hooks";
|
||||
import * as React from "react";
|
||||
import InfoBubble from "../../../images/info-bubble.svg";
|
||||
|
||||
const calloutProps = { gapSpace: 0 };
|
||||
const hostStyles: Partial<ITooltipHostStyles> = { root: { display: "inline-block" } };
|
||||
|
||||
export interface TooltipProps {
|
||||
children: string;
|
||||
}
|
||||
export const Tooltip: React.FunctionComponent = ({ children }: TooltipProps) => {
|
||||
const tooltipId = useId("tooltip");
|
||||
|
||||
return children ? (
|
||||
<span>
|
||||
<TooltipHost content={children} id={tooltipId} calloutProps={calloutProps} styles={hostStyles}>
|
||||
<img className="infoImg" src={InfoBubble} alt="More information" />
|
||||
</TooltipHost>
|
||||
</span>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import { Image, Stack, TextField } from "@fluentui/react";
|
||||
import React, { ChangeEvent, FunctionComponent, KeyboardEvent, useRef, useState } from "react";
|
||||
import FolderIcon from "../../../images/folder_16x16.svg";
|
||||
import * as Constants from "../Constants";
|
||||
import { InfoTooltip } from "../Tooltip/InfoTooltip";
|
||||
import { Tooltip } from "../Tooltip/Tooltip";
|
||||
|
||||
interface UploadProps {
|
||||
label: string;
|
||||
@@ -51,7 +51,7 @@ export const Upload: FunctionComponent<UploadProps> = ({
|
||||
return (
|
||||
<div>
|
||||
<span className="renewUploadItemsHeader">{label}</span>
|
||||
{tooltip && <InfoTooltip>{tooltip}</InfoTooltip>}
|
||||
<Tooltip>{tooltip}</Tooltip>
|
||||
<Stack horizontal>
|
||||
<TextField styles={{ fieldGroup: { width: 300 } }} readOnly value={selectedFilesTitle.toString()} />
|
||||
<input
|
||||
|
||||
@@ -20,6 +20,10 @@ describe("Component Registerer", () => {
|
||||
expect(ko.components.isRegistered("json-editor")).toBe(true);
|
||||
});
|
||||
|
||||
it("should registeradd-collection-pane component", () => {
|
||||
expect(ko.components.isRegistered("add-collection-pane")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register graph-styling-pane component", () => {
|
||||
expect(ko.components.isRegistered("graph-styling-pane")).toBe(true);
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ ko.components.register("dynamic-list", DynamicListComponent);
|
||||
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
||||
|
||||
// Panes
|
||||
|
||||
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
||||
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
||||
ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPaneComponent());
|
||||
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
||||
|
||||
@@ -20,7 +20,7 @@ export class AccessibleElement extends React.Component<AccessibleElementProps> {
|
||||
}
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const elementProps = { ...this.props };
|
||||
delete elementProps.as;
|
||||
delete elementProps.onActivated;
|
||||
|
||||
@@ -12,7 +12,7 @@ import TriangleRightIcon from "../../../../images/Triangle-right.svg";
|
||||
export interface AccordionComponentProps {}
|
||||
|
||||
export class AccordionComponent extends React.Component<AccordionComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return <div className="accordion">{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
public override componentDidUpdate() {
|
||||
if (this.props.isExpanded !== this.isExpanded) {
|
||||
this.isExpanded = this.props.isExpanded;
|
||||
this.setState({
|
||||
@@ -51,7 +51,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="accordionItemContainer">
|
||||
<div className="accordionItemHeader" onClick={this.onHeaderClick} onKeyPress={this.onHeaderKeyPress}>
|
||||
|
||||
@@ -62,7 +62,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
||||
this.props.onCreateNewSparkPoolClicked(item.key);
|
||||
};
|
||||
|
||||
public render() {
|
||||
public override render() {
|
||||
const { workspaces } = this.props;
|
||||
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map((workspace) => {
|
||||
let sparkPoolsMenuProps: IContextualMenuProps = {
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface CollapsiblePanelProps {
|
||||
}
|
||||
|
||||
export class CollapsiblePanel extends React.Component<CollapsiblePanelProps> {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className={`collapsiblePanel ${this.props.isCollapsed ? "paneCollapsed" : ""}`}>
|
||||
{!this.props.isCollapsed ? this.getExpandedFragment() : this.getCollapsedFragment()}
|
||||
|
||||
@@ -24,13 +24,13 @@ export class CollapsibleSectionComponent extends React.Component<CollapsibleSect
|
||||
this.setState({ isExpanded: !this.state.isExpanded });
|
||||
};
|
||||
|
||||
public componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
if (this.state.isExpanded && this.props.onExpand) {
|
||||
this.props.onExpand();
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Stack
|
||||
|
||||
@@ -129,7 +129,7 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
|
||||
private dropdownElt: HTMLElement;
|
||||
private expandButtonElt: HTMLElement;
|
||||
|
||||
public componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
if (!this.dropdownElt || !this.expandButtonElt) {
|
||||
return;
|
||||
}
|
||||
@@ -243,7 +243,7 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
|
||||
);
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
let mainClassName = "commandButtonComponent";
|
||||
if (this.props.disabled) {
|
||||
mainClassName += " commandDisabled";
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface DefaultDirectoryDropdownProps {
|
||||
export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDirectoryDropdownProps> {
|
||||
public static readonly lastVisitedKey: string = "lastVisited";
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const lastVisitedOption: IDropdownOption = {
|
||||
key: DefaultDirectoryDropdownComponent.lastVisitedKey,
|
||||
text: "Sign in to your last visited directory",
|
||||
|
||||
@@ -36,7 +36,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
||||
};
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const { directories: originalItems, selectedDirectoryId } = this.props;
|
||||
const { filterText } = this.state;
|
||||
const filteredItems =
|
||||
|
||||
@@ -1959,7 +1959,7 @@ exports[`test render renders with filters 1`] = `
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
<FocusRects />
|
||||
<Component />
|
||||
</BaseButton>
|
||||
</DefaultButton>
|
||||
</CustomizedDefaultButton>
|
||||
|
||||
@@ -56,7 +56,7 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
|
||||
public ariaLabel: string;
|
||||
public buttonText: string;
|
||||
public newItem: ko.Observable<string>;
|
||||
public isTemplateReady: ko.Observable<boolean>;
|
||||
public override isTemplateReady: ko.Observable<boolean>;
|
||||
public listItems: ko.ObservableArray<DynamicListItem>;
|
||||
|
||||
public constructor(options: DynamicListParams) {
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface EditorParams extends JsonEditorParams {
|
||||
*/
|
||||
// TODO: Ideally, JsonEditorViewModel should extend EditorViewModel and not the other way around
|
||||
class EditorViewModel extends JsonEditorViewModel {
|
||||
public params: EditorParams;
|
||||
public override params: EditorParams;
|
||||
private static providerRegistered: string[] = [];
|
||||
|
||||
public constructor(params: EditorParams) {
|
||||
@@ -44,11 +44,11 @@ class EditorViewModel extends JsonEditorViewModel {
|
||||
});
|
||||
}
|
||||
|
||||
protected getEditorLanguage(): string {
|
||||
protected override getEditorLanguage(): string {
|
||||
return this.params.contentType;
|
||||
}
|
||||
|
||||
protected async registerCompletionItemProvider() {
|
||||
protected override async registerCompletionItemProvider() {
|
||||
if (EditorViewModel.providerRegistered.indexOf("sql") < 0) {
|
||||
const { SqlCompletionItemProvider } = await import("@azure/cosmos-language-service");
|
||||
const monaco = await loadMonaco();
|
||||
@@ -57,7 +57,7 @@ class EditorViewModel extends JsonEditorViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
protected async getErrorMarkers(input: string): Promise<monaco.editor.IMarkerData[]> {
|
||||
protected override async getErrorMarkers(input: string): Promise<monaco.editor.IMarkerData[]> {
|
||||
const { ErrorMarkProvider } = await import("@azure/cosmos-language-service");
|
||||
return ErrorMarkProvider.getErrorMark(input);
|
||||
}
|
||||
|
||||
@@ -21,20 +21,20 @@ export class EditorReact extends React.Component<EditorReactProps> {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.createEditor(this.configureEditor.bind(this));
|
||||
}
|
||||
|
||||
public shouldComponentUpdate(): boolean {
|
||||
public override shouldComponentUpdate(): boolean {
|
||||
// Prevents component re-rendering
|
||||
return false;
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
public override componentWillUnmount(): void {
|
||||
this.selectionListener && this.selectionListener.dispose();
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return <div className="jsonEditor" ref={(elt: HTMLElement) => this.setRef(elt)} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
|
||||
};
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const textFieldProps: ITextFieldProps = {
|
||||
placeholder: AddRepoComponent.TextFieldPlaceholder,
|
||||
autoFocus: true,
|
||||
|
||||
@@ -49,7 +49,7 @@ export class AuthorizeAccessComponent extends React.Component<
|
||||
};
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const choiceGroupProps: IChoiceGroupProps = {
|
||||
options: [
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ export class GitHubReposComponent extends React.Component<GitHubReposComponentPr
|
||||
private static readonly OKButtonText = "OK";
|
||||
private static readonly CancelButtonText = "Cancel";
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const content: JSX.Element = this.props.showAuthorizeAccess ? (
|
||||
<AuthorizeAccessComponent {...this.props.authorizeAccessProps} />
|
||||
) : (
|
||||
|
||||
@@ -67,7 +67,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
|
||||
private static readonly DefaultBranchName = "master";
|
||||
private static readonly FooterIndex = -1;
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const pinnedReposListProps: IDetailsListProps = {
|
||||
styles: {
|
||||
contentWrapper: {
|
||||
|
||||
@@ -30,7 +30,7 @@ export class GalleryHeaderComponent extends React.Component {
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Stack
|
||||
tokens={{ childrenGap: 10 }}
|
||||
|
||||
@@ -113,7 +113,7 @@ export class InputTypeaheadComponent extends React.Component<
|
||||
* @param prevState
|
||||
* @param snapshot
|
||||
*/
|
||||
public componentDidUpdate(
|
||||
public override componentDidUpdate(
|
||||
prevProps: InputTypeaheadComponentProps,
|
||||
prevState: InputTypeaheadComponentState,
|
||||
snapshot: any
|
||||
@@ -127,11 +127,11 @@ export class InputTypeaheadComponent extends React.Component<
|
||||
/**
|
||||
* Executed once react is done building the DOM for this component
|
||||
*/
|
||||
public componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.initializeTypeahead();
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<span className="input-typeahead-container">
|
||||
<div
|
||||
|
||||
@@ -19,7 +19,7 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
super(props);
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="notebookTerminalContainer">
|
||||
<iframe
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
import {
|
||||
BaseButton,
|
||||
Button,
|
||||
DocumentCard,
|
||||
DocumentCardActivity,
|
||||
DocumentCardDetails,
|
||||
DocumentCardPreview,
|
||||
DocumentCardTitle,
|
||||
FontWeights,
|
||||
Icon,
|
||||
IconButton,
|
||||
IDocumentCardPreviewProps,
|
||||
IDocumentCardStyles,
|
||||
Image,
|
||||
ImageFit,
|
||||
Link,
|
||||
Persona,
|
||||
Separator,
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
Text,
|
||||
TooltipHost,
|
||||
} from "@fluentui/react";
|
||||
import { Card } from "@uifabric/react-cards";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import CosmosDBLogo from "../../../../../images/CosmosDB-logo.svg";
|
||||
import { IGalleryItem } from "../../../../Juno/JunoClient";
|
||||
@@ -51,6 +48,7 @@ export const GalleryCardComponent: FunctionComponent<GalleryCardComponentProps>
|
||||
const CARD_WIDTH = 256;
|
||||
const cardImageHeight = 144;
|
||||
const cardDescriptionMaxChars = 80;
|
||||
const cardItemGapBig = 10;
|
||||
const cardItemGapSmall = 8;
|
||||
const cardDeleteSpinnerHeight = 360;
|
||||
const smallTextLineHeight = 18;
|
||||
@@ -66,9 +64,9 @@ export const GalleryCardComponent: FunctionComponent<GalleryCardComponentProps>
|
||||
const dateString = new Date(data.created).toLocaleString("default", options);
|
||||
const cardTitle = FileSystemUtil.stripExtension(data.name, "ipynb");
|
||||
|
||||
const renderTruncated = (text: string, totalLength: number): string => {
|
||||
let truncatedDescription = text.substr(0, totalLength);
|
||||
if (text.length > totalLength) {
|
||||
const renderTruncatedDescription = (): string => {
|
||||
let truncatedDescription = data.description.substr(0, cardDescriptionMaxChars);
|
||||
if (data.description.length > cardDescriptionMaxChars) {
|
||||
truncatedDescription = `${truncatedDescription} ...`;
|
||||
}
|
||||
return truncatedDescription;
|
||||
@@ -122,35 +120,42 @@ export const GalleryCardComponent: FunctionComponent<GalleryCardComponentProps>
|
||||
event.preventDefault();
|
||||
activate();
|
||||
};
|
||||
const DocumentCardActivityPeople = [{ name: data.author, profileImageSrc: data.isSample && CosmosDBLogo }];
|
||||
const previewProps: IDocumentCardPreviewProps = {
|
||||
previewImages: [
|
||||
{
|
||||
previewImageSrc: data.thumbnailUrl,
|
||||
imageFit: ImageFit.cover,
|
||||
width: CARD_WIDTH,
|
||||
height: cardImageHeight,
|
||||
},
|
||||
],
|
||||
};
|
||||
const cardStyles: IDocumentCardStyles = {
|
||||
root: { display: "inline-block", marginRight: 20, width: CARD_WIDTH },
|
||||
};
|
||||
|
||||
return (
|
||||
<DocumentCard aria-label={cardTitle} styles={cardStyles} onClick={onClick}>
|
||||
<Card
|
||||
style={{ background: "white" }}
|
||||
aria-label={cardTitle}
|
||||
data-is-focusable="true"
|
||||
tokens={{ width: CARD_WIDTH, childrenGap: 0 }}
|
||||
onClick={(event) => handlerOnClick(event, onClick)}
|
||||
>
|
||||
{isDeletingPublishedNotebook && (
|
||||
<Spinner
|
||||
size={SpinnerSize.large}
|
||||
label={`Deleting '${cardTitle}'`}
|
||||
styles={{ root: { height: cardDeleteSpinnerHeight } }}
|
||||
/>
|
||||
<Card.Item tokens={{ padding: cardItemGapBig }}>
|
||||
<Spinner
|
||||
size={SpinnerSize.large}
|
||||
label={`Deleting '${cardTitle}'`}
|
||||
styles={{ root: { height: cardDeleteSpinnerHeight } }}
|
||||
/>
|
||||
</Card.Item>
|
||||
)}
|
||||
{!isDeletingPublishedNotebook && (
|
||||
<>
|
||||
<DocumentCardActivity activity={dateString} people={DocumentCardActivityPeople} />
|
||||
<DocumentCardPreview {...previewProps} />
|
||||
<DocumentCardDetails>
|
||||
<Text variant="small" nowrap styles={{ root: { height: smallTextLineHeight, padding: "2px 16px" } }}>
|
||||
<Card.Item tokens={{ padding: cardItemGapBig }}>
|
||||
<Persona imageUrl={data.isSample && CosmosDBLogo} text={data.author} secondaryText={dateString} />
|
||||
</Card.Item>
|
||||
|
||||
<Card.Item>
|
||||
<Image
|
||||
src={data.thumbnailUrl}
|
||||
width={CARD_WIDTH}
|
||||
height={cardImageHeight}
|
||||
imageFit={ImageFit.cover}
|
||||
alt={`${cardTitle} cover image`}
|
||||
/>
|
||||
</Card.Item>
|
||||
|
||||
<Card.Section styles={{ root: { padding: cardItemGapBig } }}>
|
||||
<Text variant="small" nowrap styles={{ root: { height: smallTextLineHeight } }}>
|
||||
{data.tags ? (
|
||||
data.tags.map((tag, index, array) => (
|
||||
<span key={tag}>
|
||||
@@ -162,22 +167,43 @@ export const GalleryCardComponent: FunctionComponent<GalleryCardComponentProps>
|
||||
<br />
|
||||
)}
|
||||
</Text>
|
||||
<DocumentCardTitle title={renderTruncated(cardTitle, 20)} shouldTruncate />
|
||||
<DocumentCardTitle
|
||||
title={renderTruncated(data.description, cardDescriptionMaxChars)}
|
||||
showAsSecondaryTitle
|
||||
/>
|
||||
<span style={{ padding: "8px 16px" }}>
|
||||
|
||||
<Text
|
||||
styles={{
|
||||
root: {
|
||||
fontWeight: FontWeights.semibold,
|
||||
paddingTop: cardItemGapSmall,
|
||||
paddingBottom: cardItemGapSmall,
|
||||
},
|
||||
}}
|
||||
nowrap
|
||||
>
|
||||
{cardTitle}
|
||||
</Text>
|
||||
|
||||
<Text variant="small" styles={{ root: { height: smallTextLineHeight * 2 } }}>
|
||||
{renderTruncatedDescription()}
|
||||
</Text>
|
||||
|
||||
<span>
|
||||
{data.views !== undefined && generateIconText("RedEye", data.views.toString())}
|
||||
{data.downloads !== undefined && generateIconText("Download", data.downloads.toString())}
|
||||
{data.favorites !== undefined && generateIconText("Heart", data.favorites.toString())}
|
||||
</span>
|
||||
</DocumentCardDetails>
|
||||
</Card.Section>
|
||||
|
||||
{cardButtonsVisible && (
|
||||
<DocumentCardDetails>
|
||||
<Card.Section
|
||||
styles={{
|
||||
root: {
|
||||
marginLeft: cardItemGapBig,
|
||||
marginRight: cardItemGapBig,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Separator styles={{ root: { padding: 0, height: 1 } }} />
|
||||
|
||||
<span style={{ padding: "0px 16px" }}>
|
||||
<span>
|
||||
{isFavorite !== undefined &&
|
||||
generateIconButtonWithTooltip(
|
||||
isFavorite ? "HeartFill" : "Heart",
|
||||
@@ -196,10 +222,10 @@ export const GalleryCardComponent: FunctionComponent<GalleryCardComponentProps>
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
</DocumentCardDetails>
|
||||
</Card.Section>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</DocumentCard>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,49 +1,59 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`GalleryCardComponent renders 1`] = `
|
||||
<StyledDocumentCardBase
|
||||
<Card
|
||||
aria-label="name"
|
||||
styles={
|
||||
data-is-focusable="true"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"root": Object {
|
||||
"display": "inline-block",
|
||||
"marginRight": 20,
|
||||
"width": 256,
|
||||
},
|
||||
"background": "white",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 0,
|
||||
"width": 256,
|
||||
}
|
||||
}
|
||||
>
|
||||
<StyledDocumentCardActivityBase
|
||||
activity="Invalid Date"
|
||||
people={
|
||||
Array [
|
||||
Object {
|
||||
"name": "author",
|
||||
"profileImageSrc": false,
|
||||
},
|
||||
]
|
||||
<CardItem
|
||||
tokens={
|
||||
Object {
|
||||
"padding": 10,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledDocumentCardPreviewBase
|
||||
previewImages={
|
||||
Array [
|
||||
Object {
|
||||
"height": 144,
|
||||
"imageFit": 2,
|
||||
"previewImageSrc": "thumbnailUrl",
|
||||
"width": 256,
|
||||
>
|
||||
<StyledPersonaBase
|
||||
imageUrl={false}
|
||||
secondaryText="Invalid Date"
|
||||
text="author"
|
||||
/>
|
||||
</CardItem>
|
||||
<CardItem>
|
||||
<Image
|
||||
alt="name cover image"
|
||||
height={144}
|
||||
imageFit={2}
|
||||
src="thumbnailUrl"
|
||||
width={256}
|
||||
/>
|
||||
</CardItem>
|
||||
<CardSection
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"padding": 10,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledDocumentCardDetailsBase>
|
||||
>
|
||||
<Text
|
||||
nowrap={true}
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"height": 18,
|
||||
"padding": "2px 16px",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -59,21 +69,33 @@ exports[`GalleryCardComponent renders 1`] = `
|
||||
</StyledLinkBase>
|
||||
</span>
|
||||
</Text>
|
||||
<StyledDocumentCardTitleBase
|
||||
shouldTruncate={true}
|
||||
title="name"
|
||||
/>
|
||||
<StyledDocumentCardTitleBase
|
||||
showAsSecondaryTitle={true}
|
||||
title="description"
|
||||
/>
|
||||
<span
|
||||
style={
|
||||
<Text
|
||||
nowrap={true}
|
||||
styles={
|
||||
Object {
|
||||
"padding": "8px 16px",
|
||||
"root": Object {
|
||||
"fontWeight": 600,
|
||||
"paddingBottom": 8,
|
||||
"paddingTop": 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
name
|
||||
</Text>
|
||||
<Text
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"height": 36,
|
||||
},
|
||||
}
|
||||
}
|
||||
variant="small"
|
||||
>
|
||||
description
|
||||
</Text>
|
||||
<span>
|
||||
<Text
|
||||
styles={
|
||||
Object {
|
||||
@@ -147,8 +169,17 @@ exports[`GalleryCardComponent renders 1`] = `
|
||||
0
|
||||
</Text>
|
||||
</span>
|
||||
</StyledDocumentCardDetailsBase>
|
||||
<StyledDocumentCardDetailsBase>
|
||||
</CardSection>
|
||||
<CardSection
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"marginLeft": 10,
|
||||
"marginRight": 10,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<Separator
|
||||
styles={
|
||||
Object {
|
||||
@@ -159,13 +190,7 @@ exports[`GalleryCardComponent renders 1`] = `
|
||||
}
|
||||
}
|
||||
/>
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"padding": "0px 16px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<StyledTooltipHostBase
|
||||
calloutProps={
|
||||
Object {
|
||||
@@ -251,6 +276,6 @@ exports[`GalleryCardComponent renders 1`] = `
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
</span>
|
||||
</StyledDocumentCardDetailsBase>
|
||||
</StyledDocumentCardBase>
|
||||
</CardSection>
|
||||
</Card>
|
||||
`;
|
||||
|
||||
@@ -42,7 +42,7 @@ export class GalleryAndNotebookViewerComponent extends React.Component<
|
||||
};
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
if (this.state.notebookUrl) {
|
||||
const props: NotebookViewerComponentProps = {
|
||||
container: this.props.container,
|
||||
|
||||
@@ -34,6 +34,7 @@ import { CodeOfConductComponent } from "./CodeOfConductComponent";
|
||||
import "./GalleryViewerComponent.less";
|
||||
import { InfoComponent } from "./InfoComponent/InfoComponent";
|
||||
|
||||
const CARD_WIDTH = 256;
|
||||
export interface GalleryViewerComponentProps {
|
||||
container?: Explorer;
|
||||
junoClient: JunoClient;
|
||||
@@ -86,7 +87,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
public static readonly PublishedTitle = "My published work";
|
||||
|
||||
private static readonly rowsPerPage = 5;
|
||||
private static readonly CARD_WIDTH = 256;
|
||||
|
||||
private static readonly mostViewedText = "Most viewed";
|
||||
private static readonly mostDownloadedText = "Most downloaded";
|
||||
private static readonly mostFavoritedText = "Most favorited";
|
||||
@@ -148,7 +149,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
this.loadFavoriteNotebooks(this.state.searchText, this.state.sortBy, false); // Need this to show correct favorite button state
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
this.traceViewGallery();
|
||||
|
||||
const tabs: GalleryTabInfo[] = [
|
||||
@@ -643,7 +644,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
|
||||
private getPageSpecification = (itemIndex?: number, visibleRect?: IRectangle): IPageSpecification => {
|
||||
if (itemIndex === 0) {
|
||||
this.columnCount = Math.floor(visibleRect.width / GalleryViewerComponent.CARD_WIDTH) || this.columnCount;
|
||||
this.columnCount = Math.floor(visibleRect.width / CARD_WIDTH) || this.columnCount;
|
||||
this.rowCount = GalleryViewerComponent.rowsPerPage;
|
||||
}
|
||||
|
||||
@@ -671,7 +672,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ float: "left", padding: 5 }}>
|
||||
<div style={{ float: "left", padding: 10 }}>
|
||||
<GalleryCardComponent {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ export class InfoComponent extends React.Component<InfoComponentProps> {
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<HoverCard plainCardProps={{ onRenderPlainCard: this.onHover }} instantOpenOnClick type={HoverCardType.plain}>
|
||||
<div className="infoPanelMain">
|
||||
|
||||
@@ -41,7 +41,7 @@ export class NotebookMetadataComponent extends React.Component<NotebookMetadataC
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
|
||||
@@ -132,7 +132,7 @@ export class NotebookViewerComponent
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="notebookViewerContainer">
|
||||
{this.props.backNavigationText !== undefined ? (
|
||||
|
||||
@@ -62,7 +62,7 @@ export class QueriesGridComponent extends React.Component<QueriesGridComponentPr
|
||||
this.selection.setItems(this.state.filteredResults);
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: QueriesGridComponentProps, prevState: QueriesGridComponentState): void {
|
||||
public override componentDidUpdate(prevProps: QueriesGridComponentProps, prevState: QueriesGridComponentState): void {
|
||||
this.selection.setItems(
|
||||
this.state.filteredResults,
|
||||
!_.isEqual(prevState.filteredResults, this.state.filteredResults)
|
||||
@@ -79,11 +79,11 @@ export class QueriesGridComponent extends React.Component<QueriesGridComponentPr
|
||||
}
|
||||
|
||||
// fetched saved queries when panel open
|
||||
public componentDidMount() {
|
||||
public override componentDidMount() {
|
||||
this.fetchSavedQueries();
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
if (this.state.queries.length === 0) {
|
||||
return this.renderBannerComponent();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface RadioSwitchComponentProps {
|
||||
}
|
||||
|
||||
export class RadioSwitchComponent extends React.Component<RadioSwitchComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="radioSwitchComponent">
|
||||
{this.props.choices.map((choice: Choice) => (
|
||||
|
||||
@@ -17,7 +17,7 @@ export abstract class ResizeSensorComponent<P, S> extends React.Component<P, S>
|
||||
protected abstract onDimensionsChanged(width: number, height: number): void;
|
||||
protected abstract getSensorTarget(): HTMLElement;
|
||||
|
||||
public componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
if (this.isSensing) {
|
||||
return;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export abstract class ResizeSensorComponent<P, S> extends React.Component<P, S>
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
public override componentWillUnmount(): void {
|
||||
if (!!this.resizeSensor) {
|
||||
this.resizeSensor.detach();
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
if (this.isCollectionSettingsTab) {
|
||||
this.refreshIndexTransformationProgress();
|
||||
this.loadMongoIndexes();
|
||||
@@ -226,7 +226,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
if (this.props.settingsTab.isActive()) {
|
||||
this.props.settingsTab.getContainer().onUpdateTabsButtons(this.getTabsButtons());
|
||||
}
|
||||
@@ -879,7 +879,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
return mongoIndexingPolicyAADError;
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const scaleComponentProps: ScaleComponentProps = {
|
||||
collection: this.collection,
|
||||
database: this.database,
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from "./SettingsRenderUtils";
|
||||
|
||||
class SettingsRenderUtilsTestComponent extends React.Component {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const estimatedSpendingColumns: IColumn[] = [
|
||||
{ key: "costType", name: "", fieldName: "costType", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{ key: "hourly", name: "Hourly", fieldName: "hourly", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
|
||||
@@ -40,11 +40,11 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
{ key: DataModels.ConflictResolutionMode.Custom, text: "Merge Procedure (custom)" },
|
||||
];
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
/>
|
||||
);
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...subComponentStackProps}>
|
||||
{this.getConflictResolutionModeComponent()}
|
||||
|
||||
@@ -38,7 +38,7 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
if (this.props.shouldDiscardIndexingPolicy) {
|
||||
this.resetIndexingPolicyEditor();
|
||||
this.props.resetShouldDiscardIndexingPolicy();
|
||||
@@ -46,7 +46,7 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.resetIndexingPolicyEditor();
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
@@ -112,7 +112,7 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
}
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<IndexingPolicyRefreshComponent
|
||||
|
||||
@@ -52,7 +52,7 @@ export class IndexingPolicyRefreshComponent extends React.Component<
|
||||
}
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return this.renderIndexTransformationWarning() ? (
|
||||
<MessageBar messageBarType={MessageBarType.warning}>{this.renderIndexTransformationWarning()}</MessageBar>
|
||||
) : (
|
||||
|
||||
@@ -61,7 +61,7 @@ export class AddMongoIndexComponent extends React.Component<AddMongoIndexCompone
|
||||
this.descriptionTextField.focus();
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...mongoWarningStackProps}>
|
||||
<Stack horizontal tokens={addMongoIndexSubElementsTokens}>
|
||||
|
||||
@@ -89,14 +89,14 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
},
|
||||
];
|
||||
|
||||
componentDidUpdate(prevProps: MongoIndexingPolicyComponentProps): void {
|
||||
public override componentDidUpdate(prevProps: MongoIndexingPolicyComponentProps): void {
|
||||
if (this.props.indexesToAdd.length > prevProps.indexesToAdd.length) {
|
||||
this.addMongoIndexComponentRefs[prevProps.indexesToAdd.length]?.current?.focus();
|
||||
}
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
if (this.props.mongoIndexes) {
|
||||
if (this.hasCompoundIndex()) {
|
||||
return mongoCompoundIndexNotSupportedMessage;
|
||||
|
||||
@@ -216,7 +216,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
);
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...subComponentStackProps}>
|
||||
{this.isFreeTierAccount() && (
|
||||
|
||||
@@ -72,11 +72,11 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
|
||||
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...subComponentStackProps}>
|
||||
{userContext.apiType !== "Cassandra" && this.getTtlComponent()}
|
||||
|
||||
@@ -96,11 +96,11 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
{ key: "false", text: "Manual" },
|
||||
];
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
@@ -627,7 +627,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...checkBoxAndInputStackProps}>
|
||||
{this.renderWarningMessage()}
|
||||
|
||||
@@ -252,7 +252,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
|
||||
>
|
||||
capacity calculator
|
||||
|
||||
<FontIcon
|
||||
<Component
|
||||
iconName="NavigateExternalInline"
|
||||
/>
|
||||
</StyledLinkBase>
|
||||
@@ -526,7 +526,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
|
||||
>
|
||||
capacity calculator
|
||||
|
||||
<FontIcon
|
||||
<Component
|
||||
iconName="NavigateExternalInline"
|
||||
/>
|
||||
</StyledLinkBase>
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface ToolTipLabelComponentProps {
|
||||
const iconButtonStyles: Partial<IIconStyles> = { root: { marginBottom: -3 } };
|
||||
|
||||
export class ToolTipLabelComponent extends React.Component<ToolTipLabelComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Stack horizontal verticalAlign="center" tokens={toolTipLabelStackTokens}>
|
||||
|
||||
@@ -75,6 +75,89 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
AddCollectionPane {
|
||||
"_isSynapseLinkEnabled": [Function],
|
||||
"autoPilotThroughput": [Function],
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canRequestSupport": [Function],
|
||||
"collectionId": [Function],
|
||||
"collectionIdTitle": [Function],
|
||||
"collectionWithThroughputInShared": [Function],
|
||||
"collectionWithThroughputInSharedTitle": [Function],
|
||||
"container": [Circular],
|
||||
"costsVisible": [Function],
|
||||
"databaseCreateNew": [Function],
|
||||
"databaseCreateNewShared": [Function],
|
||||
"databaseHasSharedOffer": [Function],
|
||||
"databaseId": [Function],
|
||||
"databaseIds": [Function],
|
||||
"dedicatedRequestUnitsUsageCost": [Function],
|
||||
"displayCollectionThroughput": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"formWarnings": [Function],
|
||||
"freeTierExceedThroughputTooltip": [Function],
|
||||
"id": "addcollectionpane",
|
||||
"isAnalyticalStorageOn": [Function],
|
||||
"isAutoPilotSelected": [Function],
|
||||
"isEnableMongoCapabilityEnabled": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isFixedStorageSelected": [Function],
|
||||
"isFreeTierAccount": [Function],
|
||||
"isNonTableApi": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isSharedAutoPilotSelected": [Function],
|
||||
"isSynapseLinkSupported": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"isTryCosmosDBSubscription": [Function],
|
||||
"isUnlimitedStorageSelected": [Function],
|
||||
"largePartitionKey": [Function],
|
||||
"lowerCasePartitionKeyName": [Function],
|
||||
"maxCollectionsReached": [Function],
|
||||
"maxCollectionsReachedMessage": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"partitionKey": [Function],
|
||||
"partitionKeyName": [Function],
|
||||
"partitionKeyPattern": [Function],
|
||||
"partitionKeyPlaceholder": [Function],
|
||||
"partitionKeyTitle": [Function],
|
||||
"partitionKeyVisible": [Function],
|
||||
"requestUnitsUsageCost": [Function],
|
||||
"ruToolTipText": [Function],
|
||||
"sharedAutoPilotThroughput": [Function],
|
||||
"sharedThroughputRangeText": [Function],
|
||||
"shouldCreateMongoWildcardIndex": [Function],
|
||||
"shouldUseDatabaseThroughput": [Function],
|
||||
"showAnalyticalStore": [Function],
|
||||
"showEnableSynapseLink": [Function],
|
||||
"showIndexingOptionsForSharedThroughput": [Function],
|
||||
"showUpsellMessage": [Function],
|
||||
"storage": [Function],
|
||||
"throughputDatabase": [Function],
|
||||
"throughputMultiPartition": [Function],
|
||||
"throughputRangeText": [Function],
|
||||
"throughputSinglePartition": [Function],
|
||||
"throughputSpendAck": [Function],
|
||||
"throughputSpendAckText": [Function],
|
||||
"throughputSpendAckVisible": [Function],
|
||||
"title": [Function],
|
||||
"ttl90DaysEnabled": [Function],
|
||||
"uniqueKeys": [Function],
|
||||
"uniqueKeysPlaceholder": [Function],
|
||||
"uniqueKeysVisible": [Function],
|
||||
"upsellAnchorText": [Function],
|
||||
"upsellAnchorUrl": [Function],
|
||||
"upsellMessage": [Function],
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"useIndexingForSharedThroughput": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
GraphStylingPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
@@ -143,6 +226,89 @@ exports[`SettingsComponent renders 1`] = `
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"addCollectionPane": AddCollectionPane {
|
||||
"_isSynapseLinkEnabled": [Function],
|
||||
"autoPilotThroughput": [Function],
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canRequestSupport": [Function],
|
||||
"collectionId": [Function],
|
||||
"collectionIdTitle": [Function],
|
||||
"collectionWithThroughputInShared": [Function],
|
||||
"collectionWithThroughputInSharedTitle": [Function],
|
||||
"container": [Circular],
|
||||
"costsVisible": [Function],
|
||||
"databaseCreateNew": [Function],
|
||||
"databaseCreateNewShared": [Function],
|
||||
"databaseHasSharedOffer": [Function],
|
||||
"databaseId": [Function],
|
||||
"databaseIds": [Function],
|
||||
"dedicatedRequestUnitsUsageCost": [Function],
|
||||
"displayCollectionThroughput": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"formWarnings": [Function],
|
||||
"freeTierExceedThroughputTooltip": [Function],
|
||||
"id": "addcollectionpane",
|
||||
"isAnalyticalStorageOn": [Function],
|
||||
"isAutoPilotSelected": [Function],
|
||||
"isEnableMongoCapabilityEnabled": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isFixedStorageSelected": [Function],
|
||||
"isFreeTierAccount": [Function],
|
||||
"isNonTableApi": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isSharedAutoPilotSelected": [Function],
|
||||
"isSynapseLinkSupported": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"isTryCosmosDBSubscription": [Function],
|
||||
"isUnlimitedStorageSelected": [Function],
|
||||
"largePartitionKey": [Function],
|
||||
"lowerCasePartitionKeyName": [Function],
|
||||
"maxCollectionsReached": [Function],
|
||||
"maxCollectionsReachedMessage": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"partitionKey": [Function],
|
||||
"partitionKeyName": [Function],
|
||||
"partitionKeyPattern": [Function],
|
||||
"partitionKeyPlaceholder": [Function],
|
||||
"partitionKeyTitle": [Function],
|
||||
"partitionKeyVisible": [Function],
|
||||
"requestUnitsUsageCost": [Function],
|
||||
"ruToolTipText": [Function],
|
||||
"sharedAutoPilotThroughput": [Function],
|
||||
"sharedThroughputRangeText": [Function],
|
||||
"shouldCreateMongoWildcardIndex": [Function],
|
||||
"shouldUseDatabaseThroughput": [Function],
|
||||
"showAnalyticalStore": [Function],
|
||||
"showEnableSynapseLink": [Function],
|
||||
"showIndexingOptionsForSharedThroughput": [Function],
|
||||
"showUpsellMessage": [Function],
|
||||
"storage": [Function],
|
||||
"throughputDatabase": [Function],
|
||||
"throughputMultiPartition": [Function],
|
||||
"throughputRangeText": [Function],
|
||||
"throughputSinglePartition": [Function],
|
||||
"throughputSpendAck": [Function],
|
||||
"throughputSpendAckText": [Function],
|
||||
"throughputSpendAckVisible": [Function],
|
||||
"title": [Function],
|
||||
"ttl90DaysEnabled": [Function],
|
||||
"uniqueKeys": [Function],
|
||||
"uniqueKeysPlaceholder": [Function],
|
||||
"uniqueKeysVisible": [Function],
|
||||
"upsellAnchorText": [Function],
|
||||
"upsellAnchorUrl": [Function],
|
||||
"upsellMessage": [Function],
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"useIndexingForSharedThroughput": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"addCollectionText": [Function],
|
||||
"addDatabasePane": AddDatabasePane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
@@ -1187,6 +1353,89 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
AddCollectionPane {
|
||||
"_isSynapseLinkEnabled": [Function],
|
||||
"autoPilotThroughput": [Function],
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canRequestSupport": [Function],
|
||||
"collectionId": [Function],
|
||||
"collectionIdTitle": [Function],
|
||||
"collectionWithThroughputInShared": [Function],
|
||||
"collectionWithThroughputInSharedTitle": [Function],
|
||||
"container": [Circular],
|
||||
"costsVisible": [Function],
|
||||
"databaseCreateNew": [Function],
|
||||
"databaseCreateNewShared": [Function],
|
||||
"databaseHasSharedOffer": [Function],
|
||||
"databaseId": [Function],
|
||||
"databaseIds": [Function],
|
||||
"dedicatedRequestUnitsUsageCost": [Function],
|
||||
"displayCollectionThroughput": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"formWarnings": [Function],
|
||||
"freeTierExceedThroughputTooltip": [Function],
|
||||
"id": "addcollectionpane",
|
||||
"isAnalyticalStorageOn": [Function],
|
||||
"isAutoPilotSelected": [Function],
|
||||
"isEnableMongoCapabilityEnabled": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isFixedStorageSelected": [Function],
|
||||
"isFreeTierAccount": [Function],
|
||||
"isNonTableApi": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isSharedAutoPilotSelected": [Function],
|
||||
"isSynapseLinkSupported": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"isTryCosmosDBSubscription": [Function],
|
||||
"isUnlimitedStorageSelected": [Function],
|
||||
"largePartitionKey": [Function],
|
||||
"lowerCasePartitionKeyName": [Function],
|
||||
"maxCollectionsReached": [Function],
|
||||
"maxCollectionsReachedMessage": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"partitionKey": [Function],
|
||||
"partitionKeyName": [Function],
|
||||
"partitionKeyPattern": [Function],
|
||||
"partitionKeyPlaceholder": [Function],
|
||||
"partitionKeyTitle": [Function],
|
||||
"partitionKeyVisible": [Function],
|
||||
"requestUnitsUsageCost": [Function],
|
||||
"ruToolTipText": [Function],
|
||||
"sharedAutoPilotThroughput": [Function],
|
||||
"sharedThroughputRangeText": [Function],
|
||||
"shouldCreateMongoWildcardIndex": [Function],
|
||||
"shouldUseDatabaseThroughput": [Function],
|
||||
"showAnalyticalStore": [Function],
|
||||
"showEnableSynapseLink": [Function],
|
||||
"showIndexingOptionsForSharedThroughput": [Function],
|
||||
"showUpsellMessage": [Function],
|
||||
"storage": [Function],
|
||||
"throughputDatabase": [Function],
|
||||
"throughputMultiPartition": [Function],
|
||||
"throughputRangeText": [Function],
|
||||
"throughputSinglePartition": [Function],
|
||||
"throughputSpendAck": [Function],
|
||||
"throughputSpendAckText": [Function],
|
||||
"throughputSpendAckVisible": [Function],
|
||||
"title": [Function],
|
||||
"ttl90DaysEnabled": [Function],
|
||||
"uniqueKeys": [Function],
|
||||
"uniqueKeysPlaceholder": [Function],
|
||||
"uniqueKeysVisible": [Function],
|
||||
"upsellAnchorText": [Function],
|
||||
"upsellAnchorUrl": [Function],
|
||||
"upsellMessage": [Function],
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"useIndexingForSharedThroughput": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
GraphStylingPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
@@ -1255,6 +1504,89 @@ exports[`SettingsComponent renders 1`] = `
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"addCollectionPane": AddCollectionPane {
|
||||
"_isSynapseLinkEnabled": [Function],
|
||||
"autoPilotThroughput": [Function],
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canRequestSupport": [Function],
|
||||
"collectionId": [Function],
|
||||
"collectionIdTitle": [Function],
|
||||
"collectionWithThroughputInShared": [Function],
|
||||
"collectionWithThroughputInSharedTitle": [Function],
|
||||
"container": [Circular],
|
||||
"costsVisible": [Function],
|
||||
"databaseCreateNew": [Function],
|
||||
"databaseCreateNewShared": [Function],
|
||||
"databaseHasSharedOffer": [Function],
|
||||
"databaseId": [Function],
|
||||
"databaseIds": [Function],
|
||||
"dedicatedRequestUnitsUsageCost": [Function],
|
||||
"displayCollectionThroughput": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"formWarnings": [Function],
|
||||
"freeTierExceedThroughputTooltip": [Function],
|
||||
"id": "addcollectionpane",
|
||||
"isAnalyticalStorageOn": [Function],
|
||||
"isAutoPilotSelected": [Function],
|
||||
"isEnableMongoCapabilityEnabled": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isFixedStorageSelected": [Function],
|
||||
"isFreeTierAccount": [Function],
|
||||
"isNonTableApi": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isSharedAutoPilotSelected": [Function],
|
||||
"isSynapseLinkSupported": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"isTryCosmosDBSubscription": [Function],
|
||||
"isUnlimitedStorageSelected": [Function],
|
||||
"largePartitionKey": [Function],
|
||||
"lowerCasePartitionKeyName": [Function],
|
||||
"maxCollectionsReached": [Function],
|
||||
"maxCollectionsReachedMessage": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"partitionKey": [Function],
|
||||
"partitionKeyName": [Function],
|
||||
"partitionKeyPattern": [Function],
|
||||
"partitionKeyPlaceholder": [Function],
|
||||
"partitionKeyTitle": [Function],
|
||||
"partitionKeyVisible": [Function],
|
||||
"requestUnitsUsageCost": [Function],
|
||||
"ruToolTipText": [Function],
|
||||
"sharedAutoPilotThroughput": [Function],
|
||||
"sharedThroughputRangeText": [Function],
|
||||
"shouldCreateMongoWildcardIndex": [Function],
|
||||
"shouldUseDatabaseThroughput": [Function],
|
||||
"showAnalyticalStore": [Function],
|
||||
"showEnableSynapseLink": [Function],
|
||||
"showIndexingOptionsForSharedThroughput": [Function],
|
||||
"showUpsellMessage": [Function],
|
||||
"storage": [Function],
|
||||
"throughputDatabase": [Function],
|
||||
"throughputMultiPartition": [Function],
|
||||
"throughputRangeText": [Function],
|
||||
"throughputSinglePartition": [Function],
|
||||
"throughputSpendAck": [Function],
|
||||
"throughputSpendAckText": [Function],
|
||||
"throughputSpendAckVisible": [Function],
|
||||
"title": [Function],
|
||||
"ttl90DaysEnabled": [Function],
|
||||
"uniqueKeys": [Function],
|
||||
"uniqueKeysPlaceholder": [Function],
|
||||
"uniqueKeysVisible": [Function],
|
||||
"upsellAnchorText": [Function],
|
||||
"upsellAnchorUrl": [Function],
|
||||
"upsellMessage": [Function],
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"useIndexingForSharedThroughput": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"addCollectionText": [Function],
|
||||
"addDatabasePane": AddDatabasePane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
@@ -2312,6 +2644,89 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
AddCollectionPane {
|
||||
"_isSynapseLinkEnabled": [Function],
|
||||
"autoPilotThroughput": [Function],
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canRequestSupport": [Function],
|
||||
"collectionId": [Function],
|
||||
"collectionIdTitle": [Function],
|
||||
"collectionWithThroughputInShared": [Function],
|
||||
"collectionWithThroughputInSharedTitle": [Function],
|
||||
"container": [Circular],
|
||||
"costsVisible": [Function],
|
||||
"databaseCreateNew": [Function],
|
||||
"databaseCreateNewShared": [Function],
|
||||
"databaseHasSharedOffer": [Function],
|
||||
"databaseId": [Function],
|
||||
"databaseIds": [Function],
|
||||
"dedicatedRequestUnitsUsageCost": [Function],
|
||||
"displayCollectionThroughput": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"formWarnings": [Function],
|
||||
"freeTierExceedThroughputTooltip": [Function],
|
||||
"id": "addcollectionpane",
|
||||
"isAnalyticalStorageOn": [Function],
|
||||
"isAutoPilotSelected": [Function],
|
||||
"isEnableMongoCapabilityEnabled": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isFixedStorageSelected": [Function],
|
||||
"isFreeTierAccount": [Function],
|
||||
"isNonTableApi": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isSharedAutoPilotSelected": [Function],
|
||||
"isSynapseLinkSupported": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"isTryCosmosDBSubscription": [Function],
|
||||
"isUnlimitedStorageSelected": [Function],
|
||||
"largePartitionKey": [Function],
|
||||
"lowerCasePartitionKeyName": [Function],
|
||||
"maxCollectionsReached": [Function],
|
||||
"maxCollectionsReachedMessage": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"partitionKey": [Function],
|
||||
"partitionKeyName": [Function],
|
||||
"partitionKeyPattern": [Function],
|
||||
"partitionKeyPlaceholder": [Function],
|
||||
"partitionKeyTitle": [Function],
|
||||
"partitionKeyVisible": [Function],
|
||||
"requestUnitsUsageCost": [Function],
|
||||
"ruToolTipText": [Function],
|
||||
"sharedAutoPilotThroughput": [Function],
|
||||
"sharedThroughputRangeText": [Function],
|
||||
"shouldCreateMongoWildcardIndex": [Function],
|
||||
"shouldUseDatabaseThroughput": [Function],
|
||||
"showAnalyticalStore": [Function],
|
||||
"showEnableSynapseLink": [Function],
|
||||
"showIndexingOptionsForSharedThroughput": [Function],
|
||||
"showUpsellMessage": [Function],
|
||||
"storage": [Function],
|
||||
"throughputDatabase": [Function],
|
||||
"throughputMultiPartition": [Function],
|
||||
"throughputRangeText": [Function],
|
||||
"throughputSinglePartition": [Function],
|
||||
"throughputSpendAck": [Function],
|
||||
"throughputSpendAckText": [Function],
|
||||
"throughputSpendAckVisible": [Function],
|
||||
"title": [Function],
|
||||
"ttl90DaysEnabled": [Function],
|
||||
"uniqueKeys": [Function],
|
||||
"uniqueKeysPlaceholder": [Function],
|
||||
"uniqueKeysVisible": [Function],
|
||||
"upsellAnchorText": [Function],
|
||||
"upsellAnchorUrl": [Function],
|
||||
"upsellMessage": [Function],
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"useIndexingForSharedThroughput": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
GraphStylingPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
@@ -2380,6 +2795,89 @@ exports[`SettingsComponent renders 1`] = `
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"addCollectionPane": AddCollectionPane {
|
||||
"_isSynapseLinkEnabled": [Function],
|
||||
"autoPilotThroughput": [Function],
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canRequestSupport": [Function],
|
||||
"collectionId": [Function],
|
||||
"collectionIdTitle": [Function],
|
||||
"collectionWithThroughputInShared": [Function],
|
||||
"collectionWithThroughputInSharedTitle": [Function],
|
||||
"container": [Circular],
|
||||
"costsVisible": [Function],
|
||||
"databaseCreateNew": [Function],
|
||||
"databaseCreateNewShared": [Function],
|
||||
"databaseHasSharedOffer": [Function],
|
||||
"databaseId": [Function],
|
||||
"databaseIds": [Function],
|
||||
"dedicatedRequestUnitsUsageCost": [Function],
|
||||
"displayCollectionThroughput": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"formWarnings": [Function],
|
||||
"freeTierExceedThroughputTooltip": [Function],
|
||||
"id": "addcollectionpane",
|
||||
"isAnalyticalStorageOn": [Function],
|
||||
"isAutoPilotSelected": [Function],
|
||||
"isEnableMongoCapabilityEnabled": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isFixedStorageSelected": [Function],
|
||||
"isFreeTierAccount": [Function],
|
||||
"isNonTableApi": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isSharedAutoPilotSelected": [Function],
|
||||
"isSynapseLinkSupported": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"isTryCosmosDBSubscription": [Function],
|
||||
"isUnlimitedStorageSelected": [Function],
|
||||
"largePartitionKey": [Function],
|
||||
"lowerCasePartitionKeyName": [Function],
|
||||
"maxCollectionsReached": [Function],
|
||||
"maxCollectionsReachedMessage": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"partitionKey": [Function],
|
||||
"partitionKeyName": [Function],
|
||||
"partitionKeyPattern": [Function],
|
||||
"partitionKeyPlaceholder": [Function],
|
||||
"partitionKeyTitle": [Function],
|
||||
"partitionKeyVisible": [Function],
|
||||
"requestUnitsUsageCost": [Function],
|
||||
"ruToolTipText": [Function],
|
||||
"sharedAutoPilotThroughput": [Function],
|
||||
"sharedThroughputRangeText": [Function],
|
||||
"shouldCreateMongoWildcardIndex": [Function],
|
||||
"shouldUseDatabaseThroughput": [Function],
|
||||
"showAnalyticalStore": [Function],
|
||||
"showEnableSynapseLink": [Function],
|
||||
"showIndexingOptionsForSharedThroughput": [Function],
|
||||
"showUpsellMessage": [Function],
|
||||
"storage": [Function],
|
||||
"throughputDatabase": [Function],
|
||||
"throughputMultiPartition": [Function],
|
||||
"throughputRangeText": [Function],
|
||||
"throughputSinglePartition": [Function],
|
||||
"throughputSpendAck": [Function],
|
||||
"throughputSpendAckText": [Function],
|
||||
"throughputSpendAckVisible": [Function],
|
||||
"title": [Function],
|
||||
"ttl90DaysEnabled": [Function],
|
||||
"uniqueKeys": [Function],
|
||||
"uniqueKeysPlaceholder": [Function],
|
||||
"uniqueKeysVisible": [Function],
|
||||
"upsellAnchorText": [Function],
|
||||
"upsellAnchorUrl": [Function],
|
||||
"upsellMessage": [Function],
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"useIndexingForSharedThroughput": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"addCollectionText": [Function],
|
||||
"addDatabasePane": AddDatabasePane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
@@ -3424,6 +3922,89 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
AddCollectionPane {
|
||||
"_isSynapseLinkEnabled": [Function],
|
||||
"autoPilotThroughput": [Function],
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canRequestSupport": [Function],
|
||||
"collectionId": [Function],
|
||||
"collectionIdTitle": [Function],
|
||||
"collectionWithThroughputInShared": [Function],
|
||||
"collectionWithThroughputInSharedTitle": [Function],
|
||||
"container": [Circular],
|
||||
"costsVisible": [Function],
|
||||
"databaseCreateNew": [Function],
|
||||
"databaseCreateNewShared": [Function],
|
||||
"databaseHasSharedOffer": [Function],
|
||||
"databaseId": [Function],
|
||||
"databaseIds": [Function],
|
||||
"dedicatedRequestUnitsUsageCost": [Function],
|
||||
"displayCollectionThroughput": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"formWarnings": [Function],
|
||||
"freeTierExceedThroughputTooltip": [Function],
|
||||
"id": "addcollectionpane",
|
||||
"isAnalyticalStorageOn": [Function],
|
||||
"isAutoPilotSelected": [Function],
|
||||
"isEnableMongoCapabilityEnabled": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isFixedStorageSelected": [Function],
|
||||
"isFreeTierAccount": [Function],
|
||||
"isNonTableApi": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isSharedAutoPilotSelected": [Function],
|
||||
"isSynapseLinkSupported": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"isTryCosmosDBSubscription": [Function],
|
||||
"isUnlimitedStorageSelected": [Function],
|
||||
"largePartitionKey": [Function],
|
||||
"lowerCasePartitionKeyName": [Function],
|
||||
"maxCollectionsReached": [Function],
|
||||
"maxCollectionsReachedMessage": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"partitionKey": [Function],
|
||||
"partitionKeyName": [Function],
|
||||
"partitionKeyPattern": [Function],
|
||||
"partitionKeyPlaceholder": [Function],
|
||||
"partitionKeyTitle": [Function],
|
||||
"partitionKeyVisible": [Function],
|
||||
"requestUnitsUsageCost": [Function],
|
||||
"ruToolTipText": [Function],
|
||||
"sharedAutoPilotThroughput": [Function],
|
||||
"sharedThroughputRangeText": [Function],
|
||||
"shouldCreateMongoWildcardIndex": [Function],
|
||||
"shouldUseDatabaseThroughput": [Function],
|
||||
"showAnalyticalStore": [Function],
|
||||
"showEnableSynapseLink": [Function],
|
||||
"showIndexingOptionsForSharedThroughput": [Function],
|
||||
"showUpsellMessage": [Function],
|
||||
"storage": [Function],
|
||||
"throughputDatabase": [Function],
|
||||
"throughputMultiPartition": [Function],
|
||||
"throughputRangeText": [Function],
|
||||
"throughputSinglePartition": [Function],
|
||||
"throughputSpendAck": [Function],
|
||||
"throughputSpendAckText": [Function],
|
||||
"throughputSpendAckVisible": [Function],
|
||||
"title": [Function],
|
||||
"ttl90DaysEnabled": [Function],
|
||||
"uniqueKeys": [Function],
|
||||
"uniqueKeysPlaceholder": [Function],
|
||||
"uniqueKeysVisible": [Function],
|
||||
"upsellAnchorText": [Function],
|
||||
"upsellAnchorUrl": [Function],
|
||||
"upsellMessage": [Function],
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"useIndexingForSharedThroughput": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
GraphStylingPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
@@ -3492,6 +4073,89 @@ exports[`SettingsComponent renders 1`] = `
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"addCollectionPane": AddCollectionPane {
|
||||
"_isSynapseLinkEnabled": [Function],
|
||||
"autoPilotThroughput": [Function],
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canRequestSupport": [Function],
|
||||
"collectionId": [Function],
|
||||
"collectionIdTitle": [Function],
|
||||
"collectionWithThroughputInShared": [Function],
|
||||
"collectionWithThroughputInSharedTitle": [Function],
|
||||
"container": [Circular],
|
||||
"costsVisible": [Function],
|
||||
"databaseCreateNew": [Function],
|
||||
"databaseCreateNewShared": [Function],
|
||||
"databaseHasSharedOffer": [Function],
|
||||
"databaseId": [Function],
|
||||
"databaseIds": [Function],
|
||||
"dedicatedRequestUnitsUsageCost": [Function],
|
||||
"displayCollectionThroughput": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"formWarnings": [Function],
|
||||
"freeTierExceedThroughputTooltip": [Function],
|
||||
"id": "addcollectionpane",
|
||||
"isAnalyticalStorageOn": [Function],
|
||||
"isAutoPilotSelected": [Function],
|
||||
"isEnableMongoCapabilityEnabled": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isFixedStorageSelected": [Function],
|
||||
"isFreeTierAccount": [Function],
|
||||
"isNonTableApi": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isSharedAutoPilotSelected": [Function],
|
||||
"isSynapseLinkSupported": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"isTryCosmosDBSubscription": [Function],
|
||||
"isUnlimitedStorageSelected": [Function],
|
||||
"largePartitionKey": [Function],
|
||||
"lowerCasePartitionKeyName": [Function],
|
||||
"maxCollectionsReached": [Function],
|
||||
"maxCollectionsReachedMessage": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"partitionKey": [Function],
|
||||
"partitionKeyName": [Function],
|
||||
"partitionKeyPattern": [Function],
|
||||
"partitionKeyPlaceholder": [Function],
|
||||
"partitionKeyTitle": [Function],
|
||||
"partitionKeyVisible": [Function],
|
||||
"requestUnitsUsageCost": [Function],
|
||||
"ruToolTipText": [Function],
|
||||
"sharedAutoPilotThroughput": [Function],
|
||||
"sharedThroughputRangeText": [Function],
|
||||
"shouldCreateMongoWildcardIndex": [Function],
|
||||
"shouldUseDatabaseThroughput": [Function],
|
||||
"showAnalyticalStore": [Function],
|
||||
"showEnableSynapseLink": [Function],
|
||||
"showIndexingOptionsForSharedThroughput": [Function],
|
||||
"showUpsellMessage": [Function],
|
||||
"storage": [Function],
|
||||
"throughputDatabase": [Function],
|
||||
"throughputMultiPartition": [Function],
|
||||
"throughputRangeText": [Function],
|
||||
"throughputSinglePartition": [Function],
|
||||
"throughputSpendAck": [Function],
|
||||
"throughputSpendAckText": [Function],
|
||||
"throughputSpendAckVisible": [Function],
|
||||
"title": [Function],
|
||||
"ttl90DaysEnabled": [Function],
|
||||
"uniqueKeys": [Function],
|
||||
"uniqueKeysPlaceholder": [Function],
|
||||
"uniqueKeysVisible": [Function],
|
||||
"upsellAnchorText": [Function],
|
||||
"upsellAnchorUrl": [Function],
|
||||
"upsellMessage": [Function],
|
||||
"upsellMessageAriaLabel": [Function],
|
||||
"useIndexingForSharedThroughput": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"addCollectionText": [Function],
|
||||
"addDatabasePane": AddDatabasePane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
|
||||
@@ -115,7 +115,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
fontSize: 12,
|
||||
};
|
||||
|
||||
componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
if (!this.shouldCheckErrors) {
|
||||
this.shouldCheckErrors = true;
|
||||
return;
|
||||
@@ -407,7 +407,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
);
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return this.renderNode(this.props.descriptor.root);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export class TabComponent extends React.Component<TabComponentProps> {
|
||||
});
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const currentTabContent = this.props.tabs[this.props.currentTabIndex].content;
|
||||
let className = "tabComponentContent";
|
||||
if (currentTabContent.className) {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { CostEstimateText } from "./CostEstimateText";
|
||||
const props = {
|
||||
requestUnits: 5,
|
||||
isAutoscale: false,
|
||||
};
|
||||
describe("CostEstimateText Pane", () => {
|
||||
it("should render Default properly", () => {
|
||||
const wrapper = shallow(<CostEstimateText {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,77 +0,0 @@
|
||||
import { Text } from "@fluentui/react";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { InfoTooltip } from "../../../../Common/Tooltip/InfoTooltip";
|
||||
import * as SharedConstants from "../../../../Shared/Constants";
|
||||
import { userContext } from "../../../../UserContext";
|
||||
import {
|
||||
calculateEstimateNumber,
|
||||
computeRUUsagePriceHourly,
|
||||
getAutoscalePricePerRu,
|
||||
getCurrencySign,
|
||||
getMultimasterMultiplier,
|
||||
getPriceCurrency,
|
||||
getPricePerRu,
|
||||
} from "../../../../Utils/PricingUtils";
|
||||
|
||||
interface CostEstimateTextProps {
|
||||
requestUnits: number;
|
||||
isAutoscale: boolean;
|
||||
}
|
||||
|
||||
export const CostEstimateText: FunctionComponent<CostEstimateTextProps> = ({
|
||||
requestUnits,
|
||||
isAutoscale,
|
||||
}: CostEstimateTextProps) => {
|
||||
const { databaseAccount } = userContext;
|
||||
if (!databaseAccount?.properties) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const serverId: string = userContext.portalEnv;
|
||||
const numberOfRegions: number = databaseAccount.properties.readLocations?.length || 1;
|
||||
const multimasterEnabled: boolean = databaseAccount.properties.enableMultipleWriteLocations;
|
||||
const hourlyPrice: number = computeRUUsagePriceHourly({
|
||||
serverId,
|
||||
requestUnits,
|
||||
numberOfRegions,
|
||||
multimasterEnabled,
|
||||
isAutoscale,
|
||||
});
|
||||
const dailyPrice: number = hourlyPrice * 24;
|
||||
const monthlyPrice: number = hourlyPrice * SharedConstants.hoursInAMonth;
|
||||
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 iconWithEstimatedCostDisclaimer: JSX.Element = <InfoTooltip>PricingUtils.estimatedCostDisclaimer</InfoTooltip>;
|
||||
|
||||
if (isAutoscale) {
|
||||
return (
|
||||
<Text variant="small">
|
||||
Estimated monthly cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
||||
<b>
|
||||
{currencySign + calculateEstimateNumber(monthlyPrice / 10)} -{" "}
|
||||
{currencySign + calculateEstimateNumber(monthlyPrice)}{" "}
|
||||
</b>
|
||||
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits / 10} - {requestUnits}{" "}
|
||||
RU/s, {currencySign + pricePerRu}/RU)
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Text variant="small">
|
||||
Estimated cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
||||
<b>
|
||||
{currencySign + calculateEstimateNumber(hourlyPrice)} hourly /{" "}
|
||||
{currencySign + calculateEstimateNumber(dailyPrice)} daily /{" "}
|
||||
{currencySign + calculateEstimateNumber(monthlyPrice)} monthly{" "}
|
||||
</b>
|
||||
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits}RU/s,{" "}
|
||||
{currencySign + pricePerRu}/RU)
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CostEstimateText Pane should render Default properly 1`] = `<Fragment />`;
|
||||
@@ -1,36 +0,0 @@
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
import React from "react";
|
||||
import { ThroughputInput } from "./ThroughputInput";
|
||||
const props = {
|
||||
isDatabase: false,
|
||||
showFreeTierExceedThroughputTooltip: true,
|
||||
isSharded: false,
|
||||
setThroughputValue: () => jest.fn(),
|
||||
setIsAutoscale: () => jest.fn(),
|
||||
onCostAcknowledgeChange: () => jest.fn(),
|
||||
};
|
||||
describe("ThroughputInput Pane", () => {
|
||||
let wrapper: ReactWrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(<ThroughputInput {...props} />);
|
||||
});
|
||||
|
||||
it("should render Default properly", () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("test Autoscale Mode select", () => {
|
||||
wrapper.setProps({ isAutoscaleSelected: true });
|
||||
expect(wrapper.find('[aria-label="ruDescription"]').at(0).text()).toBe(
|
||||
"Estimate your required RU/s with capacity calculator."
|
||||
);
|
||||
expect(wrapper.find('[aria-label="maxRUDescription"]').at(0).text()).toContain("Max RU/s");
|
||||
});
|
||||
|
||||
it("test Manual Mode select", () => {
|
||||
wrapper.setProps({ isAutoscaleSelected: false });
|
||||
expect(wrapper.find('[aria-label="ruDescription"]').at(0).text()).toContain("Estimate your required RU/s with");
|
||||
expect(wrapper.find('[aria-label="capacityLink"]').at(0).text()).toContain("capacity calculator");
|
||||
});
|
||||
});
|
||||
@@ -1,14 +1,11 @@
|
||||
import { Checkbox, DirectionalHint, Link, Stack, Text, TextField, TooltipHost } from "@fluentui/react";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import { Checkbox, DirectionalHint, Icon, Link, Stack, Text, TextField, TooltipHost } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
|
||||
import * as SharedConstants from "../../../Shared/Constants";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { getCollectionName } from "../../../Utils/APITypeUtils";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import * as PricingUtils from "../../../Utils/PricingUtils";
|
||||
import { CostEstimateText } from "./CostEstimateText/CostEstimateText";
|
||||
import "./ThroughputInput.less";
|
||||
|
||||
export interface ThroughputInputProps {
|
||||
isDatabase: boolean;
|
||||
@@ -17,25 +14,178 @@ export interface ThroughputInputProps {
|
||||
setThroughputValue: (throughput: number) => void;
|
||||
setIsAutoscale: (isAutoscale: boolean) => void;
|
||||
onCostAcknowledgeChange: (isAcknowledged: boolean) => void;
|
||||
isAutoscaleSelected?: boolean;
|
||||
throughput?: number;
|
||||
}
|
||||
|
||||
export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
isDatabase,
|
||||
showFreeTierExceedThroughputTooltip,
|
||||
setThroughputValue,
|
||||
setIsAutoscale,
|
||||
isSharded,
|
||||
isAutoscaleSelected = true,
|
||||
throughput = AutoPilotUtils.minAutoPilotThroughput,
|
||||
onCostAcknowledgeChange,
|
||||
}: ThroughputInputProps) => {
|
||||
const [isCostAcknowledged, setIsCostAcknowledged] = useState<boolean>(false);
|
||||
const [throughputError, setThroughputError] = useState<string>("");
|
||||
const getThroughputLabelText = (): string => {
|
||||
export interface ThroughputInputState {
|
||||
isAutoscaleSelected: boolean;
|
||||
throughput: number;
|
||||
isCostAcknowledged: boolean;
|
||||
throughputError: string;
|
||||
}
|
||||
|
||||
export class ThroughputInput extends React.Component<ThroughputInputProps, ThroughputInputState> {
|
||||
constructor(props: ThroughputInputProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isAutoscaleSelected: true,
|
||||
throughput: AutoPilotUtils.minAutoPilotThroughput,
|
||||
isCostAcknowledged: false,
|
||||
throughputError: undefined,
|
||||
};
|
||||
|
||||
this.props.setThroughputValue(AutoPilotUtils.minAutoPilotThroughput);
|
||||
this.props.setIsAutoscale(true);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="throughputInputContainer throughputInputSpacing">
|
||||
<Stack horizontal>
|
||||
<span className="mandatoryStar">* </span>
|
||||
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
|
||||
{this.getThroughputLabelText()}
|
||||
</Text>
|
||||
<TooltipHost directionalHint={DirectionalHint.bottomLeftEdge} content={PricingUtils.getRuToolTipText()}>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<input
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Autoscale mode"
|
||||
checked={this.state.isAutoscaleSelected}
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={this.onAutoscaleRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="throughputInputRadioBtnLabel">Autoscale</span>
|
||||
|
||||
<input
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Manual mode"
|
||||
checked={!this.state.isAutoscaleSelected}
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={this.onManualRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="throughputInputRadioBtnLabel">Manual</span>
|
||||
</Stack>
|
||||
|
||||
{this.state.isAutoscaleSelected && (
|
||||
<Stack className="throughputInputSpacing">
|
||||
<Text variant="small">
|
||||
Estimate your required RU/s with
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/">
|
||||
capacity calculator
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
|
||||
<Stack horizontal>
|
||||
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
|
||||
{this.props.isDatabase ? "Database" : getCollectionName()} max RU/s
|
||||
</Text>
|
||||
<TooltipHost directionalHint={DirectionalHint.bottomLeftEdge} content={this.getAutoScaleTooltip()}>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
<TextField
|
||||
type="number"
|
||||
styles={{
|
||||
fieldGroup: { width: 300, height: 27 },
|
||||
field: { fontSize: 12 },
|
||||
}}
|
||||
onChange={(event, newInput?: string) => this.onThroughputValueChange(newInput)}
|
||||
step={AutoPilotUtils.autoPilotIncrementStep}
|
||||
min={AutoPilotUtils.minAutoPilotThroughput}
|
||||
value={this.state.throughput.toString()}
|
||||
aria-label="Max request units per second"
|
||||
errorMessage={this.state.throughputError}
|
||||
/>
|
||||
|
||||
<Text variant="small">
|
||||
Your {this.props.isDatabase ? "database" : getCollectionName().toLocaleLowerCase()} throughput will
|
||||
automatically scale from{" "}
|
||||
<b>
|
||||
{AutoPilotUtils.getMinRUsBasedOnUserInput(this.state.throughput)} RU/s (10% of max RU/s) -{" "}
|
||||
{this.state.throughput} RU/s
|
||||
</b>{" "}
|
||||
based on usage.
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{!this.state.isAutoscaleSelected && (
|
||||
<Stack className="throughputInputSpacing">
|
||||
<Text variant="small">
|
||||
Estimate your required RU/s with
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/">
|
||||
capacity calculator
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
|
||||
<TooltipHost
|
||||
directionalHint={DirectionalHint.topLeftEdge}
|
||||
content={
|
||||
this.props.showFreeTierExceedThroughputTooltip &&
|
||||
this.state.throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs400
|
||||
? "The first 400 RU/s in this account are free. Billing will apply to any throughput beyond 400 RU/s."
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<TextField
|
||||
type="number"
|
||||
styles={{
|
||||
fieldGroup: { width: 300, height: 27 },
|
||||
field: { fontSize: 12 },
|
||||
}}
|
||||
onChange={(event, newInput?: string) => this.onThroughputValueChange(newInput)}
|
||||
step={100}
|
||||
min={SharedConstants.CollectionCreation.DefaultCollectionRUs400}
|
||||
max={userContext.isTryCosmosDBSubscription ? Constants.TryCosmosExperience.maxRU : Infinity}
|
||||
value={this.state.throughput.toString()}
|
||||
aria-label="Max request units per second"
|
||||
required={true}
|
||||
errorMessage={this.state.throughputError}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<CostEstimateText requestUnits={this.state.throughput} isAutoscale={this.state.isAutoscaleSelected} />
|
||||
|
||||
{this.state.throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && (
|
||||
<Stack horizontal verticalAlign="start">
|
||||
<span className="mandatoryStar">* </span>
|
||||
<Checkbox
|
||||
checked={this.state.isCostAcknowledged}
|
||||
styles={{
|
||||
checkbox: { width: 12, height: 12 },
|
||||
label: { padding: 0, margin: "4px 4px 0 0" },
|
||||
}}
|
||||
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) => {
|
||||
this.setState({ isCostAcknowledged: isChecked });
|
||||
this.props.onCostAcknowledgeChange(isChecked);
|
||||
}}
|
||||
/>
|
||||
<Text variant="small" style={{ lineHeight: "20px" }}>
|
||||
{this.getCostAcknowledgeText()}
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private getThroughputLabelText(): string {
|
||||
let throughputHeaderText: string;
|
||||
if (isAutoscaleSelected) {
|
||||
if (this.state.isAutoscaleSelected) {
|
||||
throughputHeaderText = AutoPilotUtils.getAutoPilotHeaderText().toLocaleLowerCase();
|
||||
} else {
|
||||
const minRU: string = SharedConstants.CollectionCreation.DefaultCollectionRUs400.toLocaleString();
|
||||
@@ -44,26 +194,29 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
: "unlimited";
|
||||
throughputHeaderText = `throughput (${minRU} - ${maxRU} RU/s)`;
|
||||
}
|
||||
return `${isDatabase ? "Database" : getCollectionName()} ${throughputHeaderText}`;
|
||||
};
|
||||
|
||||
const onThroughputValueChange = (newInput: string): void => {
|
||||
return `${this.props.isDatabase ? "Database" : getCollectionName()} ${throughputHeaderText}`;
|
||||
}
|
||||
|
||||
private onThroughputValueChange(newInput: string): void {
|
||||
const newThroughput = parseInt(newInput);
|
||||
setThroughputValue(newThroughput);
|
||||
if (!isSharded && newThroughput > 10000) {
|
||||
setThroughputError("Unsharded collections support up to 10,000 RUs");
|
||||
} else {
|
||||
setThroughputError("");
|
||||
}
|
||||
};
|
||||
this.setState({ throughput: newThroughput });
|
||||
this.props.setThroughputValue(newThroughput);
|
||||
|
||||
const getAutoScaleTooltip = (): string => {
|
||||
if (!this.props.isSharded && newThroughput > 10000) {
|
||||
this.setState({ throughputError: "Unsharded collections support up to 10,000 RUs" });
|
||||
} else {
|
||||
this.setState({ throughputError: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
private getAutoScaleTooltip(): string {
|
||||
const collectionName = getCollectionName().toLocaleLowerCase();
|
||||
return `Set the max RU/s to the highest RU/s you want your ${collectionName} to scale to. The ${collectionName} will scale between 10% of max RU/s to the max RU/s based on usage.`;
|
||||
};
|
||||
}
|
||||
|
||||
const getCostAcknowledgeText = (): string => {
|
||||
const databaseAccount = userContext.databaseAccount;
|
||||
private getCostAcknowledgeText(): string {
|
||||
const { databaseAccount } = userContext;
|
||||
if (!databaseAccount || !databaseAccount.properties) {
|
||||
return "";
|
||||
}
|
||||
@@ -72,159 +225,98 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
const multimasterEnabled: boolean = databaseAccount.properties.enableMultipleWriteLocations;
|
||||
|
||||
return PricingUtils.getEstimatedSpendAcknowledgeString(
|
||||
throughput,
|
||||
this.state.throughput,
|
||||
userContext.portalEnv,
|
||||
numberOfRegions,
|
||||
multimasterEnabled,
|
||||
isAutoscaleSelected
|
||||
this.state.isAutoscaleSelected
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const handleOnChangeMode = (event: React.ChangeEvent<HTMLInputElement>, mode: string): void => {
|
||||
if (mode === "Autoscale") {
|
||||
setThroughputValue(AutoPilotUtils.minAutoPilotThroughput);
|
||||
setIsAutoscale(true);
|
||||
} else {
|
||||
setThroughputValue(SharedConstants.CollectionCreation.DefaultCollectionRUs400);
|
||||
setIsAutoscale(false);
|
||||
private onAutoscaleRadioBtnChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
if (event.target.checked && !this.state.isAutoscaleSelected) {
|
||||
this.setState({ isAutoscaleSelected: true, throughput: AutoPilotUtils.minAutoPilotThroughput });
|
||||
this.props.setIsAutoscale(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private onManualRadioBtnChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
if (event.target.checked && this.state.isAutoscaleSelected) {
|
||||
this.setState({
|
||||
isAutoscaleSelected: false,
|
||||
throughput: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
|
||||
});
|
||||
this.props.setIsAutoscale(false);
|
||||
this.props.setThroughputValue(SharedConstants.CollectionCreation.DefaultCollectionRUs400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface CostEstimateTextProps {
|
||||
requestUnits: number;
|
||||
isAutoscale: boolean;
|
||||
}
|
||||
|
||||
const CostEstimateText: React.FunctionComponent<CostEstimateTextProps> = (props: CostEstimateTextProps) => {
|
||||
const { requestUnits, isAutoscale } = props;
|
||||
const { databaseAccount } = userContext;
|
||||
if (!databaseAccount?.properties) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const serverId: string = userContext.portalEnv;
|
||||
const numberOfRegions: number = databaseAccount.properties.readLocations?.length || 1;
|
||||
const multimasterEnabled: boolean = databaseAccount.properties.enableMultipleWriteLocations;
|
||||
const hourlyPrice: number = PricingUtils.computeRUUsagePriceHourly({
|
||||
serverId,
|
||||
requestUnits,
|
||||
numberOfRegions,
|
||||
multimasterEnabled,
|
||||
isAutoscale,
|
||||
});
|
||||
const dailyPrice: number = hourlyPrice * 24;
|
||||
const monthlyPrice: number = hourlyPrice * SharedConstants.hoursInAMonth;
|
||||
const currency: string = PricingUtils.getPriceCurrency(serverId);
|
||||
const currencySign: string = PricingUtils.getCurrencySign(serverId);
|
||||
const multiplier = PricingUtils.getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
|
||||
const pricePerRu = isAutoscale
|
||||
? PricingUtils.getAutoscalePricePerRu(serverId, multiplier) * multiplier
|
||||
: PricingUtils.getPricePerRu(serverId) * multiplier;
|
||||
|
||||
const iconWithEstimatedCostDisclaimer: JSX.Element = (
|
||||
<TooltipHost
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content={PricingUtils.estimatedCostDisclaimer}
|
||||
styles={{ root: { verticalAlign: "bottom" } }}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" />
|
||||
</TooltipHost>
|
||||
);
|
||||
|
||||
if (isAutoscale) {
|
||||
return (
|
||||
<Text variant="small">
|
||||
Estimated monthly cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
||||
<b>
|
||||
{currencySign + PricingUtils.calculateEstimateNumber(monthlyPrice / 10)} -{" "}
|
||||
{currencySign + PricingUtils.calculateEstimateNumber(monthlyPrice)}{" "}
|
||||
</b>
|
||||
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits / 10} - {requestUnits}{" "}
|
||||
RU/s, {currencySign + pricePerRu}/RU)
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="throughputInputContainer throughputInputSpacing">
|
||||
<Stack horizontal>
|
||||
<span className="mandatoryStar">* </span>
|
||||
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
|
||||
{getThroughputLabelText()}
|
||||
</Text>
|
||||
<InfoTooltip>{PricingUtils.getRuToolTipText()}</InfoTooltip>
|
||||
</Stack>
|
||||
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<input
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Autoscale mode"
|
||||
checked={isAutoscaleSelected}
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={(e) => handleOnChangeMode(e, "Autoscale")}
|
||||
/>
|
||||
<span className="throughputInputRadioBtnLabel">Autoscale</span>
|
||||
|
||||
<input
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Manual mode"
|
||||
checked={!isAutoscaleSelected}
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={(e) => handleOnChangeMode(e, "Manual")}
|
||||
/>
|
||||
<span className="throughputInputRadioBtnLabel">Manual</span>
|
||||
</Stack>
|
||||
|
||||
{isAutoscaleSelected && (
|
||||
<Stack className="throughputInputSpacing">
|
||||
<Text variant="small" aria-label="ruDescription">
|
||||
Estimate your required RU/s with{" "}
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/" aria-label="ruDescription">
|
||||
capacity calculator
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
|
||||
<Stack horizontal>
|
||||
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }} aria-label="maxRUDescription">
|
||||
{isDatabase ? "Database" : getCollectionName()} Max RU/s
|
||||
</Text>
|
||||
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
|
||||
</Stack>
|
||||
|
||||
<TextField
|
||||
type="number"
|
||||
styles={{
|
||||
fieldGroup: { width: 300, height: 27 },
|
||||
field: { fontSize: 12 },
|
||||
}}
|
||||
onChange={(event, newInput?: string) => onThroughputValueChange(newInput)}
|
||||
step={AutoPilotUtils.autoPilotIncrementStep}
|
||||
min={AutoPilotUtils.minAutoPilotThroughput}
|
||||
value={throughput.toString()}
|
||||
aria-label="Max request units per second"
|
||||
required={true}
|
||||
errorMessage={throughputError}
|
||||
/>
|
||||
|
||||
<Text variant="small">
|
||||
Your {isDatabase ? "database" : getCollectionName().toLocaleLowerCase()} throughput will automatically scale
|
||||
from{" "}
|
||||
<b>
|
||||
{AutoPilotUtils.getMinRUsBasedOnUserInput(throughput)} RU/s (10% of max RU/s) - {throughput} RU/s
|
||||
</b>{" "}
|
||||
based on usage.
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{!isAutoscaleSelected && (
|
||||
<Stack className="throughputInputSpacing">
|
||||
<Text variant="small" aria-label="ruDescription">
|
||||
Estimate your required RU/s with
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/" aria-label="capacityLink">
|
||||
capacity calculator
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
|
||||
<TooltipHost
|
||||
directionalHint={DirectionalHint.topLeftEdge}
|
||||
content={
|
||||
showFreeTierExceedThroughputTooltip &&
|
||||
throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs400
|
||||
? "The first 400 RU/s in this account are free. Billing will apply to any throughput beyond 400 RU/s."
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<TextField
|
||||
type="number"
|
||||
styles={{
|
||||
fieldGroup: { width: 300, height: 27 },
|
||||
field: { fontSize: 12 },
|
||||
}}
|
||||
onChange={(event, newInput?: string) => onThroughputValueChange(newInput)}
|
||||
step={100}
|
||||
min={SharedConstants.CollectionCreation.DefaultCollectionRUs400}
|
||||
max={userContext.isTryCosmosDBSubscription ? Constants.TryCosmosExperience.maxRU : Infinity}
|
||||
value={throughput.toString()}
|
||||
aria-label="Max request units per second"
|
||||
required={true}
|
||||
errorMessage={throughputError}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
|
||||
|
||||
{throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && (
|
||||
<Stack horizontal verticalAlign="start">
|
||||
<Checkbox
|
||||
checked={isCostAcknowledged}
|
||||
styles={{
|
||||
checkbox: { width: 12, height: 12 },
|
||||
label: { padding: 0, margin: "4px 4px 0 0" },
|
||||
}}
|
||||
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) => {
|
||||
setIsCostAcknowledged(isChecked);
|
||||
onCostAcknowledgeChange(isChecked);
|
||||
}}
|
||||
/>
|
||||
<Text variant="small" style={{ lineHeight: "20px" }}>
|
||||
{getCostAcknowledgeText()}
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
<Text variant="small">
|
||||
Estimated cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
||||
<b>
|
||||
{currencySign + PricingUtils.calculateEstimateNumber(hourlyPrice)} hourly /{" "}
|
||||
{currencySign + PricingUtils.calculateEstimateNumber(dailyPrice)} daily /{" "}
|
||||
{currencySign + PricingUtils.calculateEstimateNumber(monthlyPrice)} monthly{" "}
|
||||
</b>
|
||||
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits}RU/s,{" "}
|
||||
{currencySign + pricePerRu}/RU)
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@ export interface TreeComponentProps {
|
||||
}
|
||||
|
||||
export class TreeComponent extends React.Component<TreeComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div style={this.props.style} className={`treeComponent ${this.props.className}`}>
|
||||
<TreeNodeComponent paddingLeft={0} node={this.props.rootNode} generation={0} />
|
||||
@@ -93,7 +93,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: TreeNodeComponentProps, prevState: TreeNodeComponentState) {
|
||||
public override componentDidUpdate(prevProps: TreeNodeComponentProps, prevState: TreeNodeComponentState) {
|
||||
// Only call when expand has actually changed
|
||||
if (this.state.isExpanded !== prevState.isExpanded) {
|
||||
if (this.state.isExpanded) {
|
||||
@@ -110,7 +110,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return this.renderNode(this.props.node, this.props.generation);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants"
|
||||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||
import { ArcadiaResourceManager } from "../SparkClusterManager/ArcadiaResourceManager";
|
||||
import { updateUserContext, userContext } from "../UserContext";
|
||||
import { getCollectionName, getDatabaseName, getUploadName } from "../Utils/APITypeUtils";
|
||||
import { getCollectionName } from "../Utils/APITypeUtils";
|
||||
import { decryptJWTToken, getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||
import { stringToBlob } from "../Utils/BlobUtils";
|
||||
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
||||
@@ -49,9 +49,9 @@ import { NotebookContentItem, NotebookContentItemType } from "./Notebook/Noteboo
|
||||
import type NotebookManager from "./Notebook/NotebookManager";
|
||||
import type { NotebookPaneContent } from "./Notebook/NotebookManager";
|
||||
import { NotebookUtil } from "./Notebook/NotebookUtil";
|
||||
import AddCollectionPane from "./Panes/AddCollectionPane";
|
||||
import { AddCollectionPanel } from "./Panes/AddCollectionPanel";
|
||||
import AddDatabasePane from "./Panes/AddDatabasePane";
|
||||
import { AddDatabasePanel } from "./Panes/AddDatabasePanel/AddDatabasePanel";
|
||||
import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane/BrowseQueriesPane";
|
||||
import CassandraAddCollectionPane from "./Panes/CassandraAddCollectionPane";
|
||||
import { ContextualPaneBase } from "./Panes/ContextualPaneBase";
|
||||
@@ -150,6 +150,7 @@ export default class Explorer {
|
||||
|
||||
// Contextual panes
|
||||
public addDatabasePane: AddDatabasePane;
|
||||
public addCollectionPane: AddCollectionPane;
|
||||
public graphStylingPane: GraphStylingPane;
|
||||
public cassandraAddCollectionPane: CassandraAddCollectionPane;
|
||||
private gitHubClient: GitHubClient;
|
||||
@@ -411,6 +412,14 @@ export default class Explorer {
|
||||
container: this,
|
||||
});
|
||||
|
||||
this.addCollectionPane = new AddCollectionPane({
|
||||
isPreferredApiTable: ko.computed(() => userContext.apiType === "Tables"),
|
||||
id: "addcollectionpane",
|
||||
visible: ko.observable<boolean>(false),
|
||||
|
||||
container: this,
|
||||
});
|
||||
|
||||
this.graphStylingPane = new GraphStylingPane({
|
||||
id: "graphstylingpane",
|
||||
visible: ko.observable<boolean>(false),
|
||||
@@ -433,7 +442,12 @@ export default class Explorer {
|
||||
}
|
||||
});
|
||||
|
||||
this._panes = [this.addDatabasePane, this.graphStylingPane, this.cassandraAddCollectionPane];
|
||||
this._panes = [
|
||||
this.addDatabasePane,
|
||||
this.addCollectionPane,
|
||||
this.graphStylingPane,
|
||||
this.cassandraAddCollectionPane,
|
||||
];
|
||||
this.addDatabaseText.subscribe((addDatabaseText: string) => this.addDatabasePane.title(addDatabaseText));
|
||||
this.isTabsContentExpanded = ko.observable(false);
|
||||
|
||||
@@ -457,6 +471,11 @@ export default class Explorer {
|
||||
this.collectionTreeNodeAltText("Container");
|
||||
this.deleteCollectionText("Delete Container");
|
||||
this.deleteDatabaseText("Delete Database");
|
||||
this.addCollectionPane.title("Add Container");
|
||||
this.addCollectionPane.collectionIdTitle("Container id");
|
||||
this.addCollectionPane.collectionWithThroughputInSharedTitle(
|
||||
"Provision dedicated throughput for this container"
|
||||
);
|
||||
this.refreshTreeTitle("Refresh containers");
|
||||
break;
|
||||
case "Mongo":
|
||||
@@ -466,6 +485,11 @@ export default class Explorer {
|
||||
this.collectionTreeNodeAltText("Collection");
|
||||
this.deleteCollectionText("Delete Collection");
|
||||
this.deleteDatabaseText("Delete Database");
|
||||
this.addCollectionPane.title("Add Collection");
|
||||
this.addCollectionPane.collectionIdTitle("Collection id");
|
||||
this.addCollectionPane.collectionWithThroughputInSharedTitle(
|
||||
"Provision dedicated throughput for this collection"
|
||||
);
|
||||
this.refreshTreeTitle("Refresh collections");
|
||||
break;
|
||||
case "Gremlin":
|
||||
@@ -475,6 +499,9 @@ export default class Explorer {
|
||||
this.deleteDatabaseText("Delete Database");
|
||||
this.collectionTitle("Gremlin API");
|
||||
this.collectionTreeNodeAltText("Graph");
|
||||
this.addCollectionPane.title("Add Graph");
|
||||
this.addCollectionPane.collectionIdTitle("Graph id");
|
||||
this.addCollectionPane.collectionWithThroughputInSharedTitle("Provision dedicated throughput for this graph");
|
||||
this.refreshTreeTitle("Refresh graphs");
|
||||
break;
|
||||
case "Tables":
|
||||
@@ -484,6 +511,9 @@ export default class Explorer {
|
||||
this.deleteDatabaseText("Delete Database");
|
||||
this.collectionTitle("Azure Table API");
|
||||
this.collectionTreeNodeAltText("Table");
|
||||
this.addCollectionPane.title("Add Table");
|
||||
this.addCollectionPane.collectionIdTitle("Table id");
|
||||
this.addCollectionPane.collectionWithThroughputInSharedTitle("Provision dedicated throughput for this table");
|
||||
this.refreshTreeTitle("Refresh tables");
|
||||
this.tableDataClient = new TablesAPIDataClient();
|
||||
break;
|
||||
@@ -494,6 +524,9 @@ export default class Explorer {
|
||||
this.deleteDatabaseText("Delete Keyspace");
|
||||
this.collectionTitle("Cassandra API");
|
||||
this.collectionTreeNodeAltText("Table");
|
||||
this.addCollectionPane.title("Add Table");
|
||||
this.addCollectionPane.collectionIdTitle("Table id");
|
||||
this.addCollectionPane.collectionWithThroughputInSharedTitle("Provision dedicated throughput for this table");
|
||||
this.refreshTreeTitle("Refresh tables");
|
||||
this.tableDataClient = new CassandraAPIDataClient();
|
||||
break;
|
||||
@@ -1812,6 +1845,9 @@ export default class Explorer {
|
||||
public onNewCollectionClicked(databaseId?: string): void {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
this.cassandraAddCollectionPane.open();
|
||||
} else if (userContext.features.enableKOPanel) {
|
||||
this.addCollectionPane.open(this.selectedDatabaseId());
|
||||
document.getElementById("linkAddCollection").focus();
|
||||
} else {
|
||||
this.openAddCollectionPanel(databaseId);
|
||||
}
|
||||
@@ -1925,7 +1961,7 @@ export default class Explorer {
|
||||
|
||||
public openDeleteDatabaseConfirmationPane(): void {
|
||||
this.openSidePanel(
|
||||
"Delete " + getDatabaseName(),
|
||||
"Delete Database",
|
||||
<DeleteDatabaseConfirmationPanel
|
||||
explorer={this}
|
||||
openNotificationConsole={this.expandConsole}
|
||||
@@ -1936,12 +1972,12 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
public openUploadItemsPanePane(): void {
|
||||
this.openSidePanel("Upload " + getUploadName(), <UploadItemsPane explorer={this} />);
|
||||
this.openSidePanel("Upload", <UploadItemsPane explorer={this} closePanel={this.closeSidePanel} />);
|
||||
}
|
||||
|
||||
public openSettingPane(): void {
|
||||
this.openSidePanel(
|
||||
"Setting",
|
||||
"Settings",
|
||||
<SettingsPane expandConsole={() => this.expandConsole()} closePanel={this.closeSidePanel} />
|
||||
);
|
||||
}
|
||||
@@ -1969,21 +2005,6 @@ export default class Explorer {
|
||||
/>
|
||||
);
|
||||
}
|
||||
public openAddDatabasePane(): void {
|
||||
if (userContext.features.enableKOPanel) {
|
||||
this.addDatabasePane.open();
|
||||
document.getElementById("linkAddDatabase").focus();
|
||||
} else {
|
||||
this.openSidePanel(
|
||||
"Add " + getDatabaseName(),
|
||||
<AddDatabasePanel
|
||||
explorer={this}
|
||||
openNotificationConsole={this.expandConsole}
|
||||
closePanel={this.closeSidePanel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public openBrowseQueriesPanel(): void {
|
||||
this.openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={this} closePanel={this.closeSidePanel} />);
|
||||
@@ -2000,7 +2021,7 @@ export default class Explorer {
|
||||
public openUploadFilePanel(parent?: NotebookContentItem): void {
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.openSidePanel(
|
||||
"Upload file to notebook server",
|
||||
"Upload File",
|
||||
<UploadFilePane
|
||||
expandConsole={() => this.expandConsole()}
|
||||
closePanel={this.closeSidePanel}
|
||||
|
||||
@@ -733,16 +733,15 @@ export class D3ForceGraph implements GraphRenderer {
|
||||
.attr("aria-label", (d: D3Node) => {
|
||||
return this.retrieveNodeCaption(d);
|
||||
})
|
||||
.on("dblclick", function (this: Element, _: MouseEvent, d: D3Node) {
|
||||
// https://stackoverflow.com/a/41945742 ('this' implicitly has type 'any' because it does not have a type annotation)
|
||||
.on("dblclick", function (_: MouseEvent, d: D3Node) {
|
||||
// this is the <g> element
|
||||
self.onNodeClicked(this.parentNode, d);
|
||||
})
|
||||
.on("click", function (this: Element, _: MouseEvent, d: D3Node) {
|
||||
.on("click", function (_: MouseEvent, d: D3Node) {
|
||||
// this is the <g> element
|
||||
self.onNodeClicked(this.parentNode, d);
|
||||
})
|
||||
.on("keypress", function (this: Element, event: KeyboardEvent, d: D3Node) {
|
||||
.on("keypress", function (event: KeyboardEvent, d: D3Node) {
|
||||
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
|
||||
event.stopPropagation();
|
||||
// this is the <g> element
|
||||
|
||||
@@ -34,7 +34,7 @@ export class EditorNeighborsComponent extends React.Component<EditorNeighborsCom
|
||||
this.addNewEdgeToNeighbor = this.props.isSource ? this.addNewEdgeToSource : this.addNewEdgeToTarget;
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
// Show empty text boxes by default if no neighbor for convenience
|
||||
if (this.props.editedNeighbors.currentNeighbors.length === 0) {
|
||||
if (this.props.isSource) {
|
||||
@@ -45,7 +45,7 @@ export class EditorNeighborsComponent extends React.Component<EditorNeighborsCom
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const neighborTitle = this.props.isSource
|
||||
? EditorNeighborsComponent.SOURCE_TITLE
|
||||
: EditorNeighborsComponent.TARGET_TITLE;
|
||||
|
||||
@@ -20,7 +20,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
public static readonly VERTEX_PROPERTY_TYPES = ["string", "number", "boolean" /* 'null' */]; // TODO Enable null when fully supported by backend
|
||||
private static readonly DEFAULT_PROPERTY_TYPE = "string";
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<table className="propertyTable">
|
||||
<tbody>
|
||||
|
||||
@@ -965,7 +965,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
}
|
||||
|
||||
/* ************* React life-cycle methods *********** */
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const currentTabIndex = ((resultDisplay: ResultDisplay): number => {
|
||||
switch (resultDisplay) {
|
||||
case ResultDisplay.Graph:
|
||||
@@ -1022,10 +1022,10 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
public override componentWillUnmount(): void {
|
||||
this.gremlinClient.destroy();
|
||||
}
|
||||
public componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
if (this.props.onLoadStartKey != null && this.props.onLoadStartKey != undefined) {
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.Tab,
|
||||
@@ -1069,7 +1069,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
this.onIsPropertyPaneEditing(this.isPropertyPaneEditing());
|
||||
this.onIsNewVertexDisabledChange(this.isNewVertexDisabled());
|
||||
}
|
||||
|
||||
@@ -18,20 +18,20 @@ export class GraphVizComponent extends React.Component<GraphVizComponentProps> {
|
||||
this.forceGraph = new D3ForceGraph(this.props.forceGraphParams);
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.forceGraph.init(this.rootNode);
|
||||
}
|
||||
|
||||
public shouldComponentUpdate(): boolean {
|
||||
public override shouldComponentUpdate(): boolean {
|
||||
// Prevents component re-rendering
|
||||
return false;
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
public override componentWillUnmount(): void {
|
||||
this.forceGraph.destroy();
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<svg id="maingraph" ref={(elt: Element) => this.setRef(elt)}>
|
||||
<title>Main Graph</title>
|
||||
|
||||
@@ -18,7 +18,7 @@ interface LeftPaneComponentProps {
|
||||
}
|
||||
|
||||
export class LeftPaneComponent extends React.Component<LeftPaneComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="leftPane">
|
||||
<div className="paneTitle leftPaneResults">Results</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ interface MiddlePaneComponentProps {
|
||||
}
|
||||
|
||||
export class MiddlePaneComponent extends React.Component<MiddlePaneComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="middlePane">
|
||||
<div className="graphTitle">
|
||||
|
||||
@@ -104,7 +104,7 @@ export class NodePropertiesComponent extends React.Component<
|
||||
return null;
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
if (!this.props.node) {
|
||||
return <span />;
|
||||
} else {
|
||||
|
||||
@@ -25,7 +25,7 @@ export class QueryContainerComponent extends React.Component<
|
||||
};
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="queryContainer">
|
||||
<InputTypeaheadComponent.InputTypeaheadComponent
|
||||
|
||||
@@ -20,7 +20,7 @@ export class ReadOnlyNeighborsComponent extends React.Component<ReadOnlyNeighbor
|
||||
private static readonly NO_TARGETS_LABEL = "No targets found";
|
||||
private static readonly TARGET_TITLE = "Target";
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const neighbors = this.props.isSource ? this.props.node.sources : this.props.node.targets;
|
||||
const noNeighborsLabel = this.props.isSource
|
||||
? ReadOnlyNeighborsComponent.NO_SOURCES_LABEL
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface ReadOnlyNodePropertiesComponentProps {
|
||||
}
|
||||
|
||||
export class ReadOnlyNodePropertiesComponent extends React.Component<ReadOnlyNodePropertiesComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<table className="roPropertyTable propertyTable">
|
||||
<tbody>
|
||||
|
||||
@@ -266,7 +266,8 @@ function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
||||
iconSrc: AddDatabaseIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
container.openAddDatabasePane();
|
||||
container.addDatabasePane.open();
|
||||
document.getElementById("linkAddDatabase").focus();
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
|
||||
@@ -10,17 +10,17 @@ interface MemoryTrackerProps {
|
||||
export class MemoryTrackerComponent extends React.Component<MemoryTrackerProps> {
|
||||
private memoryUsageInfoSubscription: Subscription;
|
||||
|
||||
public componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.memoryUsageInfoSubscription = this.props.memoryUsageInfo.subscribe(() => {
|
||||
this.forceUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
public override componentWillUnmount(): void {
|
||||
this.memoryUsageInfoSubscription && this.memoryUsageInfoSubscription.dispose();
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const memoryUsageInfo: MemoryUsageInfo = this.props.memoryUsageInfo();
|
||||
if (!memoryUsageInfo) {
|
||||
return (
|
||||
|
||||
@@ -23,7 +23,7 @@ export class ControlBarComponent extends React.Component<ControlBarComponentProp
|
||||
);
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
if (!this.props.buttons || this.props.buttons.length < 1) {
|
||||
return <React.Fragment />;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export class NotificationConsoleComponent extends React.Component<
|
||||
this.prevHeaderStatus = undefined;
|
||||
}
|
||||
|
||||
public componentDidUpdate(
|
||||
public override componentDidUpdate(
|
||||
prevProps: NotificationConsoleComponentProps,
|
||||
prevState: NotificationConsoleComponentState
|
||||
): void {
|
||||
@@ -102,7 +102,7 @@ export class NotificationConsoleComponent extends React.Component<
|
||||
this.consoleHeaderElement = element;
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const numInProgress = this.state.allConsoleData.filter(
|
||||
(data: ConsoleData) => data.type === ConsoleDataType.InProgress
|
||||
).length;
|
||||
|
||||
@@ -6,11 +6,11 @@ import { default as Contents } from "./contents";
|
||||
export class NotebookComponent extends React.Component<{ contentRef: ContentRef }> {
|
||||
notificationSystem!: ReactNotificationSystem;
|
||||
|
||||
shouldComponentUpdate(nextProps: { contentRef: ContentRef }): boolean {
|
||||
public override shouldComponentUpdate(nextProps: { contentRef: ContentRef }): boolean {
|
||||
return nextProps.contentRef !== this.props.contentRef;
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="notebookComponentContainer">
|
||||
<Contents contentRef={this.props.contentRef} />
|
||||
|
||||
@@ -53,7 +53,7 @@ export class NotebookComponentAdapter extends NotebookComponentBootstrapper impl
|
||||
};
|
||||
}
|
||||
|
||||
protected renderExtraComponent = (): JSX.Element => {
|
||||
protected override renderExtraComponent = (): JSX.Element => {
|
||||
return <VirtualCommandBarComponent contentRef={this.contentRef} onRender={this.onUpdateKernelInfo} />;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class VirtualCommandBarComponent extends React.Component<VirtualCommandBarCompon
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: VirtualCommandBarComponentProps): boolean {
|
||||
public override shouldComponentUpdate(nextProps: VirtualCommandBarComponentProps): boolean {
|
||||
return (
|
||||
this.props.kernelStatus !== nextProps.kernelStatus ||
|
||||
this.props.kernelSpecName !== nextProps.kernelSpecName ||
|
||||
@@ -24,7 +24,7 @@ class VirtualCommandBarComponent extends React.Component<VirtualCommandBarCompon
|
||||
);
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
this.props.onRender && this.props.onRender();
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ export class File extends React.PureComponent<FileProps> {
|
||||
return choice;
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const choice = this.getChoice();
|
||||
|
||||
// Right now we only handle one kind of editor
|
||||
|
||||
@@ -35,7 +35,7 @@ interface TextFileState {
|
||||
}
|
||||
|
||||
class EditorPlaceholder extends React.PureComponent<MonacoEditorProps> {
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
// TODO: Show a little blocky placeholder
|
||||
return null;
|
||||
}
|
||||
@@ -53,13 +53,13 @@ export class TextFile extends React.PureComponent<TextFileProps, TextFileState>
|
||||
this.props.handleChange(source);
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
import(/* webpackChunkName: "monaco-editor" */ "@nteract/monaco-editor").then((module) => {
|
||||
this.setState({ Editor: module.default });
|
||||
});
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const Editor = this.state.Editor;
|
||||
|
||||
return (
|
||||
|
||||
@@ -54,7 +54,7 @@ class Contents extends React.PureComponent<ContentsProps> {
|
||||
SAVE: ["ctrl+s", "ctrl+shift+s", "meta+s", "meta+shift+s"],
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const { contentRef, handlers } = this.props;
|
||||
|
||||
if (!contentRef) {
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface NotebookRendererProps {
|
||||
* This is the class that uses nteract to render a read-only notebook.
|
||||
*/
|
||||
class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
||||
componentDidMount() {
|
||||
public override componentDidMount() {
|
||||
if (!userContext.features.sandboxNotebookOutputs) {
|
||||
loadTransform(this.props as any);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
||||
);
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<div className="NotebookReadOnlyRender">
|
||||
<Cells contentRef={this.props.contentRef}>
|
||||
|
||||
@@ -68,22 +68,22 @@ class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public override componentDidMount() {
|
||||
if (!userContext.features.sandboxNotebookOutputs) {
|
||||
loadTransform(this.props as any);
|
||||
}
|
||||
this.props.updateNotebookParentDomElt(this.props.contentRef, this.notebookRendererRef.current);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
public override componentDidUpdate() {
|
||||
this.props.updateNotebookParentDomElt(this.props.contentRef, this.notebookRendererRef.current);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public override componentWillUnmount() {
|
||||
this.props.updateNotebookParentDomElt(this.props.contentRef, undefined);
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<div className="NotebookRendererContainer">
|
||||
|
||||
@@ -36,7 +36,7 @@ interface DispatchProps {
|
||||
type Props = StateProps & DispatchProps & ComponentProps;
|
||||
|
||||
export class PromptPure extends React.Component<Props> {
|
||||
render() {
|
||||
public override render() {
|
||||
return (
|
||||
<div className="nteract-cell-prompt">
|
||||
{this.props.children({
|
||||
|
||||
@@ -45,14 +45,14 @@ const BarContainer = styled.div`
|
||||
`;
|
||||
|
||||
export class StatusBar extends React.Component<Props> {
|
||||
shouldComponentUpdate(nextProps: Props): boolean {
|
||||
public override shouldComponentUpdate(nextProps: Props): boolean {
|
||||
if (this.props.lastSaved !== nextProps.lastSaved || this.props.kernelStatus !== nextProps.kernelStatus) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
const name = this.props.kernelSpecDisplayName || "Loading...";
|
||||
|
||||
return (
|
||||
|
||||
@@ -37,7 +37,7 @@ interface StateProps {
|
||||
class BaseToolbar extends React.PureComponent<ComponentProps & DispatchProps & StateProps> {
|
||||
static contextType = CellToolbarContext;
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
let items: IContextualMenuItem[] = [];
|
||||
|
||||
if (this.props.cellType === "code") {
|
||||
|
||||
@@ -132,7 +132,7 @@ export class PureCellCreator extends React.PureComponent<CellCreatorProps> {
|
||||
this.props.createCell("code", this.props.above);
|
||||
};
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return (
|
||||
<CreatorHoverMask>
|
||||
<CreatorHoverRegion>
|
||||
@@ -171,7 +171,7 @@ class CellCreator extends React.PureComponent<ComponentProps & DispatchProps & S
|
||||
: createCellBelow({ cellType: type, id, source: "", contentRef });
|
||||
};
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.props.isFirstCell && (
|
||||
|
||||
@@ -19,7 +19,7 @@ interface StateProps {
|
||||
* Displays "Cell <index>"
|
||||
*/
|
||||
class CellLabeler extends React.Component<ComponentProps & StateProps> {
|
||||
render() {
|
||||
public override render() {
|
||||
return (
|
||||
<div className="CellLabeler">
|
||||
<div className="CellLabel">Cell {this.props.cellIndex + 1}</div>
|
||||
|
||||
@@ -20,7 +20,7 @@ interface DispatchProps {
|
||||
* HoverableCell sets the hovered cell
|
||||
*/
|
||||
class HoverableCell extends React.Component<ComponentProps & DispatchProps> {
|
||||
render() {
|
||||
public override render() {
|
||||
return (
|
||||
<div className="HoverableCell" onMouseEnter={this.props.hover} onMouseLeave={this.props.unHover}>
|
||||
{this.props.children}
|
||||
|
||||
@@ -173,11 +173,11 @@ function collectTarget(
|
||||
export class DraggableCellView extends React.Component<Props & DnDSourceProps & DnDTargetProps, State> {
|
||||
el?: HTMLDivElement | null;
|
||||
|
||||
state = {
|
||||
public override state = {
|
||||
hoverUpperHalf: true,
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
const connectDragPreview = this.props.connectDragPreview;
|
||||
const img = new (window as any).Image();
|
||||
|
||||
@@ -193,7 +193,7 @@ export class DraggableCellView extends React.Component<Props & DnDSourceProps &
|
||||
focusCell({ id, contentRef });
|
||||
};
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return this.props.connectDropTarget(
|
||||
// Sadly connectDropTarget _has_ to take a React element for a DOM element (no styled-divs)
|
||||
<div>
|
||||
|
||||
@@ -47,15 +47,15 @@ export class HijackScroll extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
public override componentDidUpdate(prevProps: Props) {
|
||||
this.scrollIntoViewIfNeeded(prevProps.focused);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.scrollIntoViewIfNeeded();
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return (
|
||||
<div
|
||||
onClick={this.props.selectCell}
|
||||
|
||||
@@ -31,18 +31,18 @@ export class KeyboardShortcuts extends React.Component<Props> {
|
||||
this.keyDown = this.keyDown.bind(this);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: Props) {
|
||||
public override shouldComponentUpdate(nextProps: Props) {
|
||||
const newContentRef = this.props.contentRef !== nextProps.contentRef;
|
||||
const newFocusedCell = this.props.focusedCell !== nextProps.focusedCell;
|
||||
const newCellOrder = this.props.cellOrder && this.props.cellOrder.size !== nextProps.cellOrder.size;
|
||||
return newContentRef || newFocusedCell || newCellOrder;
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
document.addEventListener("keydown", this.keyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
public override componentWillUnmount(): void {
|
||||
document.removeEventListener("keydown", this.keyDown);
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export class KeyboardShortcuts extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return <React.Fragment>{this.props.children}</React.Fragment>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export const Source = styled(BareSource)`
|
||||
width: -moz-available;
|
||||
`;
|
||||
export class PureMarkdownCell extends React.Component<ComponentProps & DispatchProps & StateProps> {
|
||||
render() {
|
||||
public override render() {
|
||||
const { contentRef, id, cell, children } = this.props;
|
||||
|
||||
const { isEditorFocused, isCellFocused, markdownOptions } = this.props;
|
||||
|
||||
@@ -33,7 +33,7 @@ interface DispatchProps {
|
||||
export class SandboxOutputs extends React.PureComponent<ComponentProps & StateProps & DispatchProps> {
|
||||
private childWindow: Window;
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
// Using min-width to set the width of the iFrame, works around an issue in iOS that can prevent the iFrame from sizing correctly.
|
||||
return (
|
||||
<IframeResizer
|
||||
@@ -72,11 +72,11 @@ export class SandboxOutputs extends React.PureComponent<ComponentProps & StatePr
|
||||
postRobot.send(this.childWindow, "props", props);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
this.sendPropsToFrame();
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
public override componentDidUpdate(): void {
|
||||
this.sendPropsToFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class SchemaAnalyzerComponent extends React.Component<
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
public override componentDidMount(): void {
|
||||
loadTransform(this.props);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export class SchemaAnalyzerComponent extends React.Component<
|
||||
this.props.runCell(this.props.contentRef, this.props.firstCellId);
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
public override render(): JSX.Element {
|
||||
const { firstCellId: id, contentRef, kernelStatus, outputs } = this.props;
|
||||
if (!id) {
|
||||
return <></>;
|
||||
|
||||
@@ -71,7 +71,7 @@ export class SchemaAnalyzerComponentAdapter extends NotebookComponentBootstrappe
|
||||
}
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
public override renderComponent(): JSX.Element {
|
||||
const props = {
|
||||
contentRef: this.contentRef,
|
||||
kernelRef: this.kernelRef,
|
||||
|
||||
602
src/Explorer/Panes/AddCollectionPane.html
Normal file
602
src/Explorer/Panes/AddCollectionPane.html
Normal file
@@ -0,0 +1,602 @@
|
||||
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
|
||||
<div class="contextual-pane-out" data-bind="setTemplateReady: true, click: cancel, clickBubble: false"></div>
|
||||
<div class="contextual-pane" data-bind="attr: { id: id }">
|
||||
<!-- Add collection form -- Start -->
|
||||
<div class="contextual-pane-in">
|
||||
<form data-bind="submit: submit" style="height: 100%">
|
||||
<div
|
||||
class="paneContentContainer"
|
||||
role="dialog"
|
||||
aria-labelledby="containerTitle"
|
||||
data-bind="template: { name: 'add-collection-inputs' }"
|
||||
></div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Add collection form -- End -->
|
||||
<!-- Loader - Start -->
|
||||
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
|
||||
<img class="dataExplorerLoader" src="/LoadingIndicator_3Squares.gif" />
|
||||
</div>
|
||||
<!-- Loader - End -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="add-collection-inputs">
|
||||
<!-- Add collection header - Start -->
|
||||
<div class="firstdivbg headerline">
|
||||
<span id="containerTitle" role="heading" aria-level="2" data-bind="text: title"></span>
|
||||
<div
|
||||
class="closeImg"
|
||||
id="closeBtnAddCollection"
|
||||
role="button"
|
||||
aria-label="Add collection close pane"
|
||||
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
|
||||
tabindex="0"
|
||||
>
|
||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add collection header - End -->
|
||||
|
||||
<!-- Add collection errors - Start -->
|
||||
<div class="warningErrorContainer" aria-live="assertive" data-bind="visible: formErrors() && formErrors() !== ''">
|
||||
<div class="warningErrorContent">
|
||||
<span><img class="paneErrorIcon" src="/error_red.svg" alt="Error" /></span>
|
||||
<span class="warningErrorDetailsLinkContainer">
|
||||
<span class="formErrors" data-bind="text: formErrors, attr: { title: formErrors }"></span>
|
||||
<a
|
||||
class="errorLink"
|
||||
role="link"
|
||||
data-bind="visible: formErrorsDetails() && formErrorsDetails() !== '' , click: showErrorDetails, event: { keypress: onMoreDetailsKeyPress }"
|
||||
tabindex="0"
|
||||
>
|
||||
More details</a
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warningErrorContainer" aria-live="assertive" data-bind="visible: formWarnings() && formWarnings() !== ''">
|
||||
<div class="warningErrorContent">
|
||||
<span><img class="paneErrorIcon" src="/warning.svg" alt="Warning" /></span>
|
||||
<span class="warningErrorDetailsLinkContainer">
|
||||
<span class="formErrors" data-bind="text: formWarnings, attr: { title: formWarnings }"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add collection errors - End -->
|
||||
|
||||
<!-- upsell message - start -->
|
||||
<div
|
||||
class="infoBoxContainer"
|
||||
aria-live="assertive"
|
||||
data-bind="visible: showUpsellMessage && showUpsellMessage() && formErrors && !formErrors()"
|
||||
>
|
||||
<div class="infoBoxContent">
|
||||
<span><img class="infoBoxIcon" src="/info_color.svg" alt="Promo" /></span>
|
||||
<span class="infoBoxDetails">
|
||||
<span class="infoBoxMessage" data-bind="text: upsellMessage, attr: { title: upsellMessage }"></span>
|
||||
<a
|
||||
class="underlinedLink"
|
||||
id="linkAddCollection"
|
||||
data-bind="text: upsellAnchorText, attr: { 'href': upsellAnchorUrl, 'aria-label': upsellMessageAriaLabel }"
|
||||
target="_blank"
|
||||
href=""
|
||||
tabindex="0"
|
||||
></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- upsell message - end -->
|
||||
|
||||
<!-- Add collection inputs - Start -->
|
||||
<div class="paneMainContent" data-bind="visible: !maxCollectionsReached()">
|
||||
<div data-bind="visible: !isPreferredApiTable()">
|
||||
<p>
|
||||
<span class="mandatoryStar">*</span>
|
||||
<span class="addCollectionLabel">Database id</span>
|
||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span class="tooltiptext infoTooltipWidth"
|
||||
>A database is analogous to a namespace. It is the unit of management for a set of containers.</span
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<div class="createNewDatabaseOrUseExisting">
|
||||
<input
|
||||
class="createNewDatabaseOrUseExistingRadio"
|
||||
aria-label="Create new database"
|
||||
name="databaseType"
|
||||
type="radio"
|
||||
role="radio"
|
||||
id="databaseCreateNew"
|
||||
data-test="addCollection-createNewDatabase"
|
||||
tabindex="0"
|
||||
data-bind="checked: databaseCreateNew, checkedValue: true, attr: { 'aria-checked': databaseCreateNew() ? 'true' : 'false' }"
|
||||
/>
|
||||
<span class="createNewDatabaseOrUseExistingSpace" for="databaseCreateNew">Create new</span>
|
||||
|
||||
<input
|
||||
class="createNewDatabaseOrUseExistingRadio"
|
||||
aria-label="Use existing database"
|
||||
name="databaseType"
|
||||
type="radio"
|
||||
role="radio"
|
||||
id="databaseUseExisting"
|
||||
data-test="addCollection-existingDatabase"
|
||||
tabindex="0"
|
||||
data-bind="checked: databaseCreateNew, checkedValue: false, attr: { 'aria-checked': !databaseCreateNew() ? 'true' : 'false' }"
|
||||
/>
|
||||
<span class="createNewDatabaseOrUseExistingSpace" for="databaseUseExisting">Use existing</span>
|
||||
</div>
|
||||
|
||||
<input
|
||||
name="newDatabaseId"
|
||||
id="databaseId"
|
||||
data-test="addCollection-newDatabaseId"
|
||||
aria-required="true"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
pattern="[^/?#\\]*[^/?# \\]"
|
||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||
placeholder="Type a new database id"
|
||||
size="40"
|
||||
class="collid"
|
||||
data-bind="visible: databaseCreateNew, textInput: databaseId, hasFocus: firstFieldHasFocus"
|
||||
autofocus
|
||||
/>
|
||||
|
||||
<input
|
||||
name="existingDatabaseId"
|
||||
id="existingDatabaseId"
|
||||
data-test="addCollection-existingDatabaseId"
|
||||
aria-required="true"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
pattern="[^/?#\\]*[^/?# \\]"
|
||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||
list="databasesList"
|
||||
placeholder="Choose an existing database"
|
||||
size="40"
|
||||
class="collid"
|
||||
data-bind="visible: !databaseCreateNew(), textInput: databaseId, hasFocus: firstFieldHasFocus"
|
||||
/>
|
||||
|
||||
<datalist id="databasesList" data-bind="foreach: databaseIds" data-bind="visible: databaseCreateNew">
|
||||
<option data-bind="value: $data"></option>
|
||||
</datalist>
|
||||
|
||||
<!-- Database provisioned throughput - Start -->
|
||||
<!-- ko if: canConfigureThroughput -->
|
||||
<div class="databaseProvision" aria-label="Provision database throughput" data-bind="visible: databaseCreateNew">
|
||||
<input
|
||||
tabindex="0"
|
||||
type="checkbox"
|
||||
data-test="addCollectionPane-databaseSharedThroughput"
|
||||
id="addCollection-databaseSharedThroughput"
|
||||
title="Provision database throughput"
|
||||
data-bind="checked: databaseCreateNewShared"
|
||||
/>
|
||||
<span class="databaseProvisionText" for="databaseSharedThroughput">Provision database throughput</span>
|
||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span class="tooltiptext provisionDatabaseThroughput"
|
||||
>Provisioned throughput at the database level will be shared across all containers within the
|
||||
database.</span
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div data-bind="visible: databaseCreateNewShared() && databaseCreateNew()">
|
||||
<!-- 1 -->
|
||||
<throughput-input-autopilot-v3
|
||||
params="{
|
||||
testId: 'databaseThroughputValue',
|
||||
value: throughputDatabase,
|
||||
minimum: minThroughputRU,
|
||||
maximum: maxThroughputRU,
|
||||
isEnabled: databaseCreateNewShared() && databaseCreateNew(),
|
||||
label: sharedThroughputRangeText,
|
||||
ariaLabel: sharedThroughputRangeText,
|
||||
costsVisible: costsVisible,
|
||||
requestUnitsUsageCost: requestUnitsUsageCost,
|
||||
spendAckChecked: throughputSpendAck,
|
||||
spendAckId: 'throughputSpendAck',
|
||||
spendAckText: throughputSpendAckText,
|
||||
spendAckVisible: throughputSpendAckVisible,
|
||||
showAsMandatory: true,
|
||||
infoBubbleText: ruToolTipText,
|
||||
throughputAutoPilotRadioId: 'newContainer-databaseThroughput-autoPilotRadio',
|
||||
throughputProvisionedRadioId: 'newContainer-databaseThroughput-manualRadio',
|
||||
throughputModeRadioName: 'sharedThroughputModeRadio',
|
||||
isAutoPilotSelected: isSharedAutoPilotSelected,
|
||||
maxAutoPilotThroughputSet: sharedAutoPilotThroughput,
|
||||
autoPilotUsageCost: autoPilotUsageCost,
|
||||
canExceedMaximumValue: canExceedMaximumValue,
|
||||
freeTierExceedThroughputTooltip: freeTierExceedThroughputTooltip
|
||||
}"
|
||||
>
|
||||
</throughput-input-autopilot-v3>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
<!-- Database provisioned throughput - End -->
|
||||
</div>
|
||||
|
||||
<div class="seconddivpadding">
|
||||
<p>
|
||||
<span class="mandatoryStar">*</span>
|
||||
<span class="addCollectionLabel" data-bind="text: collectionIdTitle"></span>
|
||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span class="tooltiptext infoTooltipWidth"
|
||||
>Unique identifier for the container and used for id-based routing through REST and all SDKs</span
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
<input
|
||||
name="collectionId"
|
||||
id="containerId"
|
||||
data-test="addCollection-collectionId"
|
||||
type="text"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
pattern="[^/?#\\]*[^/?# \\]"
|
||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||
placeholder="e.g., Container1"
|
||||
size="40"
|
||||
class="textfontclr collid"
|
||||
data-bind="value: collectionId"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Indexing For Shared Throughput - start -->
|
||||
<div class="seconddivpadding" data-bind="visible: showIndexingOptionsForSharedThroughput() && !isMongo()">
|
||||
<div
|
||||
class="useIndexingForSharedThroughput createNewDatabaseOrUseExisting"
|
||||
aria-label="Indexing For Shared Throughput"
|
||||
>
|
||||
<p>
|
||||
<span class="mandatoryStar">*</span>
|
||||
<span class="addCollectionLabel">Indexing</span>
|
||||
</p>
|
||||
<div>
|
||||
<input
|
||||
type="radio"
|
||||
id="useIndexingForSharedThroughputOn"
|
||||
name="useIndexingForSharedThroughput"
|
||||
value="on"
|
||||
class="createNewDatabaseOrUseExistingRadio"
|
||||
data-bind="checked: useIndexingForSharedThroughput, checkedValue: true"
|
||||
/>
|
||||
<span class="createNewDatabaseOrUseExistingSpace" for="useIndexingForSharedThroughputOn">Automatic</span>
|
||||
<input
|
||||
type="radio"
|
||||
id="useIndexingForSharedThroughputOff"
|
||||
name="useIndexingForSharedThroughput"
|
||||
value="off"
|
||||
class="createNewDatabaseOrUseExistingRadio"
|
||||
data-bind="checked: useIndexingForSharedThroughput, checkedValue: false"
|
||||
/>
|
||||
<span class="createNewDatabaseOrUseExistingSpace" for="useIndexingForSharedThroughputOff">Off</span>
|
||||
</div>
|
||||
<p data-bind="visible: useIndexingForSharedThroughput">
|
||||
All properties in your documents will be indexed by default for flexible and efficient queries.
|
||||
<a class="errorLink" href="https://aka.ms/cosmos-indexing-policy" target="_blank">Learn more</a>
|
||||
</p>
|
||||
<p data-bind="visible: useIndexingForSharedThroughput() === false">
|
||||
Indexing will be turned off. Recommended if you don't need to run queries or only have key value operations.
|
||||
<a class="errorLink" href="https://aka.ms/cosmos-indexing-policy" target="_blank">Learn more</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Indexing For Shared Throughput - end -->
|
||||
|
||||
<p
|
||||
class="seconddivpadding"
|
||||
data-bind="visible: isMongo() && !databaseHasSharedOffer() || container.isFixedCollectionWithSharedThroughputSupported"
|
||||
>
|
||||
<span class="mandatoryStar">*</span>
|
||||
<span class="addCollectionLabel">Storage capacity</span>
|
||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span class="tooltiptext infoTooltipWidth"
|
||||
>This is the maximum storage size of the container. Storage is billed per GB based on consumption.</span
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<div class="tabs">
|
||||
<div
|
||||
tabindex="0"
|
||||
data-bind="event: { keydown: onStorageOptionsKeyDown }, visible: isMongo() && !databaseHasSharedOffer() || container.isFixedCollectionWithSharedThroughputSupported"
|
||||
aria-label="Storage capacity"
|
||||
>
|
||||
<!-- Fixed option button - Start -->
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab1" name="storage" value="10" class="radio" data-bind="checked: storage" />
|
||||
<label for="tab1">Fixed (20 GB)</label>
|
||||
</div>
|
||||
<!-- Fixed option button - End -->
|
||||
|
||||
<!-- Unlimited option button - Start -->
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab2" name="storage" value="100" class="radio" data-bind="checked: storage" />
|
||||
<label for="tab2">Unlimited</label>
|
||||
</div>
|
||||
<!-- Unlimited option button - End -->
|
||||
</div>
|
||||
<!-- Unlimited Button Content - Start -->
|
||||
<div class="tabcontent" data-bind="visible: isUnlimitedStorageSelected() || databaseHasSharedOffer()">
|
||||
<div data-bind="visible: partitionKeyVisible">
|
||||
<p>
|
||||
<span class="mandatoryStar">*</span>
|
||||
<span class="addCollectionLabel" data-bind="text: partitionKeyName"></span>
|
||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span class="tooltiptext infoTooltipWidth"
|
||||
>The <span data-bind="text: partitionKeyName"></span> is used to automatically partition data among
|
||||
multiple servers for scalability. Choose a JSON property name that has a wide range of values and is
|
||||
likely to have evenly distributed access patterns.</span
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
id="addCollection-partitionKeyValue"
|
||||
data-test="addCollection-partitionKeyValue"
|
||||
aria-required="true"
|
||||
size="40"
|
||||
class="textfontclr collid"
|
||||
data-bind="textInput: partitionKey,
|
||||
attr: {
|
||||
placeholder: partitionKeyPlaceholder,
|
||||
required: partitionKeyVisible(),
|
||||
pattern: partitionKeyPattern,
|
||||
title: partitionKeyTitle
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<!-- large parition key - start -->
|
||||
<div class="largePartitionKey" aria-label="Large Partition Key" data-bind="visible: partitionKeyVisible">
|
||||
<input
|
||||
tabindex="0"
|
||||
type="checkbox"
|
||||
id="largePartitionKey"
|
||||
data-test="addCollection-largePartitionKey"
|
||||
title="Large Partition Key"
|
||||
data-bind="checked: largePartitionKey"
|
||||
/>
|
||||
<span for="largePartitionKey"
|
||||
>My <span data-bind="text: lowerCasePartitionKeyName"></span> is larger than 100 bytes</span
|
||||
>
|
||||
<p
|
||||
data-bind="visible: largePartitionKey"
|
||||
class="largePartitionKeyDescription"
|
||||
data-test="addCollection-largePartitionKeyDescription"
|
||||
>
|
||||
Old SDKs do not work with containers that support large
|
||||
<span data-bind="text: lowerCasePartitionKeyName"></span>s, ensure you are using the right SDK version.
|
||||
<a class="errorLink" href="https://aka.ms/cosmosdb/pkv2" target="_blank">Learn more</a>
|
||||
</p>
|
||||
</div>
|
||||
<!-- large parition key - end -->
|
||||
|
||||
<!-- ko if: canConfigureThroughput -->
|
||||
<!-- Provision collection throughput checkbox - start -->
|
||||
<div class="pkPadding" data-bind="visible: databaseHasSharedOffer() && !databaseCreateNew()">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="collectionSharedThroughput"
|
||||
data-bind="checked: collectionWithThroughputInShared, attr: {title:collectionWithThroughputInSharedTitle}"
|
||||
/>
|
||||
<span for="collectionSharedThroughput" data-bind="text: collectionWithThroughputInSharedTitle"></span>
|
||||
<span class="leftAlignInfoTooltip" role="tooltip" tabindex="0">
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span class="tooltiptext sharedCollectionThroughputTooltipWidth"
|
||||
>You can optionally provision dedicated throughput for a container within a database that has throughput
|
||||
provisioned. This dedicated throughput amount will not be shared with other containers in the database and
|
||||
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.</span
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<!-- Provision collection throughput checkbox - end -->
|
||||
|
||||
<!-- Provision collection throughput spinner - start -->
|
||||
<div data-bind="visible: displayCollectionThroughput" data-test="addCollection-displayCollectionThroughput">
|
||||
<!-- 3 -->
|
||||
<throughput-input-autopilot-v3
|
||||
params="{
|
||||
testId: 'collectionThroughputValue',
|
||||
value: throughputMultiPartition,
|
||||
minimum: minThroughputRU,
|
||||
maximum: maxThroughputRU,
|
||||
isEnabled: displayCollectionThroughput,
|
||||
label: throughputRangeText,
|
||||
ariaLabel: throughputRangeText,
|
||||
costsVisible: costsVisible,
|
||||
requestUnitsUsageCost: dedicatedRequestUnitsUsageCost,
|
||||
spendAckChecked: throughputSpendAck,
|
||||
spendAckId: 'throughputSpendAckCollection',
|
||||
spendAckText: throughputSpendAckText,
|
||||
spendAckVisible: throughputSpendAckVisible,
|
||||
showAsMandatory: true,
|
||||
infoBubbleText: ruToolTipText,
|
||||
throughputAutoPilotRadioId: 'newContainer-containerThroughput-autoPilotRadio',
|
||||
throughputProvisionedRadioId: 'newContainer-containerThroughput-manualRadio',
|
||||
throughputModeRadioName: 'throughputModeRadioName',
|
||||
isAutoPilotSelected: isAutoPilotSelected,
|
||||
maxAutoPilotThroughputSet: autoPilotThroughput,
|
||||
autoPilotUsageCost: autoPilotUsageCost,
|
||||
canExceedMaximumValue: canExceedMaximumValue,
|
||||
freeTierExceedThroughputTooltip: freeTierExceedThroughputTooltip
|
||||
}"
|
||||
>
|
||||
</throughput-input-autopilot-v3>
|
||||
</div>
|
||||
<!-- Provision collection throughput spinner - end -->
|
||||
<!-- /ko -->
|
||||
<!-- Provision collection throughput - end -->
|
||||
|
||||
<!-- Custom indexes for mongo checkbox - start -->
|
||||
<div class="pkPadding" data-bind="visible: isEnableMongoCapabilityEnabled()">
|
||||
<p>
|
||||
<span class="addCollectionLabel">Indexing</span>
|
||||
</p>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="mongoWildcardIndex"
|
||||
title="mongoWildcardIndex"
|
||||
data-bind="checked: shouldCreateMongoWildcardIndex"
|
||||
/>
|
||||
<span>Create a Wildcard Index on all fields</span>
|
||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span class="tooltiptext mongoWildcardIndexTooltipWidth">
|
||||
By default, only the field _id is indexed. Creating a wildcard index on all fields will quickly optimize
|
||||
query performance and is recommended during development.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- Custom indexes for mongo checkbox - end -->
|
||||
|
||||
<!-- Enable analytical storage - start -->
|
||||
<div
|
||||
class="enableAnalyticalStorage pkPadding"
|
||||
aria-label="Enable Analytical Store"
|
||||
data-bind="visible: isSynapseLinkSupported"
|
||||
>
|
||||
<div>
|
||||
<span class="mandatoryStar">*</span>
|
||||
<span class="addCollectionLabel">Analytical store</span>
|
||||
<span
|
||||
class="infoTooltip"
|
||||
role="tooltip"
|
||||
tabindex="0"
|
||||
data-bind="event: { focus: function(data, event) { transferFocus('tooltip1', 'link1') } }"
|
||||
>
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span id="tooltip1" class="tooltiptext infoTooltipWidth" data-bind="event: { mouseout: onMouseOut }">
|
||||
Enable analytical store capability to perform near real-time analytics on your operational data, without
|
||||
impacting the performance of transactional workloads. Learn more
|
||||
<a
|
||||
id="link1"
|
||||
class="errorLink"
|
||||
href="https://aka.ms/analytical-store-overview"
|
||||
target="_blank"
|
||||
data-bind="event: { focusout: onFocusOut, keydown: onKeyDown.bind($data, 'largePartitionKey') }"
|
||||
>here</a
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="paragraph">
|
||||
<input
|
||||
class="enableAnalyticalStorageRadio"
|
||||
id="enableAnalyticalStorageRadioOn"
|
||||
name="analyticalStore"
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
data-bind="
|
||||
disable: showEnableSynapseLink,
|
||||
checked: isAnalyticalStorageOn,
|
||||
checkedValue: true,
|
||||
attr: {
|
||||
'aria-checked': isAnalyticalStorageOn() ? 'true' : 'false'
|
||||
}"
|
||||
/>
|
||||
<label for="enableAnalyticalStorageRadioOn" class="enableAnalyticalStorageRadioLabel">
|
||||
<span data-bind="disable: showEnableSynapseLink"> On </span>
|
||||
</label>
|
||||
|
||||
<input
|
||||
class="enableAnalyticalStorageRadio"
|
||||
id="enableAnalyticalStorageRadioOff"
|
||||
name="analyticalStore"
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
data-bind="
|
||||
disable: showEnableSynapseLink,
|
||||
checked: isAnalyticalStorageOn,
|
||||
checkedValue: false,
|
||||
attr: {
|
||||
'aria-checked': isAnalyticalStorageOn() ? 'false' : 'true'
|
||||
}"
|
||||
/>
|
||||
<label for="enableAnalyticalStorageRadioOff" class="enableAnalyticalStorageRadioLabel">
|
||||
<span data-bind="disable: showEnableSynapseLink"> Off </span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="paragraph italic" data-bind="visible: ttl90DaysEnabled() && isAnalyticalStorageOn()">
|
||||
By default, Analytical Time-to-Live will be configured to retain 90 days of data in the analytical store.
|
||||
You can configure a custom retention policy in the 'Settings' tab.
|
||||
<span
|
||||
><a class="errorLink" href="https://aka.ms/cosmosdb-analytical-ttl" target="_blank">Learn more</a></span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="paragraph" data-bind="visible: showEnableSynapseLink">
|
||||
Azure Synapse Link is required for creating an analytical store container. Enable Synapse Link for this
|
||||
Cosmos DB account.
|
||||
<span><a class="errorLink" href="https://aka.ms/cosmosdb-synapselink" target="_blank">Learn more</a></span>
|
||||
</div>
|
||||
|
||||
<div class="paragraph" data-bind="visible: showEnableSynapseLink">
|
||||
<button
|
||||
class="button"
|
||||
type="button"
|
||||
data-bind="
|
||||
click: onEnableSynapseLinkButtonClicked,
|
||||
disable: isSynapseLinkUpdating,
|
||||
css: {
|
||||
enabled: !isSynapseLinkUpdating(),
|
||||
disabled: isSynapseLinkUpdating
|
||||
}
|
||||
"
|
||||
>
|
||||
Enable
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Enable analytical storage - end -->
|
||||
</div>
|
||||
<!-- Unlimited Button Content - End -->
|
||||
</div>
|
||||
<div class="uniqueIndexesContainer" data-bind="visible: uniqueKeysVisible">
|
||||
<p class="uniqueKeys">
|
||||
<span class="addCollectionLabel">Unique keys</span>
|
||||
<span class="uniqueInfoTooltip" role="tooltip" tabindex="0">
|
||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||
<span class="uniqueTooltiptext infoTooltipWidth"
|
||||
>Unique keys provide developers with the ability to add a layer of data integrity to their database. By
|
||||
creating a unique key policy when a container is created, you ensure the uniqueness of one or more values
|
||||
per partition key.</span
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
<dynamic-list
|
||||
params="{ listItems: uniqueKeys, placeholder: uniqueKeysPlaceholder(), ariaLabel: 'Write a comma separated path list of unique keys', buttonText: 'Add unique key' }"
|
||||
>
|
||||
</dynamic-list>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paneFooter">
|
||||
<div class="leftpanel-okbut">
|
||||
<input
|
||||
name="createCollection"
|
||||
id="submitBtnAddCollection"
|
||||
data-test="addCollection-createCollection"
|
||||
type="submit"
|
||||
value="OK"
|
||||
class="btncreatecoll1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div data-bind="visible: maxCollectionsReached">
|
||||
<error-display params="{ errorMsg: maxCollectionsReachedMessage }"></error-display>
|
||||
</div>
|
||||
<!-- Add collection inputs - End -->
|
||||
</script>
|
||||
108
src/Explorer/Panes/AddCollectionPane.test.ts
Normal file
108
src/Explorer/Panes/AddCollectionPane.test.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import Explorer from "../Explorer";
|
||||
import AddCollectionPane from "./AddCollectionPane";
|
||||
|
||||
const mockDatabaseAccount: DatabaseAccount = {
|
||||
id: "mock",
|
||||
kind: "DocumentDB",
|
||||
location: "",
|
||||
name: "mock",
|
||||
properties: {
|
||||
documentEndpoint: "",
|
||||
cassandraEndpoint: "",
|
||||
gremlinEndpoint: "",
|
||||
tableEndpoint: "",
|
||||
enableFreeTier: false,
|
||||
},
|
||||
type: undefined,
|
||||
};
|
||||
|
||||
const mockFreeTierDatabaseAccount: DatabaseAccount = {
|
||||
id: "mock",
|
||||
kind: "DocumentDB",
|
||||
location: "",
|
||||
name: "mock",
|
||||
properties: {
|
||||
documentEndpoint: "",
|
||||
cassandraEndpoint: "",
|
||||
gremlinEndpoint: "",
|
||||
tableEndpoint: "",
|
||||
enableFreeTier: true,
|
||||
},
|
||||
type: undefined,
|
||||
};
|
||||
|
||||
describe("Add Collection Pane", () => {
|
||||
describe("isValid()", () => {
|
||||
it("should be true if graph API and partition key is not /id nor /label", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableGremlin" }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const explorer = new Explorer();
|
||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
||||
addCollectionPane.partitionKey("/blah");
|
||||
expect(addCollectionPane.isValid()).toBe(true);
|
||||
});
|
||||
|
||||
it("should be false if graph API and partition key is /id or /label", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableGremlin" }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const explorer = new Explorer();
|
||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
||||
addCollectionPane.partitionKey("/id");
|
||||
expect(addCollectionPane.isValid()).toBe(false);
|
||||
|
||||
addCollectionPane.partitionKey("/label");
|
||||
expect(addCollectionPane.isValid()).toBe(false);
|
||||
});
|
||||
|
||||
it("should be true for any non-graph API with /id or /label partition key", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableCassandra" }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const explorer = new Explorer();
|
||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
||||
|
||||
addCollectionPane.partitionKey("/id");
|
||||
expect(addCollectionPane.isValid()).toBe(true);
|
||||
|
||||
addCollectionPane.partitionKey("/label");
|
||||
expect(addCollectionPane.isValid()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display free tier text in upsell messaging", () => {
|
||||
updateUserContext({ databaseAccount: mockFreeTierDatabaseAccount });
|
||||
const explorer = new Explorer();
|
||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
||||
expect(addCollectionPane.isFreeTierAccount()).toBe(true);
|
||||
expect(addCollectionPane.upsellMessage()).toContain("With free tier");
|
||||
expect(addCollectionPane.upsellAnchorUrl()).toBe(Constants.Urls.freeTierInformation);
|
||||
expect(addCollectionPane.upsellAnchorText()).toBe("Learn more");
|
||||
});
|
||||
|
||||
it("should display standard texr in upsell messaging", () => {
|
||||
updateUserContext({ databaseAccount: mockDatabaseAccount });
|
||||
const explorer = new Explorer();
|
||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
||||
expect(addCollectionPane.isFreeTierAccount()).toBe(false);
|
||||
expect(addCollectionPane.upsellMessage()).toContain("Start at");
|
||||
expect(addCollectionPane.upsellAnchorUrl()).toBe(Constants.Urls.cosmosPricing);
|
||||
expect(addCollectionPane.upsellAnchorText()).toBe("More details");
|
||||
});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user