Fix throughput input component and add database panel (#773)

This commit is contained in:
victor-meng 2021-05-12 11:56:24 -07:00 committed by GitHub
parent 0a6c7c0ff9
commit 2f6dbd83f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 177 additions and 299 deletions

View File

@ -19,18 +19,4 @@ describe("ThroughputInput Pane", () => {
it("should render Default properly", () => { it("should render Default properly", () => {
expect(wrapper).toMatchSnapshot(); 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");
});
}); });

View File

@ -17,8 +17,6 @@ export interface ThroughputInputProps {
setThroughputValue: (throughput: number) => void; setThroughputValue: (throughput: number) => void;
setIsAutoscale: (isAutoscale: boolean) => void; setIsAutoscale: (isAutoscale: boolean) => void;
onCostAcknowledgeChange: (isAcknowledged: boolean) => void; onCostAcknowledgeChange: (isAcknowledged: boolean) => void;
isAutoscaleSelected?: boolean;
throughput?: number;
} }
export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
@ -27,12 +25,16 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
setThroughputValue, setThroughputValue,
setIsAutoscale, setIsAutoscale,
isSharded, isSharded,
isAutoscaleSelected = true,
throughput = AutoPilotUtils.minAutoPilotThroughput,
onCostAcknowledgeChange, onCostAcknowledgeChange,
}: ThroughputInputProps) => { }: ThroughputInputProps) => {
const [isAutoscaleSelected, setIsAutoScaleSelected] = useState<boolean>(true);
const [throughput, setThroughput] = useState<number>(AutoPilotUtils.minAutoPilotThroughput);
const [isCostAcknowledged, setIsCostAcknowledged] = useState<boolean>(false); const [isCostAcknowledged, setIsCostAcknowledged] = useState<boolean>(false);
const [throughputError, setThroughputError] = useState<string>(""); const [throughputError, setThroughputError] = useState<string>("");
setIsAutoscale(isAutoscaleSelected);
setThroughputValue(throughput);
const getThroughputLabelText = (): string => { const getThroughputLabelText = (): string => {
let throughputHeaderText: string; let throughputHeaderText: string;
if (isAutoscaleSelected) { if (isAutoscaleSelected) {
@ -49,6 +51,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
const onThroughputValueChange = (newInput: string): void => { const onThroughputValueChange = (newInput: string): void => {
const newThroughput = parseInt(newInput); const newThroughput = parseInt(newInput);
setThroughput(newThroughput);
setThroughputValue(newThroughput); setThroughputValue(newThroughput);
if (!isSharded && newThroughput > 10000) { if (!isSharded && newThroughput > 10000) {
setThroughputError("Unsharded collections support up to 10,000 RUs"); setThroughputError("Unsharded collections support up to 10,000 RUs");
@ -82,9 +85,13 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
const handleOnChangeMode = (event: React.ChangeEvent<HTMLInputElement>, mode: string): void => { const handleOnChangeMode = (event: React.ChangeEvent<HTMLInputElement>, mode: string): void => {
if (mode === "Autoscale") { if (mode === "Autoscale") {
setThroughput(AutoPilotUtils.minAutoPilotThroughput);
setIsAutoScaleSelected(true);
setThroughputValue(AutoPilotUtils.minAutoPilotThroughput); setThroughputValue(AutoPilotUtils.minAutoPilotThroughput);
setIsAutoscale(true); setIsAutoscale(true);
} else { } else {
setThroughput(SharedConstants.CollectionCreation.DefaultCollectionRUs400);
setIsAutoScaleSelected(false);
setThroughputValue(SharedConstants.CollectionCreation.DefaultCollectionRUs400); setThroughputValue(SharedConstants.CollectionCreation.DefaultCollectionRUs400);
setIsAutoscale(false); setIsAutoscale(false);
} }

View File

@ -25,7 +25,7 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { getCollectionName } from "../../Utils/APITypeUtils"; import { getCollectionName } from "../../Utils/APITypeUtils";
import { isCapabilityEnabled } from "../../Utils/CapabilityUtils"; import { isCapabilityEnabled, isServerlessAccount } from "../../Utils/CapabilityUtils";
import { getUpsellMessage } from "../../Utils/PricingUtils"; import { getUpsellMessage } from "../../Utils/PricingUtils";
import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent"; import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent";
import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput"; import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput";
@ -182,7 +182,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
} }
/> />
{!this.isServerlessAccount() && ( {!isServerlessAccount() && (
<Stack horizontal> <Stack horizontal>
<Checkbox <Checkbox
label={`Share throughput across ${getCollectionName(true).toLocaleLowerCase()}`} label={`Share throughput across ${getCollectionName(true).toLocaleLowerCase()}`}
@ -207,14 +207,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</Stack> </Stack>
)} )}
{!this.isServerlessAccount() && this.state.isSharedThroughputChecked && ( {!isServerlessAccount() && this.state.isSharedThroughputChecked && (
<ThroughputInput <ThroughputInput
showFreeTierExceedThroughputTooltip={ showFreeTierExceedThroughputTooltip={
this.isFreeTierAccount() && !this.props.explorer.isFirstResourceCreated() this.isFreeTierAccount() && !this.props.explorer.isFirstResourceCreated()
} }
isDatabase={true} isDatabase={true}
isAutoscaleSelected={this.isNewDatabaseAutoscale}
throughput={this.newDatabaseThroughput}
isSharded={this.state.isSharded} isSharded={this.state.isSharded}
setThroughputValue={(throughput: number) => (this.newDatabaseThroughput = throughput)} setThroughputValue={(throughput: number) => (this.newDatabaseThroughput = throughput)}
setIsAutoscale={(isAutoscale: boolean) => (this.isNewDatabaseAutoscale = isAutoscale)} setIsAutoscale={(isAutoscale: boolean) => (this.isNewDatabaseAutoscale = isAutoscale)}
@ -398,7 +396,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if ( if (
userContext.apiType !== "Mongo" && userContext.apiType !== "Mongo" &&
this.state.partitionKey === "" && !this.state.partitionKey &&
!event.target.value.startsWith("/") !event.target.value.startsWith("/")
) { ) {
this.setState({ partitionKey: "/" + event.target.value }); this.setState({ partitionKey: "/" + event.target.value });
@ -410,7 +408,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</Stack> </Stack>
)} )}
{!this.isServerlessAccount() && !this.state.createNewDatabase && this.isSelectedDatabaseSharedThroughput() && ( {!isServerlessAccount() && !this.state.createNewDatabase && this.isSelectedDatabaseSharedThroughput() && (
<Stack horizontal verticalAlign="center"> <Stack horizontal verticalAlign="center">
<Checkbox <Checkbox
label={`Provision dedicated throughput for this ${getCollectionName().toLocaleLowerCase()}`} label={`Provision dedicated throughput for this ${getCollectionName().toLocaleLowerCase()}`}
@ -444,8 +442,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
this.isFreeTierAccount() && !this.props.explorer.isFirstResourceCreated() this.isFreeTierAccount() && !this.props.explorer.isFirstResourceCreated()
} }
isDatabase={false} isDatabase={false}
isAutoscaleSelected={this.isCollectionAutoscale}
throughput={this.collectionThroughput}
isSharded={this.state.isSharded} isSharded={this.state.isSharded}
setThroughputValue={(throughput: number) => (this.collectionThroughput = throughput)} setThroughputValue={(throughput: number) => (this.collectionThroughput = throughput)}
setIsAutoscale={(isAutoscale: boolean) => (this.isCollectionAutoscale = isAutoscale)} setIsAutoscale={(isAutoscale: boolean) => (this.isCollectionAutoscale = isAutoscale)}
@ -755,14 +751,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
return userContext.databaseAccount?.properties?.enableFreeTier; return userContext.databaseAccount?.properties?.enableFreeTier;
} }
private isServerlessAccount(): boolean {
return userContext.databaseAccount.properties?.capabilities?.some(
(capability) => capability.name === Constants.CapabilityNames.EnableServerless
);
}
private getSharedThroughputDefault(): boolean { private getSharedThroughputDefault(): boolean {
return userContext.subscriptionType !== SubscriptionType.EA && !this.isServerlessAccount(); return userContext.subscriptionType !== SubscriptionType.EA && !isServerlessAccount();
} }
private getFreeTierIndexingText(): string { private getFreeTierIndexingText(): string {
@ -800,7 +790,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
} }
private shouldShowCollectionThroughputInput(): boolean { private shouldShowCollectionThroughputInput(): boolean {
if (this.isServerlessAccount()) { if (isServerlessAccount()) {
return false; return false;
} }
@ -830,7 +820,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
return false; return false;
} }
if (this.isServerlessAccount()) { if (isServerlessAccount()) {
return false; return false;
} }

View File

@ -1,10 +1,9 @@
import { Checkbox, Text, TextField } from "@fluentui/react"; import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
import React, { FunctionComponent, useEffect, useState } from "react"; import React, { FunctionComponent, useEffect, useState } from "react";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { createDatabase } from "../../../Common/dataAccess/createDatabase"; import { createDatabase } from "../../../Common/dataAccess/createDatabase";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip"; import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
import { configContext, Platform } from "../../../ConfigContext";
import * as DataModels from "../../../Contracts/DataModels"; import * as DataModels from "../../../Contracts/DataModels";
import { SubscriptionType } from "../../../Contracts/SubscriptionType"; import { SubscriptionType } from "../../../Contracts/SubscriptionType";
import * as SharedConstants from "../../../Shared/Constants"; import * as SharedConstants from "../../../Shared/Constants";
@ -12,7 +11,8 @@ import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryCons
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../../UserContext"; import { userContext } from "../../../UserContext";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils"; import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import * as PricingUtils from "../../../Utils/PricingUtils"; import { isServerlessAccount } from "../../../Utils/CapabilityUtils";
import { getUpsellMessage } from "../../../Utils/PricingUtils";
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput"; import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
import Explorer from "../../Explorer"; import Explorer from "../../Explorer";
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent"; import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
@ -29,17 +29,10 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
closePanel, closePanel,
openNotificationConsole, openNotificationConsole,
}: AddDatabasePaneProps) => { }: AddDatabasePaneProps) => {
let throughput: number;
let isAutoscaleSelected: boolean;
let isCostAcknowledged: boolean;
const { subscriptionType } = userContext; const { subscriptionType } = userContext;
const getSharedThroughputDefault = !(subscriptionType === SubscriptionType.EA || container.isServerlessEnabled());
const _isAutoPilotSelectedAndWhatTier = (): DataModels.AutoPilotCreationSettings => {
if (isAutoPilotSelected && maxAutoPilotThroughputSet) {
return {
maxThroughput: maxAutoPilotThroughputSet * 1,
};
}
return undefined;
};
const isCassandraAccount: boolean = userContext.apiType === "Cassandra"; const isCassandraAccount: boolean = userContext.apiType === "Cassandra";
const databaseLabel: string = isCassandraAccount ? "keyspace" : "database"; const databaseLabel: string = isCassandraAccount ? "keyspace" : "database";
const collectionsLabel: string = isCassandraAccount ? "tables" : "collections"; const collectionsLabel: string = isCassandraAccount ? "tables" : "collections";
@ -52,61 +45,14 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
} is a logical container of one or more ${isCassandraAccount ? "tables" : "collections"}`; } is a logical container of one or more ${isCassandraAccount ? "tables" : "collections"}`;
const databaseLevelThroughputTooltipText = `Provisioned throughput at the ${databaseLabel} level will be shared across all ${collectionsLabel} within the ${databaseLabel}.`; const databaseLevelThroughputTooltipText = `Provisioned throughput at the ${databaseLabel} level will be shared across all ${collectionsLabel} within the ${databaseLabel}.`;
const [databaseCreateNewShared, setDatabaseCreateNewShared] = useState<boolean>(getSharedThroughputDefault); const [databaseCreateNewShared, setDatabaseCreateNewShared] = useState<boolean>(
subscriptionType !== SubscriptionType.EA && !isServerlessAccount()
);
const [formErrorsDetails, setFormErrorsDetails] = useState<string>(); const [formErrorsDetails, setFormErrorsDetails] = useState<string>();
const [formErrors, setFormErrors] = useState<string>(""); const [formErrors, setFormErrors] = useState<string>("");
const [isAutoPilotSelected, setIsAutoPilotSelected] = useState<boolean>(container.isAutoscaleDefaultEnabled());
const throughputDefaults = container.collectionCreationDefaults.throughput;
const [throughput, setThroughput] = useState<number>(
isAutoPilotSelected ? AutoPilotUtils.minAutoPilotThroughput : throughputDefaults.shared
);
const [throughputSpendAck, setThroughputSpendAck] = useState<boolean>(false);
const canRequestSupport = () => {
if (
configContext.platform !== Platform.Emulator &&
!userContext.isTryCosmosDBSubscription &&
configContext.platform !== Platform.Portal
) {
const offerThroughput: number = throughput;
return offerThroughput <= 100000;
}
return false;
};
const isFreeTierAccount: boolean = userContext.databaseAccount?.properties?.enableFreeTier;
const upsellMessage: string = PricingUtils.getUpsellMessage(
userContext.portalEnv,
isFreeTierAccount,
container.isFirstResourceCreated(),
false
);
const upsellAnchorUrl: string = isFreeTierAccount ? Constants.Urls.freeTierInformation : Constants.Urls.cosmosPricing;
const upsellAnchorText: string = isFreeTierAccount ? "Learn more" : "More details";
const maxAutoPilotThroughputSet = AutoPilotUtils.minAutoPilotThroughput;
const canConfigureThroughput = !container.isServerlessEnabled();
const showUpsellMessage = () => {
if (container.isServerlessEnabled()) {
return false;
}
if (isFreeTierAccount) {
return databaseCreateNewShared;
}
return true;
};
const [isExecuting, setIsExecuting] = useState<boolean>(false); const [isExecuting, setIsExecuting] = useState<boolean>(false);
useEffect(() => { const isFreeTierAccount: boolean = userContext.databaseAccount?.properties?.enableFreeTier;
setDatabaseCreateNewShared(getSharedThroughputDefault);
}, [subscriptionType]);
const addDatabasePaneMessage = { const addDatabasePaneMessage = {
database: { database: {
@ -126,7 +72,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
subscriptionType: SubscriptionType[subscriptionType], subscriptionType: SubscriptionType[subscriptionType],
subscriptionQuotaId: userContext.quotaId, subscriptionQuotaId: userContext.quotaId,
defaultsCheck: { defaultsCheck: {
throughput: throughput, throughput,
flight: userContext.addCollectionFlight, flight: userContext.addCollectionFlight,
}, },
dataExplorerArea: Constants.Areas.ContextualPane, dataExplorerArea: Constants.Areas.ContextualPane,
@ -139,11 +85,9 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
return; return;
} }
const offerThroughput: number = _computeOfferThroughput();
const addDatabasePaneStartMessage = { const addDatabasePaneStartMessage = {
...addDatabasePaneMessage, ...addDatabasePaneMessage,
offerThroughput, throughput,
}; };
const startKey: number = TelemetryProcessor.traceStart(Action.CreateDatabase, addDatabasePaneStartMessage); const startKey: number = TelemetryProcessor.traceStart(Action.CreateDatabase, addDatabasePaneStartMessage);
setFormErrors(""); setFormErrors("");
@ -153,18 +97,18 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
databaseId: addDatabasePaneStartMessage.database.id, databaseId: addDatabasePaneStartMessage.database.id,
databaseLevelThroughput: addDatabasePaneStartMessage.database.shared, databaseLevelThroughput: addDatabasePaneStartMessage.database.shared,
}; };
if (isAutoPilotSelected) { if (isAutoscaleSelected) {
createDatabaseParams.autoPilotMaxThroughput = addDatabasePaneStartMessage.offerThroughput; createDatabaseParams.autoPilotMaxThroughput = addDatabasePaneStartMessage.throughput;
} else { } else {
createDatabaseParams.offerThroughput = addDatabasePaneStartMessage.offerThroughput; createDatabaseParams.offerThroughput = addDatabasePaneStartMessage.throughput;
} }
createDatabase(createDatabaseParams).then( createDatabase(createDatabaseParams).then(
() => { () => {
_onCreateDatabaseSuccess(offerThroughput, startKey); _onCreateDatabaseSuccess(throughput, startKey);
}, },
(error: string) => { (error: string) => {
_onCreateDatabaseFailure(error, offerThroughput, startKey); _onCreateDatabaseFailure(error, throughput, startKey);
} }
); );
}; };
@ -194,48 +138,19 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
TelemetryProcessor.traceFailure(Action.CreateDatabase, addDatabasePaneFailedMessage, startKey); TelemetryProcessor.traceFailure(Action.CreateDatabase, addDatabasePaneFailedMessage, startKey);
}; };
const _getThroughput = (): number => {
return isNaN(throughput) ? 0 : Number(throughput);
};
const _computeOfferThroughput = (): number => {
if (!canConfigureThroughput) {
return undefined;
}
return _getThroughput();
};
const _isValid = (): boolean => { const _isValid = (): boolean => {
// TODO add feature flag that disables validation for customers with custom accounts // TODO add feature flag that disables validation for customers with custom accounts
if (isAutoPilotSelected) { if (isAutoscaleSelected) {
const autoPilot = _isAutoPilotSelectedAndWhatTier(); if (!AutoPilotUtils.isValidAutoPilotThroughput(throughput)) {
if (
!autoPilot ||
!autoPilot.maxThroughput ||
!AutoPilotUtils.isValidAutoPilotThroughput(autoPilot.maxThroughput)
) {
setFormErrors( setFormErrors(
`Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput` `Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput`
); );
return false; return false;
} }
} }
const throughput = _getThroughput();
if (throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && !throughputSpendAck) { if (throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && !isCostAcknowledged) {
setFormErrors(`Please acknowledge the estimated daily spend.`); setFormErrors(`Please acknowledge the estimated ${isAutoscaleSelected ? "monthly" : "daily"} spend.`);
return false;
}
const autoscaleThroughput = maxAutoPilotThroughputSet * 1;
if (
isAutoPilotSelected &&
autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
!throughputSpendAck
) {
setFormErrors(`Please acknowledge the estimated monthly spend.`);
return false; return false;
} }
@ -250,7 +165,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
); );
const props: RightPaneFormProps = { const props: RightPaneFormProps = {
expandConsole: container.expandConsole, expandConsole: openNotificationConsole,
formError: formErrors, formError: formErrors,
formErrorDetail: formErrorsDetails, formErrorDetail: formErrorsDetails,
isExecuting, isExecuting,
@ -260,81 +175,66 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
return ( return (
<RightPaneForm {...props}> <RightPaneForm {...props}>
<div className="paneContentContainer" role="dialog" aria-labelledby="databaseTitle"> {!formErrors && isFreeTierAccount && (
{showUpsellMessage && formErrors === "" && ( <PanelInfoErrorComponent
<PanelInfoErrorComponent message={getUpsellMessage(userContext.portalEnv, true, container.isFirstResourceCreated(), true)}
message={upsellMessage} messageType="info"
messageType="info" showErrorDetails={false}
showErrorDetails={false} openNotificationConsole={openNotificationConsole}
openNotificationConsole={openNotificationConsole} link={Constants.Urls.freeTierInformation}
link={upsellAnchorUrl} linkText="Learn more"
linkText={upsellAnchorText} />
)}
<div className="panelMainContent">
<div>
<Stack horizontal>
<span className="mandatoryStar">*</span>
<Text variant="small">{databaseIdLabel}</Text>
<InfoTooltip>{databaseIdTooltipText}</InfoTooltip>
</Stack>
<TextField
id="database-id"
type="text"
aria-required="true"
autoComplete="off"
pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'"
size={40}
aria-label={databaseIdLabel}
placeholder={databaseIdPlaceHolder}
value={databaseId}
onChange={handleonChangeDBId}
autoFocus
style={{ fontSize: 12 }}
styles={{ root: { width: 300 } }}
/> />
)}
<div className="paneMainContent">
<div>
<p>
<span className="mandatoryStar">*</span>
<Text variant="small">{databaseIdLabel}</Text>
<InfoTooltip>{databaseIdTooltipText}</InfoTooltip>
</p>
<TextField <Stack horizontal>
id="database-id" <Checkbox
type="text" title="Provision shared throughput"
aria-required="true" styles={{
autoComplete="off" text: { fontSize: 12 },
pattern="[^/?#\\]*[^/?# \\]" checkbox: { width: 12, height: 12 },
title="May not end with space nor contain characters '\' '/' '#' '?'" label: { padding: 0, alignItems: "center" },
size={40} }}
aria-label={databaseIdLabel} label="Provision throughput"
placeholder={databaseIdPlaceHolder} checked={databaseCreateNewShared}
value={databaseId} onChange={() => setDatabaseCreateNewShared(!databaseCreateNewShared)}
onChange={handleonChangeDBId}
autoFocus
/> />
<InfoTooltip>{databaseLevelThroughputTooltipText}</InfoTooltip>
</Stack>
<div {!isServerlessAccount() && databaseCreateNewShared && (
className="databaseProvision" <ThroughputInput
aria-label="New database provision support" showFreeTierExceedThroughputTooltip={isFreeTierAccount && !container?.isFirstResourceCreated()}
style={{ display: "block ruby" }} isDatabase={true}
> isSharded={databaseCreateNewShared}
<Checkbox setThroughputValue={(newThroughput: number) => (throughput = newThroughput)}
title="Provision shared throughput" setIsAutoscale={(isAutoscale: boolean) => (isAutoscaleSelected = isAutoscale)}
styles={{ onCostAcknowledgeChange={(isAcknowledged: boolean) => (isCostAcknowledged = isAcknowledged)}
checkbox: { width: 12, height: 12 }, />
label: { padding: 0, alignItems: "center" }, )}
}}
label="Provision throughput"
checked={databaseCreateNewShared}
onChange={() => setDatabaseCreateNewShared(!databaseCreateNewShared)}
/>{" "}
<InfoTooltip>{databaseLevelThroughputTooltipText}</InfoTooltip>
</div>
{databaseCreateNewShared && (
<div>
<ThroughputInput
showFreeTierExceedThroughputTooltip={isFreeTierAccount && !container?.isFirstResourceCreated()}
isDatabase={true}
isSharded={databaseCreateNewShared}
isAutoscaleSelected={isAutoPilotSelected}
throughput={throughput}
setThroughputValue={(throughput: number) => setThroughput(throughput)}
setIsAutoscale={(isAutoscale: boolean) => setIsAutoPilotSelected(isAutoscale)}
onCostAcknowledgeChange={(isAcknowledged: boolean) => setThroughputSpendAck(isAcknowledged)}
/>
{canRequestSupport() && (
<p>
<a href="https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20More%20Throughput%20Request">
Contact support{" "}
</a>
for more than <span>{throughputDefaults.unlimitedmax?.toLocaleString()} </span> RU/s.
</p>
)}
</div>
)}
</div>
</div> </div>
</div> </div>
</RightPaneForm> </RightPaneForm>

View File

@ -9,95 +9,87 @@ exports[`AddDatabasePane Pane should render Default properly 1`] = `
submitButtonText="OK" submitButtonText="OK"
> >
<div <div
aria-labelledby="databaseTitle" className="panelMainContent"
className="paneContentContainer"
role="dialog"
> >
<PanelInfoErrorComponent <div>
link="https://aka.ms/azure-cosmos-db-pricing" <Stack
linkText="More details" horizontal={true}
message="Start at $24/mo per database, multiple containers included" >
messageType="info" <span
openNotificationConsole={[Function]} className="mandatoryStar"
showErrorDetails={false} >
/> *
<div </span>
className="paneMainContent" <Text
> variant="small"
<div> >
<p> Database id
<span </Text>
className="mandatoryStar" <InfoTooltip>
> A database is a logical container of one or more collections
* </InfoTooltip>
</span> </Stack>
<Text <StyledTextFieldBase
variant="small" aria-label="Database id"
> aria-required="true"
Database id autoComplete="off"
</Text> autoFocus={true}
<InfoTooltip> id="database-id"
A database is a logical container of one or more collections onChange={[Function]}
</InfoTooltip> pattern="[^/?#\\\\\\\\]*[^/?# \\\\\\\\]"
</p> placeholder="Type a new database id"
<StyledTextFieldBase size={40}
aria-label="Database id" style={
aria-required="true" Object {
autoComplete="off" "fontSize": 12,
autoFocus={true} }
id="database-id" }
styles={
Object {
"root": Object {
"width": 300,
},
}
}
title="May not end with space nor contain characters '\\\\' '/' '#' '?'"
type="text"
value=""
/>
<Stack
horizontal={true}
>
<StyledCheckboxBase
checked={true}
label="Provision throughput"
onChange={[Function]} onChange={[Function]}
pattern="[^/?#\\\\\\\\]*[^/?# \\\\\\\\]" styles={
placeholder="Type a new database id"
size={40}
title="May not end with space nor contain characters '\\\\' '/' '#' '?'"
type="text"
value=""
/>
<div
aria-label="New database provision support"
className="databaseProvision"
style={
Object { Object {
"display": "block ruby", "checkbox": Object {
"height": 12,
"width": 12,
},
"label": Object {
"alignItems": "center",
"padding": 0,
},
"text": Object {
"fontSize": 12,
},
} }
} }
> title="Provision shared throughput"
<StyledCheckboxBase />
checked={true} <InfoTooltip>
label="Provision throughput" Provisioned throughput at the database level will be shared across all collections within the database.
onChange={[Function]} </InfoTooltip>
styles={ </Stack>
Object { <ThroughputInput
"checkbox": Object { isDatabase={true}
"height": 12, isSharded={true}
"width": 12, onCostAcknowledgeChange={[Function]}
}, setIsAutoscale={[Function]}
"label": Object { setThroughputValue={[Function]}
"alignItems": "center", />
"padding": 0,
},
}
}
title="Provision shared throughput"
/>
<InfoTooltip>
Provisioned throughput at the database level will be shared across all collections within the database.
</InfoTooltip>
</div>
<div>
<ThroughputInput
isAutoscaleSelected={false}
isDatabase={true}
isSharded={true}
onCostAcknowledgeChange={[Function]}
setIsAutoscale={[Function]}
setThroughputValue={[Function]}
throughput={400}
/>
</div>
</div>
</div> </div>
</div> </div>
</RightPaneForm> </RightPaneForm>

View File

@ -1,4 +1,7 @@
import * as Constants from "../Common/Constants";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
export const isCapabilityEnabled = (capabilityName: string): boolean => export const isCapabilityEnabled = (capabilityName: string): boolean =>
userContext.databaseAccount?.properties?.capabilities?.some((capability) => capability.name === capabilityName); userContext.databaseAccount?.properties?.capabilities?.some((capability) => capability.name === capabilityName);
export const isServerlessAccount = (): boolean => isCapabilityEnabled(Constants.CapabilityNames.EnableServerless);

View File

@ -18,7 +18,7 @@ export const useSidePanel = (): SidePanelHooks => {
setHeaderText(headerText); setHeaderText(headerText);
setPanelContent(panelContent); setPanelContent(panelContent);
setIsPanelOpen(true); setIsPanelOpen(true);
setOnCloseCallback({ callback: onClose }); !!onClose && setOnCloseCallback({ callback: onClose });
}; };
const closeSidePanel = (): void => { const closeSidePanel = (): void => {