Add condition for showing quick start carousel (#1278)
* Add condition for showing quick start carousel * Show coach mark when carousel is closed * Add condition for showing quick start carousel and other UI changes * Fix compile error * Fix issue with coach mark * Fix test * Add new sample data, fix link url, fix e2e tests * Fix e2e tests
This commit is contained in:
parent
d13b7a50ad
commit
46ca952955
|
@ -1,26 +1,25 @@
|
||||||
{
|
{
|
||||||
"databaseId": "SampleDB",
|
|
||||||
"offerThroughput": 400,
|
|
||||||
"databaseLevelThroughput": false,
|
|
||||||
"collectionId": "Persons",
|
|
||||||
"createNewDatabase": true,
|
|
||||||
"partitionKey": { "kind": "Hash", "paths": ["/firstname"], "version": 1 },
|
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{ "address": "2007, NE 37TH PL" },
|
||||||
"firstname": "Eva",
|
{ "address": "11635, SE MAY CREEK PARK DR" },
|
||||||
"age": 44
|
{ "address": "8923, 133RD AVE SE" },
|
||||||
},
|
{ "address": "1124, N 33RD ST" },
|
||||||
{
|
{ "address": "4288, 131ST PL SE" },
|
||||||
"firstname": "Véronique",
|
{ "address": "10900, SE 66TH ST" },
|
||||||
"age": 50
|
{ "address": "6260, 139TH AVE NE" },
|
||||||
},
|
{ "address": "13427, NE SPRING BLVD" },
|
||||||
{
|
{ "address": "13812, NE SPRING BLVD" },
|
||||||
"firstname": "亜妃子",
|
{ "address": "5029, 159TH PL SE" },
|
||||||
"age": 5
|
{ "address": "8604, 117TH AVE SE" },
|
||||||
},
|
{ "address": "1561, 139TH LN NE" },
|
||||||
{
|
{ "address": "1575, 139TH CT NE" },
|
||||||
"firstname": "John",
|
{ "address": "13901, NE 15TH CT" },
|
||||||
"age": 23
|
{ "address": "16365, NE 12TH PL" },
|
||||||
}
|
{ "address": "12226, NE 37TH ST" },
|
||||||
|
{ "address": "4021, 129TH CT SE" },
|
||||||
|
{ "address": "1455, 159TH PL NE" },
|
||||||
|
{ "address": "15825, NE 14TH RD" },
|
||||||
|
{ "address": "1418, 157TH CT NE" },
|
||||||
|
{ "address": "889, 131ST PL NE" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,7 @@ export interface Database extends TreeNode {
|
||||||
offer: ko.Observable<DataModels.Offer>;
|
offer: ko.Observable<DataModels.Offer>;
|
||||||
isDatabaseExpanded: ko.Observable<boolean>;
|
isDatabaseExpanded: ko.Observable<boolean>;
|
||||||
isDatabaseShared: ko.Computed<boolean>;
|
isDatabaseShared: ko.Computed<boolean>;
|
||||||
|
isSampleDB?: boolean;
|
||||||
|
|
||||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||||
|
|
||||||
|
@ -112,6 +113,7 @@ export interface CollectionBase extends TreeNode {
|
||||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||||
children: ko.ObservableArray<TreeNode>;
|
children: ko.ObservableArray<TreeNode>;
|
||||||
isCollectionExpanded: ko.Observable<boolean>;
|
isCollectionExpanded: ko.Observable<boolean>;
|
||||||
|
isSampleCollection?: boolean;
|
||||||
|
|
||||||
onDocumentDBDocumentsClick(): void;
|
onDocumentDBDocumentsClick(): void;
|
||||||
onNewQueryClick(source: any, event?: MouseEvent, queryText?: string): void;
|
onNewQueryClick(source: any, event?: MouseEvent, queryText?: string): void;
|
||||||
|
|
|
@ -113,7 +113,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
createNewDatabase: userContext.apiType !== "Tables" && !this.props.databaseId,
|
createNewDatabase: userContext.apiType !== "Tables" && !this.props.databaseId,
|
||||||
newDatabaseId: props.isQuickstart ? "SampleDB" : "",
|
newDatabaseId: props.isQuickstart ? this.getSampleDBName() : "",
|
||||||
isSharedThroughputChecked: this.getSharedThroughputDefault(),
|
isSharedThroughputChecked: this.getSharedThroughputDefault(),
|
||||||
selectedDatabaseId:
|
selectedDatabaseId:
|
||||||
userContext.apiType === "Tables" ? CollectionCreation.TablesAPIDefaultDatabase : this.props.databaseId,
|
userContext.apiType === "Tables" ? CollectionCreation.TablesAPIDefaultDatabase : this.props.databaseId,
|
||||||
|
@ -173,7 +173,19 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||||
footerContent="Step 1 of 4"
|
footerContent="Step 1 of 4"
|
||||||
>
|
>
|
||||||
Database is the parent of a container, create a new database / use an existing one
|
<Stack>
|
||||||
|
<Text style={{ color: "white" }}>
|
||||||
|
Database is the parent of a container. You can create a new database or use an existing one. In this
|
||||||
|
tutorial we are creating a new database named SampleDB.
|
||||||
|
</Text>
|
||||||
|
<Link
|
||||||
|
style={{ color: "white", fontWeight: 600 }}
|
||||||
|
target="_blank"
|
||||||
|
href="https://aka.ms/TeachingbubbleResources"
|
||||||
|
>
|
||||||
|
Learn more about resources.
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -187,8 +199,15 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||||
footerContent="Step 2 of 4"
|
footerContent="Step 2 of 4"
|
||||||
>
|
>
|
||||||
Cosmos DB recommends sharing throughput across database. Autoscale will give you a flexible amount of
|
<Stack>
|
||||||
throughput based on the max RU/s set
|
<Text style={{ color: "white" }}>
|
||||||
|
Cosmos DB recommends sharing throughput across database. Autoscale will give you a flexible amount of
|
||||||
|
throughput based on the max RU/s set (Request Units).
|
||||||
|
</Text>
|
||||||
|
<Link style={{ color: "white", fontWeight: 600 }} target="_blank" href="https://aka.ms/teachingbubbleRU">
|
||||||
|
Learn more about RU/s.
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -773,18 +792,23 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||||
headline="Creating sample container"
|
headline="Creating sample container"
|
||||||
target={"#loadingScreen"}
|
target={"#loadingScreen"}
|
||||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||||
footerContent={
|
styles={{ footer: { width: "100%" } }}
|
||||||
<ProgressIndicator
|
|
||||||
styles={{ itemName: { color: "rgb(255, 255, 255)" } }}
|
|
||||||
label="Adding sample data set"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
A sample container is now being created and we are adding sample data for you. It should take about 1
|
A sample container is now being created and we are adding sample data for you. It should take about 1
|
||||||
minute.
|
minute.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Once the sample container is created, review your sample dataset and follow next steps
|
Once the sample container is created, review your sample dataset and follow next steps
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<ProgressIndicator
|
||||||
|
styles={{
|
||||||
|
itemName: { color: "white" },
|
||||||
|
progressTrack: { backgroundColor: "#A6A6A6" },
|
||||||
|
progressBar: { background: "white" },
|
||||||
|
}}
|
||||||
|
label="Adding sample data set"
|
||||||
|
/>
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1102,6 +1126,23 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||||
document.getElementById("collapsibleSectionContent")?.scrollIntoView();
|
document.getElementById("collapsibleSectionContent")?.scrollIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getSampleDBName(): string {
|
||||||
|
const existingSampleDBs = useDatabases
|
||||||
|
.getState()
|
||||||
|
.databases?.filter((database) => database.id().startsWith("SampleDB"));
|
||||||
|
const existingSampleDBNames = existingSampleDBs?.map((database) => database.id());
|
||||||
|
if (!existingSampleDBNames || existingSampleDBNames.length === 0) {
|
||||||
|
return "SampleDB";
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 1;
|
||||||
|
while (existingSampleDBNames.indexOf(`SampleDB${i}`) !== -1) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `SampleDB${i}`;
|
||||||
|
}
|
||||||
|
|
||||||
private async submit(event?: React.FormEvent<HTMLFormElement>): Promise<void> {
|
private async submit(event?: React.FormEvent<HTMLFormElement>): Promise<void> {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
|
|
||||||
|
@ -1198,11 +1239,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||||
await createCollection(createCollectionParams);
|
await createCollection(createCollectionParams);
|
||||||
await this.props.explorer.refreshAllDatabases();
|
await this.props.explorer.refreshAllDatabases();
|
||||||
if (this.props.isQuickstart) {
|
if (this.props.isQuickstart) {
|
||||||
const database = useDatabases.getState().findDatabaseWithId("SampleDB");
|
const database = useDatabases.getState().findDatabaseWithId(databaseId);
|
||||||
if (database) {
|
if (database) {
|
||||||
|
database.isSampleDB = true;
|
||||||
// populate sample container with sample data
|
// populate sample container with sample data
|
||||||
await database.loadCollections();
|
await database.loadCollections();
|
||||||
const collection = database.findCollectionWithId("SampleContainer");
|
const collection = database.findCollectionWithId(collectionId);
|
||||||
|
collection.isSampleCollection = true;
|
||||||
|
useTeachingBubble.getState().setSampleCollection(collection);
|
||||||
const sampleGenerator = await ContainerSampleGenerator.createSampleGeneratorAsync(this.props.explorer);
|
const sampleGenerator = await ContainerSampleGenerator.createSampleGeneratorAsync(this.props.explorer);
|
||||||
await sampleGenerator.populateContainerAsync(collection);
|
await sampleGenerator.populateContainerAsync(collection);
|
||||||
// auto-expand sample database + container and show teaching bubble
|
// auto-expand sample database + container and show teaching bubble
|
||||||
|
|
|
@ -9,31 +9,6 @@ const createExplorer = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("SplashScreen", () => {
|
describe("SplashScreen", () => {
|
||||||
it("allows sample collection creation for supported api's", () => {
|
|
||||||
const explorer = createExplorer();
|
|
||||||
const dataSampleUtil = new DataSamplesUtil(explorer);
|
|
||||||
const createStub = jest
|
|
||||||
.spyOn(dataSampleUtil, "createGeneratorAsync")
|
|
||||||
.mockImplementation(() => Promise.reject(undefined));
|
|
||||||
|
|
||||||
// Sample is supported
|
|
||||||
jest.spyOn(dataSampleUtil, "isSampleContainerCreationSupported").mockImplementation(() => true);
|
|
||||||
|
|
||||||
const splashScreen = new SplashScreen({ explorer });
|
|
||||||
jest.spyOn(splashScreen, "createDataSampleUtil").mockImplementation(() => dataSampleUtil);
|
|
||||||
const mainButtons = splashScreen.createMainItems();
|
|
||||||
|
|
||||||
// Press all buttons and make sure create gets called
|
|
||||||
mainButtons.forEach((button) => {
|
|
||||||
try {
|
|
||||||
button.onClick();
|
|
||||||
} catch (e) {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(createStub).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not allow sample collection creation for non-supported api's", () => {
|
it("does not allow sample collection creation for non-supported api's", () => {
|
||||||
const explorerStub = createExplorer();
|
const explorerStub = createExplorer();
|
||||||
const dataSampleUtil = new DataSamplesUtil(explorerStub);
|
const dataSampleUtil = new DataSamplesUtil(explorerStub);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Accordion top class
|
* Accordion top class
|
||||||
*/
|
*/
|
||||||
import { Coachmark, DirectionalHint, Image, Link, Stack, TeachingBubbleContent, Text } from "@fluentui/react";
|
import { Coachmark, DirectionalHint, Image, Link, Stack, TeachingBubbleContent, Text } from "@fluentui/react";
|
||||||
|
import { useCarousel } from "hooks/useCarousel";
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import AddDatabaseIcon from "../../../images/AddDatabase.svg";
|
import AddDatabaseIcon from "../../../images/AddDatabase.svg";
|
||||||
|
@ -10,9 +11,6 @@ import NewStoredProcedureIcon from "../../../images/AddStoredProcedure.svg";
|
||||||
import OpenQueryIcon from "../../../images/BrowseQuery.svg";
|
import OpenQueryIcon from "../../../images/BrowseQuery.svg";
|
||||||
import ConnectIcon from "../../../images/Connect_color.svg";
|
import ConnectIcon from "../../../images/Connect_color.svg";
|
||||||
import ContainersIcon from "../../../images/Containers.svg";
|
import ContainersIcon from "../../../images/Containers.svg";
|
||||||
import NewContainerIcon from "../../../images/Hero-new-container.svg";
|
|
||||||
import NewNotebookIcon from "../../../images/Hero-new-notebook.svg";
|
|
||||||
import SampleIcon from "../../../images/Hero-sample.svg";
|
|
||||||
import LinkIcon from "../../../images/Link_blue.svg";
|
import LinkIcon from "../../../images/Link_blue.svg";
|
||||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||||
import NotebookColorIcon from "../../../images/Notebooks.svg";
|
import NotebookColorIcon from "../../../images/Notebooks.svg";
|
||||||
|
@ -49,11 +47,7 @@ export interface SplashScreenProps {
|
||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SplashScreenState {
|
export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||||
showCoachmark: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SplashScreen extends React.Component<SplashScreenProps, SplashScreenState> {
|
|
||||||
private static readonly seeMoreItemTitle: string = "See more Cosmos DB documentation";
|
private static readonly seeMoreItemTitle: string = "See more Cosmos DB documentation";
|
||||||
private static readonly seeMoreItemUrl: string = "https://aka.ms/cosmosdbdocument";
|
private static readonly seeMoreItemUrl: string = "https://aka.ms/cosmosdbdocument";
|
||||||
private static readonly dataModelingUrl = "https://docs.microsoft.com/azure/cosmos-db/modeling-data";
|
private static readonly dataModelingUrl = "https://docs.microsoft.com/azure/cosmos-db/modeling-data";
|
||||||
|
@ -67,10 +61,6 @@ export class SplashScreen extends React.Component<SplashScreenProps, SplashScree
|
||||||
super(props);
|
super(props);
|
||||||
this.container = props.explorer;
|
this.container = props.explorer;
|
||||||
this.subscriptions = [];
|
this.subscriptions = [];
|
||||||
|
|
||||||
this.state = {
|
|
||||||
showCoachmark: userContext.features.enableNewQuickstart,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
|
@ -87,7 +77,13 @@ export class SplashScreen extends React.Component<SplashScreenProps, SplashScree
|
||||||
(state) => state.isNotebookEnabled
|
(state) => state.isNotebookEnabled
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{ dispose: useSelectedNode.subscribe(() => this.setState({})) }
|
{ dispose: useSelectedNode.subscribe(() => this.setState({})) },
|
||||||
|
{
|
||||||
|
dispose: useCarousel.subscribe(
|
||||||
|
() => this.setState({}),
|
||||||
|
(state) => state.showCoachMark
|
||||||
|
),
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,17 +125,14 @@ export class SplashScreen extends React.Component<SplashScreenProps, SplashScree
|
||||||
{item.showLinkIcon && <Image style={{ marginLeft: 8, width: 16 }} src={LinkIcon} />}
|
{item.showLinkIcon && <Image style={{ marginLeft: 8, width: 16 }} src={LinkIcon} />}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<div
|
<div id={item.id} className="newDescription">
|
||||||
id={item.id}
|
|
||||||
className={userContext.features.enableNewQuickstart ? "newDescription" : "description"}
|
|
||||||
>
|
|
||||||
{item.description}
|
{item.description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{this.state.showCoachmark && (
|
{useCarousel.getState().showCoachMark && (
|
||||||
<Coachmark
|
<Coachmark
|
||||||
target="#quickstartDescription"
|
target="#quickstartDescription"
|
||||||
positioningContainerProps={{ directionalHint: DirectionalHint.rightTopEdge }}
|
positioningContainerProps={{ directionalHint: DirectionalHint.rightTopEdge }}
|
||||||
|
@ -152,34 +145,33 @@ export class SplashScreen extends React.Component<SplashScreenProps, SplashScree
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
text: "Get started",
|
text: "Get started",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.setState({ showCoachmark: false });
|
useCarousel.getState().setShowCoachMark(false);
|
||||||
this.container.onNewCollectionClicked({ isQuickstart: true });
|
this.container.onNewCollectionClicked({ isQuickstart: true });
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
secondaryButtonProps={{ text: "Cancel", onClick: () => this.setState({ showCoachmark: false }) }}
|
secondaryButtonProps={{
|
||||||
onDismiss={() => this.setState({ showCoachmark: false })}
|
text: "Cancel",
|
||||||
|
onClick: () => useCarousel.getState().setShowCoachMark(false),
|
||||||
|
}}
|
||||||
|
onDismiss={() => useCarousel.getState().setShowCoachMark(false)}
|
||||||
>
|
>
|
||||||
You will be guided to create a sample container with sample data, then we will give you a tour of
|
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
|
data explorer. You can also cancel launching this tour and explore yourself
|
||||||
</TeachingBubbleContent>
|
</TeachingBubbleContent>
|
||||||
</Coachmark>
|
</Coachmark>
|
||||||
)}
|
)}
|
||||||
<div className="moreStuffContainer">
|
<div className="moreStuffContainer">
|
||||||
<div className="moreStuffColumn commonTasks">
|
<div className="moreStuffColumn commonTasks">
|
||||||
<div className="title">{userContext.features.enableNewQuickstart ? "Recents" : "Common Tasks"}</div>
|
<div className="title">Recents</div>
|
||||||
{userContext.features.enableNewQuickstart ? this.getRecentItems() : this.getCommonTasksItems()}
|
{this.getRecentItems()}
|
||||||
</div>
|
</div>
|
||||||
<div className="moreStuffColumn">
|
<div className="moreStuffColumn">
|
||||||
<div className="title">
|
<div className="title">Top 3 things you need to know</div>
|
||||||
{userContext.features.enableNewQuickstart ? "Top 3 things you need to know" : "Recents"}
|
{this.top3Items()}
|
||||||
</div>
|
|
||||||
{userContext.features.enableNewQuickstart ? this.top3Items() : this.getRecentItems()}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="moreStuffColumn tipsContainer">
|
<div className="moreStuffColumn tipsContainer">
|
||||||
<div className="title">
|
<div className="title">Learning Resources</div>
|
||||||
{userContext.features.enableNewQuickstart ? "Learning Resources" : "Tips"}
|
{this.getLearningResourceItems()}
|
||||||
</div>
|
|
||||||
{userContext.features.enableNewQuickstart ? this.getLearningResourceItems() : this.getTipItems()}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -202,73 +194,45 @@ export class SplashScreen extends React.Component<SplashScreenProps, SplashScree
|
||||||
public createMainItems(): SplashScreenItem[] {
|
public createMainItems(): SplashScreenItem[] {
|
||||||
const heroes: SplashScreenItem[] = [];
|
const heroes: SplashScreenItem[] = [];
|
||||||
|
|
||||||
if (userContext.features.enableNewQuickstart) {
|
if (userContext.apiType === "SQL" || userContext.apiType === "Mongo") {
|
||||||
if (userContext.apiType === "SQL" || userContext.apiType === "Mongo") {
|
const launchQuickstartBtn = {
|
||||||
const launchQuickstartBtn = {
|
id: "quickstartDescription",
|
||||||
id: "quickstartDescription",
|
iconSrc: QuickStartIcon,
|
||||||
iconSrc: QuickStartIcon,
|
title: "Launch quick start",
|
||||||
title: "Launch quick start",
|
description: "Launch a quick start tutorial to get started with sample data",
|
||||||
description: "Launch a quick start tutorial to get started with sample data",
|
showLinkIcon: userContext.apiType === "Mongo",
|
||||||
showLinkIcon: userContext.apiType === "Mongo",
|
onClick: () =>
|
||||||
onClick: () =>
|
userContext.apiType === "Mongo"
|
||||||
userContext.apiType === "Mongo"
|
? window.open("http://aka.ms/mongodbquickstart", "_blank")
|
||||||
? window.open("http://aka.ms/mongodbquickstart", "_blank")
|
: this.container.onNewCollectionClicked({ isQuickstart: true }),
|
||||||
: this.container.onNewCollectionClicked({ isQuickstart: true }),
|
|
||||||
};
|
|
||||||
heroes.push(launchQuickstartBtn);
|
|
||||||
} else if (useNotebook.getState().isPhoenixNotebooks) {
|
|
||||||
const newNotebookBtn = {
|
|
||||||
iconSrc: NotebookColorIcon,
|
|
||||||
title: "New notebook",
|
|
||||||
description: "Visualize your data stored in Azure Cosmos DB",
|
|
||||||
onClick: () => this.container.onNewNotebookClicked(),
|
|
||||||
};
|
|
||||||
heroes.push(newNotebookBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newContainerBtn = {
|
|
||||||
iconSrc: ContainersIcon,
|
|
||||||
title: `New ${getCollectionName()}`,
|
|
||||||
description: "Create a new container for storage and throughput",
|
|
||||||
onClick: () => this.container.onNewCollectionClicked(),
|
|
||||||
};
|
};
|
||||||
heroes.push(newContainerBtn);
|
heroes.push(launchQuickstartBtn);
|
||||||
|
} else if (useNotebook.getState().isPhoenixNotebooks) {
|
||||||
const connectBtn = {
|
const newNotebookBtn = {
|
||||||
iconSrc: ConnectIcon,
|
iconSrc: NotebookColorIcon,
|
||||||
title: "Connect",
|
title: "New notebook",
|
||||||
description: "Prefer using your own choice of tooling? Find the connection string you need to connect",
|
description: "Visualize your data stored in Azure Cosmos DB",
|
||||||
onClick: () => useTabs.getState().openAndActivateConnectTab(),
|
onClick: () => this.container.onNewNotebookClicked(),
|
||||||
};
|
};
|
||||||
heroes.push(connectBtn);
|
heroes.push(newNotebookBtn);
|
||||||
} else {
|
|
||||||
const dataSampleUtil = this.createDataSampleUtil();
|
|
||||||
if (dataSampleUtil.isSampleContainerCreationSupported()) {
|
|
||||||
heroes.push({
|
|
||||||
iconSrc: SampleIcon,
|
|
||||||
title: "Start with Sample",
|
|
||||||
description: "Get started with a sample provided by Cosmos DB",
|
|
||||||
onClick: () => dataSampleUtil.createSampleContainerAsync(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
heroes.push({
|
|
||||||
iconSrc: NewContainerIcon,
|
|
||||||
title: `New ${getCollectionName()}`,
|
|
||||||
description: "Create a new container for storage and throughput",
|
|
||||||
onClick: () => this.container.onNewCollectionClicked(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
|
||||||
heroes.push({
|
|
||||||
iconSrc: NewNotebookIcon,
|
|
||||||
title: "New Notebook",
|
|
||||||
description: "Create a notebook to start querying, visualizing, and modeling your data",
|
|
||||||
onClick: () => this.container.onNewNotebookClicked(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newContainerBtn = {
|
||||||
|
iconSrc: ContainersIcon,
|
||||||
|
title: `New ${getCollectionName()}`,
|
||||||
|
description: "Create a new container for storage and throughput",
|
||||||
|
onClick: () => this.container.onNewCollectionClicked(),
|
||||||
|
};
|
||||||
|
heroes.push(newContainerBtn);
|
||||||
|
|
||||||
|
const connectBtn = {
|
||||||
|
iconSrc: ConnectIcon,
|
||||||
|
title: "Connect",
|
||||||
|
description: "Prefer using your own choice of tooling? Find the connection string you need to connect",
|
||||||
|
onClick: () => useTabs.getState().openAndActivateConnectTab(),
|
||||||
|
};
|
||||||
|
heroes.push(connectBtn);
|
||||||
|
|
||||||
return heroes;
|
return heroes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
import {
|
import { IconButton, ITextFieldStyles, Pivot, PivotItem, PrimaryButton, Stack, Text, TextField } from "@fluentui/react";
|
||||||
IconButton,
|
|
||||||
ITextFieldStyles,
|
|
||||||
Link,
|
|
||||||
Pivot,
|
|
||||||
PivotItem,
|
|
||||||
PrimaryButton,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
TextField,
|
|
||||||
} from "@fluentui/react";
|
|
||||||
import { handleError } from "Common/ErrorHandlingUtils";
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
import { sendMessage } from "Common/MessageHandler";
|
import { sendMessage } from "Common/MessageHandler";
|
||||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||||
|
@ -76,16 +66,6 @@ export const ConnectTab: React.FC = (): JSX.Element => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: "100%", padding: 16 }}>
|
<div style={{ width: "100%", padding: 16 }}>
|
||||||
<Stack style={{ marginLeft: 10 }}>
|
|
||||||
<Text variant="medium">
|
|
||||||
Ensure you have the right networking / access configuration before you establish the connection with your app
|
|
||||||
or 3rd party tool.
|
|
||||||
</Text>
|
|
||||||
<Link style={{ fontSize: 14 }} target="_blank" href="">
|
|
||||||
Configure networking in Azure portal
|
|
||||||
</Link>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Pivot>
|
<Pivot>
|
||||||
{userContext.hasWriteAccess && (
|
{userContext.hasWriteAccess && (
|
||||||
<PivotItem headerText="Read-write Keys">
|
<PivotItem headerText="Read-write Keys">
|
||||||
|
|
|
@ -121,11 +121,7 @@ function TabPane({ tab, active }: { tab: Tab; active: boolean }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect((): (() => void) | void => {
|
useEffect((): (() => void) | void => {
|
||||||
if (
|
if (tab.tabKind === CollectionTabKind.Documents && tab.collection?.isSampleCollection) {
|
||||||
tab.tabKind === CollectionTabKind.Documents &&
|
|
||||||
tab.collection?.databaseId === "SampleDB" &&
|
|
||||||
tab.collection?.id() === "SampleContainer"
|
|
||||||
) {
|
|
||||||
useTeachingBubble.getState().setIsDocumentsTabOpened(true);
|
useTeachingBubble.getState().setIsDocumentsTabOpened(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,7 @@ export default class Collection implements ViewModels.Collection {
|
||||||
public storedProceduresFocused: ko.Observable<boolean>;
|
public storedProceduresFocused: ko.Observable<boolean>;
|
||||||
public userDefinedFunctionsFocused: ko.Observable<boolean>;
|
public userDefinedFunctionsFocused: ko.Observable<boolean>;
|
||||||
public triggersFocused: ko.Observable<boolean>;
|
public triggersFocused: ko.Observable<boolean>;
|
||||||
|
public isSampleCollection: boolean;
|
||||||
private isOfferRead: boolean;
|
private isOfferRead: boolean;
|
||||||
|
|
||||||
constructor(container: Explorer, databaseId: string, data: DataModels.Collection) {
|
constructor(container: Explorer, databaseId: string, data: DataModels.Collection) {
|
||||||
|
@ -216,6 +217,7 @@ export default class Collection implements ViewModels.Collection {
|
||||||
this.isStoredProceduresExpanded = ko.observable<boolean>(false);
|
this.isStoredProceduresExpanded = ko.observable<boolean>(false);
|
||||||
this.isUserDefinedFunctionsExpanded = ko.observable<boolean>(false);
|
this.isUserDefinedFunctionsExpanded = ko.observable<boolean>(false);
|
||||||
this.isTriggersExpanded = ko.observable<boolean>(false);
|
this.isTriggersExpanded = ko.observable<boolean>(false);
|
||||||
|
this.isSampleCollection = false;
|
||||||
this.isOfferRead = false;
|
this.isOfferRead = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ export default class Database implements ViewModels.Database {
|
||||||
public isDatabaseShared: ko.Computed<boolean>;
|
public isDatabaseShared: ko.Computed<boolean>;
|
||||||
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
|
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
|
||||||
public junoClient: JunoClient;
|
public junoClient: JunoClient;
|
||||||
|
public isSampleDB: boolean;
|
||||||
private isOfferRead: boolean;
|
private isOfferRead: boolean;
|
||||||
|
|
||||||
constructor(container: Explorer, data: DataModels.Database) {
|
constructor(container: Explorer, data: DataModels.Database) {
|
||||||
|
@ -54,6 +55,7 @@ export default class Database implements ViewModels.Database {
|
||||||
return this.offer && !!this.offer();
|
return this.offer && !!this.offer();
|
||||||
});
|
});
|
||||||
this.junoClient = new JunoClient();
|
this.junoClient = new JunoClient();
|
||||||
|
this.isSampleDB = false;
|
||||||
this.isOfferRead = false;
|
this.isOfferRead = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import shallow from "zustand/shallow";
|
import shallow from "zustand/shallow";
|
||||||
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
||||||
|
@ -462,7 +461,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
||||||
|
|
||||||
if (database.isDatabaseShared()) {
|
if (database.isDatabaseShared()) {
|
||||||
databaseNode.children.push({
|
databaseNode.children.push({
|
||||||
id: database.id() === "SampleDB" ? "sampleScaleSettings" : "",
|
id: database.isSampleDB ? "sampleScaleSettings" : "",
|
||||||
label: "Scale",
|
label: "Scale",
|
||||||
isSelected: () =>
|
isSelected: () =>
|
||||||
useSelectedNode
|
useSelectedNode
|
||||||
|
@ -499,7 +498,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
||||||
const children: TreeNode[] = [];
|
const children: TreeNode[] = [];
|
||||||
children.push({
|
children.push({
|
||||||
label: collection.getLabel(),
|
label: collection.getLabel(),
|
||||||
id: collection.databaseId === "SampleDB" && collection.id() === "SampleContainer" ? "sampleItems" : "",
|
id: collection.isSampleCollection ? "sampleItems" : "",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
collection.openTab();
|
collection.openTab();
|
||||||
// push to most recent
|
// push to most recent
|
||||||
|
@ -533,10 +532,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
||||||
|
|
||||||
if (userContext.apiType !== "Cassandra" || !isServerlessAccount()) {
|
if (userContext.apiType !== "Cassandra" || !isServerlessAccount()) {
|
||||||
children.push({
|
children.push({
|
||||||
id:
|
id: collection.isSampleCollection && !database.isDatabaseShared() ? "sampleScaleSettings" : "",
|
||||||
collection.databaseId === "SampleDB" && collection.id() === "SampleContainer" && !database.isDatabaseShared()
|
|
||||||
? "sampleScaleSettings"
|
|
||||||
: "",
|
|
||||||
label: database.isDatabaseShared() || isServerlessAccount() ? "Settings" : "Scale & Settings",
|
label: database.isDatabaseShared() || isServerlessAccount() ? "Settings" : "Scale & Settings",
|
||||||
onClick: collection.onSettingsClick.bind(collection),
|
onClick: collection.onSettingsClick.bind(collection),
|
||||||
isSelected: () =>
|
isSelected: () =>
|
||||||
|
@ -593,10 +589,6 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onExpanded: () => {
|
onExpanded: () => {
|
||||||
// TODO: For testing purpose only, remove after
|
|
||||||
if (collection.databaseId === "SampleDB" && collection.id() === "SampleContainer") {
|
|
||||||
useTeachingBubble.getState().setIsSampleDBExpanded(true);
|
|
||||||
}
|
|
||||||
if (showScriptNodes) {
|
if (showScriptNodes) {
|
||||||
collection.loadStoredProcedures();
|
collection.loadStoredProcedures();
|
||||||
collection.loadUserDefinedFunctions();
|
collection.loadUserDefinedFunctions();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { DefaultButton, IconButton, Image, Modal, PrimaryButton, Stack, Text } from "@fluentui/react";
|
import { DefaultButton, IconButton, Image, Modal, PrimaryButton, Stack, Text } from "@fluentui/react";
|
||||||
|
import { useCarousel } from "hooks/useCarousel";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import Youtube from "react-youtube";
|
import Youtube from "react-youtube";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
import Image1 from "../../../images/CarouselImage1.svg";
|
import Image1 from "../../../images/CarouselImage1.svg";
|
||||||
import Image2 from "../../../images/CarouselImage2.svg";
|
import Image2 from "../../../images/CarouselImage2.svg";
|
||||||
|
|
||||||
|
@ -13,7 +15,11 @@ export const QuickstartCarousel: React.FC<QuickstartCarouselProps> = ({
|
||||||
}: QuickstartCarouselProps): JSX.Element => {
|
}: QuickstartCarouselProps): JSX.Element => {
|
||||||
const [page, setPage] = useState<number>(1);
|
const [page, setPage] = useState<number>(1);
|
||||||
return (
|
return (
|
||||||
<Modal styles={{ main: { width: 640 } }} isOpen={isOpen && page < 4}>
|
<Modal
|
||||||
|
styles={{ main: { width: 640 } }}
|
||||||
|
isOpen={isOpen && page < 4}
|
||||||
|
onDismissed={() => userContext.apiType === "SQL" && useCarousel.getState().setShowCoachMark(true)}
|
||||||
|
>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal horizontalAlign="space-between" style={{ padding: 16 }}>
|
<Stack horizontal horizontalAlign="space-between" style={{ padding: 16 }}>
|
||||||
<Text variant="xLarge">{getHeaderText(page)}</Text>
|
<Text variant="xLarge">{getHeaderText(page)}</Text>
|
||||||
|
@ -28,9 +34,23 @@ export const QuickstartCarousel: React.FC<QuickstartCarouselProps> = ({
|
||||||
<DefaultButton text="Previous" style={{ margin: "16px 8px 16px 0" }} onClick={() => setPage(page - 1)} />
|
<DefaultButton text="Previous" style={{ margin: "16px 8px 16px 0" }} onClick={() => setPage(page - 1)} />
|
||||||
)}
|
)}
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
|
id="carouselNextBtn"
|
||||||
style={{ margin: "16px 16px 16px 0" }}
|
style={{ margin: "16px 16px 16px 0" }}
|
||||||
text={page === 3 ? "Finish" : "Next"}
|
text={page === 3 ? "Finish" : "Next"}
|
||||||
onClick={() => setPage(page + 1)}
|
onClick={() => {
|
||||||
|
if (
|
||||||
|
userContext.apiType === "Cassandra" ||
|
||||||
|
userContext.apiType === "Tables" ||
|
||||||
|
userContext.apiType === "Gremlin"
|
||||||
|
) {
|
||||||
|
setPage(page + 2);
|
||||||
|
} else {
|
||||||
|
if (page === 3 && userContext.apiType === "SQL") {
|
||||||
|
useCarousel.getState().setShowCoachMark(true);
|
||||||
|
}
|
||||||
|
setPage(page + 1);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { TeachingBubble } from "@fluentui/react";
|
import { Link, Stack, TeachingBubble, Text } from "@fluentui/react";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
||||||
const { step, isSampleDBExpanded, isDocumentsTabOpened, setStep } = useTeachingBubble();
|
const { step, isSampleDBExpanded, isDocumentsTabOpened, sampleCollection, setStep } = useTeachingBubble();
|
||||||
|
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -17,8 +16,7 @@ export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
text: "Open Items",
|
text: "Open Items",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const sampleContainer = useDatabases.getState().findCollection("SampleDB", "SampleContainer");
|
sampleCollection.openTab();
|
||||||
sampleContainer.openTab();
|
|
||||||
setStep(2);
|
setStep(2);
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
@ -70,7 +68,7 @@ export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
||||||
onDismiss={() => setStep(0)}
|
onDismiss={() => setStep(0)}
|
||||||
footerContent="Step 3 of 7"
|
footerContent="Step 3 of 7"
|
||||||
>
|
>
|
||||||
Add new item by copy / pasting jsons; or uploading a json
|
Add new item by copy / pasting JSON; or uploading a JSON
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
);
|
);
|
||||||
case 4:
|
case 4:
|
||||||
|
@ -120,7 +118,7 @@ export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
||||||
target={"#newNotebookBtn"}
|
target={"#newNotebookBtn"}
|
||||||
hasCloseButton
|
hasCloseButton
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
text: "Finish",
|
text: "Next",
|
||||||
onClick: () => setStep(7),
|
onClick: () => setStep(7),
|
||||||
}}
|
}}
|
||||||
secondaryButtonProps={{
|
secondaryButtonProps={{
|
||||||
|
@ -150,8 +148,15 @@ export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
||||||
onDismiss={() => setStep(0)}
|
onDismiss={() => setStep(0)}
|
||||||
footerContent="Step 7 of 7"
|
footerContent="Step 7 of 7"
|
||||||
>
|
>
|
||||||
You have finished the tour in data explorer. For next steps, you may want to launch connect and start
|
<Stack>
|
||||||
connecting with your current app
|
<Text style={{ color: "white" }}>
|
||||||
|
You have finished the tour in data explorer. For next steps, you may want to launch connect and start
|
||||||
|
connecting with your current app.
|
||||||
|
</Text>
|
||||||
|
<Link style={{ color: "white", fontWeight: 600 }} target="_blank" href="https://aka.ms/cosmosdbsurvey">
|
||||||
|
Share your feedback
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { initializeIcons } from "@fluentui/react";
|
||||||
import "bootstrap/dist/css/bootstrap.css";
|
import "bootstrap/dist/css/bootstrap.css";
|
||||||
import { QuickstartCarousel } from "Explorer/Tutorials/QuickstartCarousel";
|
import { QuickstartCarousel } from "Explorer/Tutorials/QuickstartCarousel";
|
||||||
import { QuickstartTutorial } from "Explorer/Tutorials/QuickstartTutorial";
|
import { QuickstartTutorial } from "Explorer/Tutorials/QuickstartTutorial";
|
||||||
|
import { useCarousel } from "hooks/useCarousel";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { userContext } from "UserContext";
|
|
||||||
import "../externals/jquery-ui.min.css";
|
import "../externals/jquery-ui.min.css";
|
||||||
import "../externals/jquery-ui.min.js";
|
import "../externals/jquery-ui.min.js";
|
||||||
import "../externals/jquery-ui.structure.min.css";
|
import "../externals/jquery-ui.structure.min.css";
|
||||||
|
@ -61,6 +61,7 @@ const App: React.FunctionComponent = () => {
|
||||||
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
||||||
const openedTabs = useTabs((state) => state.openedTabs);
|
const openedTabs = useTabs((state) => state.openedTabs);
|
||||||
const isConnectTabOpen = useTabs((state) => state.isConnectTabOpen);
|
const isConnectTabOpen = useTabs((state) => state.isConnectTabOpen);
|
||||||
|
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
|
||||||
|
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const explorer = useKnockoutExplorer(config?.platform);
|
const explorer = useKnockoutExplorer(config?.platform);
|
||||||
|
@ -119,8 +120,8 @@ const App: React.FunctionComponent = () => {
|
||||||
</div>
|
</div>
|
||||||
<SidePanel />
|
<SidePanel />
|
||||||
<Dialog />
|
<Dialog />
|
||||||
{userContext.features.enableNewQuickstart && <QuickstartCarousel isOpen={true} />}
|
{<QuickstartCarousel isOpen={isCarouselOpen} />}
|
||||||
{userContext.features.enableNewQuickstart && <QuickstartTutorial />}
|
{<QuickstartTutorial />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,7 +29,6 @@ export type Features = {
|
||||||
readonly mongoProxyEndpoint?: string;
|
readonly mongoProxyEndpoint?: string;
|
||||||
readonly mongoProxyAPIs?: string;
|
readonly mongoProxyAPIs?: string;
|
||||||
readonly enableThroughputCap: boolean;
|
readonly enableThroughputCap: boolean;
|
||||||
readonly enableNewQuickstart: boolean;
|
|
||||||
|
|
||||||
// can be set via both flight and feature flag
|
// can be set via both flight and feature flag
|
||||||
autoscaleDefault: boolean;
|
autoscaleDefault: boolean;
|
||||||
|
@ -91,7 +90,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
||||||
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
|
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
|
||||||
notebooksDownBanner: "true" === get("notebooksDownBanner"),
|
notebooksDownBanner: "true" === get("notebooksDownBanner"),
|
||||||
enableThroughputCap: "true" === get("enablethroughputcap"),
|
enableThroughputCap: "true" === get("enablethroughputcap"),
|
||||||
enableNewQuickstart: "true" === get("enablenewquickstart"),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,4 +14,5 @@ export enum StorageKey {
|
||||||
MostRecentActivity,
|
MostRecentActivity,
|
||||||
SetPartitionKeyUndefined,
|
SetPartitionKeyUndefined,
|
||||||
GalleryCalloutDismissed,
|
GalleryCalloutDismissed,
|
||||||
|
VisitedAccounts,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useCarousel } from "hooks/useCarousel";
|
||||||
import { AuthType } from "./AuthType";
|
import { AuthType } from "./AuthType";
|
||||||
import { DatabaseAccount } from "./Contracts/DataModels";
|
import { DatabaseAccount } from "./Contracts/DataModels";
|
||||||
import { SubscriptionType } from "./Contracts/SubscriptionType";
|
import { SubscriptionType } from "./Contracts/SubscriptionType";
|
||||||
|
@ -73,6 +74,10 @@ const userContext: UserContext = {
|
||||||
function updateUserContext(newContext: Partial<UserContext>): void {
|
function updateUserContext(newContext: Partial<UserContext>): void {
|
||||||
if (newContext.databaseAccount) {
|
if (newContext.databaseAccount) {
|
||||||
newContext.apiType = apiType(newContext.databaseAccount);
|
newContext.apiType = apiType(newContext.databaseAccount);
|
||||||
|
if (!localStorage.getItem(newContext.databaseAccount.id)) {
|
||||||
|
useCarousel.getState().setShouldOpen(true);
|
||||||
|
localStorage.setItem(newContext.databaseAccount.id, "true");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Object.assign(userContext, newContext);
|
Object.assign(userContext, newContext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import create, { UseStore } from "zustand";
|
||||||
|
|
||||||
|
interface CarouselState {
|
||||||
|
shouldOpen: boolean;
|
||||||
|
showCoachMark: boolean;
|
||||||
|
setShouldOpen: (shouldOpen: boolean) => void;
|
||||||
|
setShowCoachMark: (showCoachMark: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCarousel: UseStore<CarouselState> = create((set) => ({
|
||||||
|
shouldOpen: false,
|
||||||
|
showCoachMark: false,
|
||||||
|
setShouldOpen: (shouldOpen: boolean) => set({ shouldOpen }),
|
||||||
|
setShowCoachMark: (showCoachMark: boolean) => set({ showCoachMark }),
|
||||||
|
}));
|
|
@ -1,19 +1,24 @@
|
||||||
|
import { Collection } from "Contracts/ViewModels";
|
||||||
import create, { UseStore } from "zustand";
|
import create, { UseStore } from "zustand";
|
||||||
|
|
||||||
interface TeachingBubbleState {
|
interface TeachingBubbleState {
|
||||||
step: number;
|
step: number;
|
||||||
isSampleDBExpanded: boolean;
|
isSampleDBExpanded: boolean;
|
||||||
isDocumentsTabOpened: boolean;
|
isDocumentsTabOpened: boolean;
|
||||||
|
sampleCollection: Collection;
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
setIsSampleDBExpanded: (isReady: boolean) => void;
|
setIsSampleDBExpanded: (isReady: boolean) => void;
|
||||||
setIsDocumentsTabOpened: (isOpened: boolean) => void;
|
setIsDocumentsTabOpened: (isOpened: boolean) => void;
|
||||||
|
setSampleCollection: (sampleCollection: Collection) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useTeachingBubble: UseStore<TeachingBubbleState> = create((set) => ({
|
export const useTeachingBubble: UseStore<TeachingBubbleState> = create((set) => ({
|
||||||
step: 1,
|
step: 1,
|
||||||
isSampleDBExpanded: false,
|
isSampleDBExpanded: false,
|
||||||
isDocumentsTabOpened: false,
|
isDocumentsTabOpened: false,
|
||||||
|
sampleCollection: undefined,
|
||||||
setStep: (step: number) => set({ step }),
|
setStep: (step: number) => set({ step }),
|
||||||
setIsSampleDBExpanded: (isSampleDBExpanded: boolean) => set({ isSampleDBExpanded }),
|
setIsSampleDBExpanded: (isSampleDBExpanded: boolean) => set({ isSampleDBExpanded }),
|
||||||
setIsDocumentsTabOpened: (isDocumentsTabOpened: boolean) => set({ isDocumentsTabOpened }),
|
setIsDocumentsTabOpened: (isDocumentsTabOpened: boolean) => set({ isDocumentsTabOpened }),
|
||||||
|
setSampleCollection: (sampleCollection: Collection) => set({ sampleCollection }),
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -13,6 +13,10 @@ test("Cassandra keyspace and table CRUD", async () => {
|
||||||
await page.waitForSelector("iframe");
|
await page.waitForSelector("iframe");
|
||||||
const explorer = await waitForExplorer();
|
const explorer = await waitForExplorer();
|
||||||
|
|
||||||
|
// Click through quick start carousel
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
|
||||||
await explorer.click('[data-test="New Table"]');
|
await explorer.click('[data-test="New Table"]');
|
||||||
await explorer.click('[aria-label="Keyspace id"]');
|
await explorer.click('[aria-label="Keyspace id"]');
|
||||||
await explorer.fill('[aria-label="Keyspace id"]', keyspaceId);
|
await explorer.fill('[aria-label="Keyspace id"]', keyspaceId);
|
||||||
|
|
|
@ -12,6 +12,10 @@ test("Graph CRUD", async () => {
|
||||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-gremlin-runner");
|
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-gremlin-runner");
|
||||||
const explorer = await waitForExplorer();
|
const explorer = await waitForExplorer();
|
||||||
|
|
||||||
|
// Click through quick start carousel
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
|
||||||
// Create new database and graph
|
// Create new database and graph
|
||||||
await explorer.click('[data-test="New Graph"]');
|
await explorer.click('[data-test="New Graph"]');
|
||||||
await explorer.fill('[aria-label="New database id"]', databaseId);
|
await explorer.fill('[aria-label="New database id"]', databaseId);
|
||||||
|
|
|
@ -12,6 +12,11 @@ test("Mongo CRUD", async () => {
|
||||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-mongo-runner");
|
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-mongo-runner");
|
||||||
const explorer = await waitForExplorer();
|
const explorer = await waitForExplorer();
|
||||||
|
|
||||||
|
// Click through quick start carousel
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
|
||||||
// Create new database and collection
|
// Create new database and collection
|
||||||
await explorer.click('[data-test="New Collection"]');
|
await explorer.click('[data-test="New Collection"]');
|
||||||
await explorer.fill('[aria-label="New database id"]', databaseId);
|
await explorer.fill('[aria-label="New database id"]', databaseId);
|
||||||
|
|
|
@ -12,6 +12,11 @@ test("Mongo CRUD", async () => {
|
||||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-mongo32-runner");
|
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-mongo32-runner");
|
||||||
const explorer = await waitForExplorer();
|
const explorer = await waitForExplorer();
|
||||||
|
|
||||||
|
// Click through quick start carousel
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
|
||||||
// Create new database and collection
|
// Create new database and collection
|
||||||
await explorer.click('[data-test="New Collection"]');
|
await explorer.click('[data-test="New Collection"]');
|
||||||
await explorer.fill('[aria-label="New database id"]', databaseId);
|
await explorer.fill('[aria-label="New database id"]', databaseId);
|
||||||
|
|
|
@ -11,6 +11,12 @@ test("SQL CRUD", async () => {
|
||||||
|
|
||||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-sql-runner-west-us");
|
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-sql-runner-west-us");
|
||||||
const explorer = await waitForExplorer();
|
const explorer = await waitForExplorer();
|
||||||
|
|
||||||
|
// Click through quick start carousel
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
|
||||||
await explorer.click('[data-test="New Container"]');
|
await explorer.click('[data-test="New Container"]');
|
||||||
await explorer.fill('[aria-label="New database id"]', databaseId);
|
await explorer.fill('[aria-label="New database id"]', databaseId);
|
||||||
await explorer.fill('[aria-label="Container id"]', containerId);
|
await explorer.fill('[aria-label="Container id"]', containerId);
|
||||||
|
|
|
@ -12,6 +12,10 @@ test("Tables CRUD", async () => {
|
||||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-tables-runner");
|
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-tables-runner");
|
||||||
const explorer = await waitForExplorer();
|
const explorer = await waitForExplorer();
|
||||||
|
|
||||||
|
// Click through quick start carousel
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
await explorer.click("#carouselNextBtn");
|
||||||
|
|
||||||
await page.waitForSelector('text="Querying databases"', { state: "detached" });
|
await page.waitForSelector('text="Querying databases"', { state: "detached" });
|
||||||
await explorer.click('[data-test="New Table"]');
|
await explorer.click('[data-test="New Table"]');
|
||||||
await explorer.fill('[aria-label="Table id"]', tableId);
|
await explorer.fill('[aria-label="Table id"]', tableId);
|
||||||
|
|
Loading…
Reference in New Issue