New quick start - create sample container (#1259)
* Update links and texts * Remove unintended change * Quick start - create sample container * Small adjustments + fix test * Hide coach mark behind feature flag * Add snapshot test for AddCollectionPanel to increase coverage * Fix snapshot * Fix snapshot 2... * Change runner account name * Change portal runner account name
This commit is contained in:
parent
d05a05716f
commit
ebbfc5f517
|
@ -39,7 +39,7 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
|||
const items: TreeNodeMenuItem[] = [
|
||||
{
|
||||
iconSrc: AddCollectionIcon,
|
||||
onClick: () => container.onNewCollectionClicked(databaseId),
|
||||
onClick: () => container.onNewCollectionClicked({ databaseId }),
|
||||
label: `New ${getCollectionName()}`,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -16,6 +16,7 @@ export interface ThroughputInputProps {
|
|||
isSharded: boolean;
|
||||
isFreeTier: boolean;
|
||||
showFreeTierExceedThroughputTooltip: boolean;
|
||||
isQuickstart?: boolean;
|
||||
setThroughputValue: (throughput: number) => void;
|
||||
setIsAutoscale: (isAutoscale: boolean) => void;
|
||||
setIsThroughputCapExceeded: (isThroughputCapExceeded: boolean) => void;
|
||||
|
@ -226,6 +227,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||
</Stack>
|
||||
|
||||
<TextField
|
||||
id="autoscaleRUValueField"
|
||||
type="number"
|
||||
styles={{
|
||||
fieldGroup: { width: 300, height: 27 },
|
||||
|
|
|
@ -1637,6 +1637,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||
<StyledTextFieldBase
|
||||
aria-label="Max request units per second"
|
||||
errorMessage=""
|
||||
id="autoscaleRUValueField"
|
||||
key=".0:$.2"
|
||||
min={1000}
|
||||
onChange={[Function]}
|
||||
|
@ -1660,6 +1661,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||
aria-label="Max request units per second"
|
||||
deferredValidationTime={200}
|
||||
errorMessage=""
|
||||
id="autoscaleRUValueField"
|
||||
min={1000}
|
||||
onChange={[Function]}
|
||||
required={true}
|
||||
|
@ -1955,7 +1957,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||
<input
|
||||
aria-invalid={false}
|
||||
className="ms-TextField-field field-64"
|
||||
id="TextField2"
|
||||
id="autoscaleRUValueField"
|
||||
min={1000}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
|
|
|
@ -68,11 +68,10 @@ export class ContainerSampleGenerator {
|
|||
return database.findCollectionWithId(this.sampleDataFile.collectionId);
|
||||
}
|
||||
|
||||
private async populateContainerAsync(collection: ViewModels.Collection): Promise<void> {
|
||||
public async populateContainerAsync(collection: ViewModels.Collection): Promise<void> {
|
||||
if (!collection) {
|
||||
throw new Error("No container to populate");
|
||||
}
|
||||
const promises: Q.Promise<any>[] = [];
|
||||
|
||||
if (userContext.apiType === "Gremlin") {
|
||||
// For Gremlin, all queries are executed sequentially, because some queries might be dependent on other queries
|
||||
|
|
|
@ -1131,7 +1131,13 @@ export default class Explorer {
|
|||
}
|
||||
}
|
||||
|
||||
public async onNewCollectionClicked(databaseId?: string): Promise<void> {
|
||||
public async onNewCollectionClicked(
|
||||
options: {
|
||||
databaseId?: string;
|
||||
isQuickstart?: boolean;
|
||||
showTeachingBubble?: boolean;
|
||||
} = {}
|
||||
): Promise<void> {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
useSidePanel
|
||||
.getState()
|
||||
|
@ -1146,7 +1152,7 @@ export default class Explorer {
|
|||
: await useDatabases.getState().loadDatabaseOffers();
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel("New " + getCollectionName(), <AddCollectionPanel explorer={this} databaseId={databaseId} />);
|
||||
.openSidePanel("New " + getCollectionName(), <AddCollectionPanel explorer={this} {...options} />);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import Explorer from "../Explorer";
|
||||
import { AddCollectionPanel } from "./AddCollectionPanel";
|
||||
|
||||
const props = {
|
||||
explorer: new Explorer(),
|
||||
};
|
||||
|
||||
describe("AddCollectionPanel", () => {
|
||||
it("should render Default properly", () => {
|
||||
const wrapper = shallow(<AddCollectionPanel {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -10,6 +10,7 @@ import {
|
|||
Link,
|
||||
Separator,
|
||||
Stack,
|
||||
TeachingBubble,
|
||||
Text,
|
||||
TooltipHost,
|
||||
} from "@fluentui/react";
|
||||
|
@ -30,6 +31,7 @@ import { isCapabilityEnabled, isServerlessAccount } from "Utils/CapabilityUtils"
|
|||
import { getUpsellMessage } from "Utils/PricingUtils";
|
||||
import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||
import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput";
|
||||
import { ContainerSampleGenerator } from "../DataSamples/ContainerSampleGenerator";
|
||||
import Explorer from "../Explorer";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { PanelFooterComponent } from "./PanelFooterComponent";
|
||||
|
@ -39,6 +41,8 @@ import { PanelLoadingScreen } from "./PanelLoadingScreen";
|
|||
export interface AddCollectionPanelProps {
|
||||
explorer: Explorer;
|
||||
databaseId?: string;
|
||||
isQuickstart?: boolean;
|
||||
showTeachingBubble?: boolean;
|
||||
}
|
||||
|
||||
const SharedDatabaseDefault: DataModels.IndexingPolicy = {
|
||||
|
@ -93,6 +97,7 @@ export interface AddCollectionPanelState {
|
|||
showErrorDetails: boolean;
|
||||
isExecuting: boolean;
|
||||
isThroughputCapExceeded: boolean;
|
||||
teachingBubbleStep: number;
|
||||
}
|
||||
|
||||
export class AddCollectionPanel extends React.Component<AddCollectionPanelProps, AddCollectionPanelState> {
|
||||
|
@ -107,11 +112,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||
|
||||
this.state = {
|
||||
createNewDatabase: userContext.apiType !== "Tables" && !this.props.databaseId,
|
||||
newDatabaseId: "",
|
||||
newDatabaseId: props.isQuickstart ? "SampleDB" : "",
|
||||
isSharedThroughputChecked: this.getSharedThroughputDefault(),
|
||||
selectedDatabaseId:
|
||||
userContext.apiType === "Tables" ? CollectionCreation.TablesAPIDefaultDatabase : this.props.databaseId,
|
||||
collectionId: "",
|
||||
collectionId: props.isQuickstart ? `Sample${getCollectionName()}` : "",
|
||||
enableIndexing: true,
|
||||
isSharded: userContext.apiType !== "Tables",
|
||||
partitionKey: this.getPartitionKey(),
|
||||
|
@ -124,9 +129,16 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||
showErrorDetails: false,
|
||||
isExecuting: false,
|
||||
isThroughputCapExceeded: false,
|
||||
teachingBubbleStep: 0,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
if (this.state.teachingBubbleStep === 0 && this.props.showTeachingBubble) {
|
||||
this.setState({ teachingBubbleStep: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const isFirstResourceCreated = useDatabases.getState().isFirstResourceCreated();
|
||||
|
||||
|
@ -150,6 +162,70 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||
/>
|
||||
)}
|
||||
|
||||
{this.state.teachingBubbleStep === 1 && (
|
||||
<TeachingBubble
|
||||
headline="Create sample database"
|
||||
target={"#newDatabaseId"}
|
||||
calloutProps={{ gapSpace: 16 }}
|
||||
primaryButtonProps={{ text: "Next", onClick: () => this.setState({ teachingBubbleStep: 2 }) }}
|
||||
secondaryButtonProps={{ text: "Cancel", onClick: () => this.setState({ teachingBubbleStep: 0 }) }}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
footerContent="Step 1 of 4"
|
||||
>
|
||||
Database is the parent of a container, create a new database / use an existing one
|
||||
</TeachingBubble>
|
||||
)}
|
||||
|
||||
{this.state.teachingBubbleStep === 2 && (
|
||||
<TeachingBubble
|
||||
headline="Setting throughput"
|
||||
target={"#autoscaleRUValueField"}
|
||||
calloutProps={{ gapSpace: 16 }}
|
||||
primaryButtonProps={{ text: "Next", onClick: () => this.setState({ teachingBubbleStep: 3 }) }}
|
||||
secondaryButtonProps={{ text: "Previous", onClick: () => this.setState({ teachingBubbleStep: 1 }) }}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
footerContent="Step 2 of 4"
|
||||
>
|
||||
Cosmos DB recommends sharing throughput across database. Autoscale will give you a flexible amount of
|
||||
throughput based on the max RU/s set
|
||||
</TeachingBubble>
|
||||
)}
|
||||
|
||||
{this.state.teachingBubbleStep === 3 && (
|
||||
<TeachingBubble
|
||||
headline="Naming container"
|
||||
target={"#collectionId"}
|
||||
calloutProps={{ gapSpace: 16 }}
|
||||
primaryButtonProps={{ text: "Next", onClick: () => this.setState({ teachingBubbleStep: 4 }) }}
|
||||
secondaryButtonProps={{ text: "Previous", onClick: () => this.setState({ teachingBubbleStep: 2 }) }}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
footerContent="Step 3 of 4"
|
||||
>
|
||||
Name your container
|
||||
</TeachingBubble>
|
||||
)}
|
||||
|
||||
{this.state.teachingBubbleStep === 4 && (
|
||||
<TeachingBubble
|
||||
headline="Setting partition key"
|
||||
target={"#addCollection-partitionKeyValue"}
|
||||
calloutProps={{ gapSpace: 16 }}
|
||||
primaryButtonProps={{
|
||||
text: "Create container",
|
||||
onClick: () => {
|
||||
this.setState({ teachingBubbleStep: 0 });
|
||||
this.submit();
|
||||
},
|
||||
}}
|
||||
secondaryButtonProps={{ text: "Previous", onClick: () => this.setState({ teachingBubbleStep: 2 }) }}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
footerContent="Step 4 of 4"
|
||||
>
|
||||
Last step - you will need to define a partition key for your collection. /address was chosen for this
|
||||
particular example. A good partition key should have a wide range of possible value
|
||||
</TeachingBubble>
|
||||
)}
|
||||
|
||||
<div className="panelMainContent">
|
||||
<Stack hidden={userContext.apiType === "Tables"}>
|
||||
<Stack horizontal>
|
||||
|
@ -832,6 +908,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||
if (userContext.features.partitionKeyDefault2) {
|
||||
return userContext.apiType === "SQL" ? "/pk" : "pk";
|
||||
}
|
||||
if (this.props.isQuickstart) {
|
||||
return userContext.apiType === "SQL" ? "/address" : "address";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -899,8 +978,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||
}
|
||||
|
||||
private isSynapseLinkEnabled(): boolean {
|
||||
const { properties } = userContext.databaseAccount;
|
||||
if (!userContext.databaseAccount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { properties } = userContext.databaseAccount;
|
||||
if (!properties) {
|
||||
return false;
|
||||
}
|
||||
|
@ -996,8 +1078,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||
document.getElementById("collapsibleSectionContent")?.scrollIntoView();
|
||||
}
|
||||
|
||||
private async submit(event: React.FormEvent<HTMLFormElement>): Promise<void> {
|
||||
event.preventDefault();
|
||||
private async submit(event?: React.FormEvent<HTMLFormElement>): Promise<void> {
|
||||
event?.preventDefault();
|
||||
|
||||
if (!this.validateInputs()) {
|
||||
return;
|
||||
|
@ -1090,8 +1172,17 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||
|
||||
try {
|
||||
await createCollection(createCollectionParams);
|
||||
await this.props.explorer.refreshAllDatabases();
|
||||
if (this.props.isQuickstart) {
|
||||
const database = useDatabases.getState().findDatabaseWithId("SampleDB");
|
||||
if (database) {
|
||||
await database.loadCollections();
|
||||
const collection = database.findCollectionWithId("SampleContainer");
|
||||
const sampleGenerator = await ContainerSampleGenerator.createSampleGeneratorAsync(this.props.explorer);
|
||||
sampleGenerator.populateContainerAsync(collection);
|
||||
}
|
||||
}
|
||||
this.setState({ isExecuting: false });
|
||||
this.props.explorer.refreshAllDatabases();
|
||||
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
|
||||
useSidePanel.getState().closeSidePanel();
|
||||
} catch (error) {
|
||||
|
|
|
@ -0,0 +1,427 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AddCollectionPanel should render Default properly 1`] = `
|
||||
<form
|
||||
className="panelFormWrapper"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div
|
||||
className="panelMainContent"
|
||||
>
|
||||
<Stack
|
||||
hidden={false}
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<span
|
||||
className="mandatoryStar"
|
||||
>
|
||||
*
|
||||
</span>
|
||||
<Text
|
||||
className="panelTextBold"
|
||||
variant="small"
|
||||
>
|
||||
Database
|
||||
id
|
||||
</Text>
|
||||
<StyledTooltipHostBase
|
||||
content="A database is analogous to a namespace. It is the unit of management for a set of containers."
|
||||
directionalHint={4}
|
||||
>
|
||||
<Icon
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
</Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<input
|
||||
aria-checked={true}
|
||||
aria-label="Create new database"
|
||||
checked={true}
|
||||
className="panelRadioBtn"
|
||||
id="databaseCreateNew"
|
||||
name="databaseType"
|
||||
onChange={[Function]}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
type="radio"
|
||||
/>
|
||||
<span
|
||||
className="panelRadioBtnLabel"
|
||||
>
|
||||
Create new
|
||||
</span>
|
||||
<input
|
||||
aria-checked={false}
|
||||
aria-label="Use existing database"
|
||||
checked={false}
|
||||
className="panelRadioBtn"
|
||||
name="databaseType"
|
||||
onChange={[Function]}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
type="radio"
|
||||
/>
|
||||
<span
|
||||
className="panelRadioBtnLabel"
|
||||
>
|
||||
Use existing
|
||||
</span>
|
||||
</Stack>
|
||||
<Stack
|
||||
className="panelGroupSpacing"
|
||||
>
|
||||
<input
|
||||
aria-label="New database id"
|
||||
aria-required={true}
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
className="panelTextField"
|
||||
id="newDatabaseId"
|
||||
name="newDatabaseId"
|
||||
onChange={[Function]}
|
||||
pattern="[^/?#\\\\\\\\]*[^/?# \\\\\\\\]"
|
||||
placeholder="Type a new database id"
|
||||
required={true}
|
||||
size={40}
|
||||
tabIndex={0}
|
||||
title="May not end with space nor contain characters '\\\\' '/' '#' '?'"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<StyledCheckboxBase
|
||||
checked={true}
|
||||
label="Share throughput across containers"
|
||||
onChange={[Function]}
|
||||
styles={
|
||||
Object {
|
||||
"checkbox": Object {
|
||||
"height": 12,
|
||||
"width": 12,
|
||||
},
|
||||
"label": Object {
|
||||
"alignItems": "center",
|
||||
"padding": 0,
|
||||
},
|
||||
"text": Object {
|
||||
"fontSize": 12,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledTooltipHostBase
|
||||
content="Throughput configured at the database level will be shared across all containers within the database."
|
||||
directionalHint={4}
|
||||
>
|
||||
<Icon
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
</Stack>
|
||||
<ThroughputInput
|
||||
isDatabase={true}
|
||||
isSharded={true}
|
||||
onCostAcknowledgeChange={[Function]}
|
||||
setIsAutoscale={[Function]}
|
||||
setIsThroughputCapExceeded={[Function]}
|
||||
setThroughputValue={[Function]}
|
||||
/>
|
||||
</Stack>
|
||||
<Separator
|
||||
className="panelSeparator"
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<span
|
||||
className="mandatoryStar"
|
||||
>
|
||||
*
|
||||
</span>
|
||||
<Text
|
||||
className="panelTextBold"
|
||||
variant="small"
|
||||
>
|
||||
Container id
|
||||
</Text>
|
||||
<StyledTooltipHostBase
|
||||
content="Unique identifier for the container and used for id-based routing through REST and all SDKs."
|
||||
directionalHint={4}
|
||||
>
|
||||
<Icon
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
</Stack>
|
||||
<input
|
||||
aria-label="Container id"
|
||||
aria-required={true}
|
||||
autoComplete="off"
|
||||
className="panelTextField"
|
||||
id="collectionId"
|
||||
name="collectionId"
|
||||
onChange={[Function]}
|
||||
pattern="[^/?#\\\\\\\\]*[^/?# \\\\\\\\]"
|
||||
placeholder="e.g., Container1"
|
||||
required={true}
|
||||
size={40}
|
||||
title="May not end with space nor contain characters '\\\\' '/' '#' '?'"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<span
|
||||
className="mandatoryStar"
|
||||
>
|
||||
*
|
||||
</span>
|
||||
<Text
|
||||
className="panelTextBold"
|
||||
variant="small"
|
||||
>
|
||||
Partition key
|
||||
</Text>
|
||||
<StyledTooltipHostBase
|
||||
content="The partition key is used to automatically distribute data across partitions for scalability. Choose a property in your JSON document that has a wide range of values and evenly distributes request volume. For small read-heavy workloads or write-heavy workloads of any size, id is often a good choice."
|
||||
directionalHint={4}
|
||||
>
|
||||
<Icon
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
</Stack>
|
||||
<Text
|
||||
aria-label="pkDescription"
|
||||
variant="small"
|
||||
/>
|
||||
<input
|
||||
aria-label="Partition key"
|
||||
aria-required={true}
|
||||
className="panelTextField"
|
||||
id="addCollection-partitionKeyValue"
|
||||
onChange={[Function]}
|
||||
pattern=".*"
|
||||
placeholder="e.g., /address/zipCode"
|
||||
required={true}
|
||||
size={40}
|
||||
title=""
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<Text
|
||||
className="panelTextBold"
|
||||
variant="small"
|
||||
>
|
||||
Unique keys
|
||||
</Text>
|
||||
<StyledTooltipHostBase
|
||||
content="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."
|
||||
directionalHint={4}
|
||||
>
|
||||
<Icon
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
</Stack>
|
||||
<CustomizedActionButton
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Add",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
styles={
|
||||
Object {
|
||||
"label": Object {
|
||||
"fontSize": 12,
|
||||
},
|
||||
"root": Object {
|
||||
"padding": 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
Add unique key
|
||||
</CustomizedActionButton>
|
||||
</Stack>
|
||||
<Stack
|
||||
className="panelGroupSpacing"
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<Text
|
||||
className="panelTextBold"
|
||||
variant="small"
|
||||
>
|
||||
Analytical store
|
||||
</Text>
|
||||
<StyledTooltipHostBase
|
||||
content={
|
||||
<Text
|
||||
variant="small"
|
||||
>
|
||||
Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads.
|
||||
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/analytical-store-overview"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
}
|
||||
directionalHint={4}
|
||||
>
|
||||
<Icon
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
</Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<input
|
||||
aria-checked={false}
|
||||
aria-label="Enable analytical store"
|
||||
checked={false}
|
||||
className="panelRadioBtn"
|
||||
disabled={true}
|
||||
id="enableAnalyticalStoreBtn"
|
||||
name="analyticalStore"
|
||||
onChange={[Function]}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
type="radio"
|
||||
/>
|
||||
<span
|
||||
className="panelRadioBtnLabel"
|
||||
>
|
||||
On
|
||||
</span>
|
||||
<input
|
||||
aria-checked={true}
|
||||
aria-label="Disable analytical store"
|
||||
checked={true}
|
||||
className="panelRadioBtn"
|
||||
disabled={true}
|
||||
id="disableAnalyticalStoreBtn"
|
||||
name="analyticalStore"
|
||||
onChange={[Function]}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
type="radio"
|
||||
/>
|
||||
<span
|
||||
className="panelRadioBtnLabel"
|
||||
>
|
||||
Off
|
||||
</span>
|
||||
</Stack>
|
||||
<Stack
|
||||
className="panelGroupSpacing"
|
||||
>
|
||||
<Text
|
||||
variant="small"
|
||||
>
|
||||
Azure Synapse Link is required for creating an analytical store
|
||||
|
||||
container
|
||||
. Enable Synapse Link for this Cosmos DB account.
|
||||
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cosmosdb-synapselink"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
<CustomizedDefaultButton
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": 27,
|
||||
"width": 80,
|
||||
}
|
||||
}
|
||||
styles={
|
||||
Object {
|
||||
"label": Object {
|
||||
"fontSize": 12,
|
||||
},
|
||||
}
|
||||
}
|
||||
text="Enable"
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<CollapsibleSectionComponent
|
||||
isExpandedByDefault={false}
|
||||
onExpand={[Function]}
|
||||
title="Advanced"
|
||||
>
|
||||
<Stack
|
||||
className="panelGroupSpacing"
|
||||
id="collapsibleSectionContent"
|
||||
>
|
||||
<StyledCheckboxBase
|
||||
checked={false}
|
||||
label="My partition key is larger than 101 bytes"
|
||||
onChange={[Function]}
|
||||
styles={
|
||||
Object {
|
||||
"checkbox": Object {
|
||||
"height": 12,
|
||||
"width": 12,
|
||||
},
|
||||
"label": Object {
|
||||
"alignItems": "center",
|
||||
"padding": 0,
|
||||
},
|
||||
"text": Object {
|
||||
"fontSize": 12,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
</CollapsibleSectionComponent>
|
||||
</div>
|
||||
<PanelFooterComponent
|
||||
buttonLabel="OK"
|
||||
isButtonDisabled={false}
|
||||
/>
|
||||
</form>
|
||||
`;
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Accordion top class
|
||||
*/
|
||||
import { Image, Link, Stack, Text } from "@fluentui/react";
|
||||
import { Coachmark, DirectionalHint, Image, Link, Stack, TeachingBubbleContent, Text } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import AddDatabaseIcon from "../../../images/AddDatabase.svg";
|
||||
import NewQueryIcon from "../../../images/AddSqlQuery_16x16.svg";
|
||||
|
@ -39,6 +39,7 @@ import { useSelectedNode } from "../useSelectedNode";
|
|||
export interface SplashScreenItem {
|
||||
iconSrc: string;
|
||||
title: string;
|
||||
id?: string;
|
||||
info?: string;
|
||||
description: string;
|
||||
onClick: () => void;
|
||||
|
@ -48,7 +49,11 @@ export interface SplashScreenProps {
|
|||
explorer: Explorer;
|
||||
}
|
||||
|
||||
export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
export interface SplashScreenState {
|
||||
showCoachmark: boolean;
|
||||
}
|
||||
|
||||
export class SplashScreen extends React.Component<SplashScreenProps, SplashScreenState> {
|
||||
private static readonly seeMoreItemTitle: string = "See more Cosmos DB documentation";
|
||||
private static readonly seeMoreItemUrl: string = "https://aka.ms/cosmosdbdocument";
|
||||
private static readonly dataModelingUrl = "https://docs.microsoft.com/azure/cosmos-db/modeling-data";
|
||||
|
@ -62,15 +67,19 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
super(props);
|
||||
this.container = props.explorer;
|
||||
this.subscriptions = [];
|
||||
|
||||
this.state = {
|
||||
showCoachmark: userContext.features.enableNewQuickstart,
|
||||
};
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
while (this.subscriptions.length) {
|
||||
this.subscriptions.pop().dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
this.subscriptions.push(
|
||||
{
|
||||
dispose: useNotebook.subscribe(
|
||||
|
@ -116,13 +125,41 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
</div>
|
||||
<div className="legendContainer">
|
||||
<div className="legend">{item.title}</div>
|
||||
<div className={userContext.features.enableNewQuickstart ? "newDescription" : "description"}>
|
||||
<div
|
||||
id={item.id}
|
||||
className={userContext.features.enableNewQuickstart ? "newDescription" : "description"}
|
||||
>
|
||||
{item.description}
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
))}
|
||||
</div>
|
||||
{this.state.showCoachmark && (
|
||||
<Coachmark
|
||||
target="#quickstartDescription"
|
||||
positioningContainerProps={{ directionalHint: DirectionalHint.rightTopEdge }}
|
||||
persistentBeak
|
||||
>
|
||||
<TeachingBubbleContent
|
||||
headline={`Start with sample ${getCollectionName().toLocaleLowerCase()}`}
|
||||
hasCloseButton
|
||||
closeButtonAriaLabel="Close"
|
||||
primaryButtonProps={{
|
||||
text: "Get started",
|
||||
onClick: () => {
|
||||
this.setState({ showCoachmark: false });
|
||||
this.container.onNewCollectionClicked({ isQuickstart: true, showTeachingBubble: true });
|
||||
},
|
||||
}}
|
||||
secondaryButtonProps={{ text: "Cancel", onClick: () => this.setState({ showCoachmark: false }) }}
|
||||
onDismiss={() => this.setState({ showCoachmark: false })}
|
||||
>
|
||||
You will be guided to create a sample container with sample data, then we will give you a tour of
|
||||
data explorer You can also cancel launching this tour and explore yourself
|
||||
</TeachingBubbleContent>
|
||||
</Coachmark>
|
||||
)}
|
||||
<div className="moreStuffContainer">
|
||||
<div className="moreStuffColumn commonTasks">
|
||||
<div className="title">
|
||||
|
@ -165,11 +202,11 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
|
||||
if (userContext.features.enableNewQuickstart) {
|
||||
const launchQuickstartBtn = {
|
||||
id: "quickstartDescription",
|
||||
iconSrc: QuickStartIcon,
|
||||
title: "Launch quick start",
|
||||
description: "Launch a quick start tutorial to get started with sample data",
|
||||
// TODO: replace onClick function
|
||||
onClick: () => 1,
|
||||
onClick: () => this.container.onNewCollectionClicked({ isQuickstart: true }),
|
||||
};
|
||||
|
||||
const newContainerBtn = {
|
||||
|
|
|
@ -11,7 +11,7 @@ const fileToUpload = `GettingStarted-ignore${Math.floor(Math.random() * 100000)}
|
|||
fs.copyFileSync(path.join(__dirname, filename), path.join(__dirname, fileToUpload));
|
||||
|
||||
test("Notebooks", async () => {
|
||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-sql-runner");
|
||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-sql-runner-west-us");
|
||||
const explorer = await waitForExplorer();
|
||||
// Upload and Delete Notebook
|
||||
await explorer.click('[data-test="My Notebooks"] [aria-label="More"]');
|
||||
|
|
|
@ -9,7 +9,7 @@ test("SQL CRUD", async () => {
|
|||
const containerId = generateUniqueName("container");
|
||||
page.setDefaultTimeout(50000);
|
||||
|
||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-sql-runner");
|
||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-sql-runner-west-us");
|
||||
const explorer = await waitForExplorer();
|
||||
await explorer.click('[data-test="New Container"]');
|
||||
await explorer.fill('[aria-label="New database id"]', databaseId);
|
||||
|
|
|
@ -15,8 +15,8 @@ const resourceGroupName = "runners";
|
|||
test("Resource token", async () => {
|
||||
const credentials = await msRestNodeAuth.loginWithServicePrincipalSecret(clientId, secret, tenantId);
|
||||
const armClient = new CosmosDBManagementClient(credentials, subscriptionId);
|
||||
const account = await armClient.databaseAccounts.get(resourceGroupName, "portal-sql-runner");
|
||||
const keys = await armClient.databaseAccounts.listKeys(resourceGroupName, "portal-sql-runner");
|
||||
const account = await armClient.databaseAccounts.get(resourceGroupName, "portal-sql-runner-west-us");
|
||||
const keys = await armClient.databaseAccounts.listKeys(resourceGroupName, "portal-sql-runner-west-us");
|
||||
const dbId = generateUniqueName("db");
|
||||
const collectionId = generateUniqueName("col");
|
||||
const client = new CosmosClient({
|
||||
|
|
|
@ -8,7 +8,7 @@ import { get, listKeys } from "../../src/Utils/arm/generatedClients/cosmos/datab
|
|||
const resourceGroup = process.env.RESOURCE_GROUP || "";
|
||||
const subscriptionId = process.env.SUBSCRIPTION_ID || "";
|
||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||
const accountName = urlSearchParams.get("accountName") || "portal-sql-runner";
|
||||
const accountName = urlSearchParams.get("accountName") || "portal-sql-runner-west-us";
|
||||
const selfServeType = urlSearchParams.get("selfServeType") || "example";
|
||||
const iframeSrc = urlSearchParams.get("iframeSrc") || "explorer.html?platform=Portal&disablePortalInitCache";
|
||||
|
||||
|
|
Loading…
Reference in New Issue