From d0c2f72ed340568f9dfa22dfe705058ec89114fe Mon Sep 17 00:00:00 2001 From: victor-meng <56978073+victor-meng@users.noreply.github.com> Date: Wed, 3 Aug 2022 13:54:01 -0700 Subject: [PATCH] Add teaching bubbles for mongo quickstart (#1313) --- .../ThroughputInput/ThroughputInput.tsx | 3 +- .../DataSamples/ContainerSampleGenerator.ts | 9 +- src/Explorer/Panes/AddCollectionPanel.tsx | 4 +- src/Explorer/SplashScreen/SplashScreen.tsx | 5 +- src/Explorer/Tabs/DocumentsTab.ts | 1 + src/Explorer/Tabs/Tabs.tsx | 5 +- src/Explorer/Tree/ResourceTree.tsx | 7 +- .../Tutorials/MongoQuickstartTutorial.tsx | 202 ++++++++++++++++++ src/Explorer/Tutorials/QuickstartTutorial.tsx | 5 + src/Main.tsx | 2 + src/hooks/useTabs.ts | 5 +- 11 files changed, 235 insertions(+), 13 deletions(-) create mode 100644 src/Explorer/Tutorials/MongoQuickstartTutorial.tsx diff --git a/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx b/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx index 7ee64287d..ab4c45c03 100644 --- a/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx +++ b/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx @@ -28,6 +28,7 @@ export const ThroughputInput: FunctionComponent = ({ isSharded, isFreeTier, showFreeTierExceedThroughputTooltip, + isQuickstart, setThroughputValue, setIsAutoscale, setIsThroughputCapExceeded, @@ -35,7 +36,7 @@ export const ThroughputInput: FunctionComponent = ({ }: ThroughputInputProps) => { const [isAutoscaleSelected, setIsAutoScaleSelected] = useState(true); const [throughput, setThroughput] = useState( - isFreeTier ? AutoPilotUtils.autoPilotThroughput1K : AutoPilotUtils.autoPilotThroughput4K + isFreeTier || isQuickstart ? AutoPilotUtils.autoPilotThroughput1K : AutoPilotUtils.autoPilotThroughput4K ); const [isCostAcknowledged, setIsCostAcknowledged] = useState(false); const [throughputError, setThroughputError] = useState(""); diff --git a/src/Explorer/DataSamples/ContainerSampleGenerator.ts b/src/Explorer/DataSamples/ContainerSampleGenerator.ts index e4a2d76bc..fda384f80 100644 --- a/src/Explorer/DataSamples/ContainerSampleGenerator.ts +++ b/src/Explorer/DataSamples/ContainerSampleGenerator.ts @@ -1,5 +1,6 @@ import { createCollection } from "../../Common/dataAccess/createCollection"; import { createDocument } from "../../Common/dataAccess/createDocument"; +import { createDocument as createMongoDocument } from "../../Common/MongoProxyClient"; import * as DataModels from "../../Contracts/DataModels"; import * as ViewModels from "../../Contracts/ViewModels"; import { userContext } from "../../UserContext"; @@ -28,7 +29,7 @@ export class ContainerSampleGenerator { dataFileContent = await import( /* webpackChunkName: "gremlinSampleJsonData" */ "../../../sampleData/gremlinSampleData.json" ); - } else if (userContext.apiType === "SQL") { + } else if (userContext.apiType === "SQL" || userContext.apiType === "Mongo") { dataFileContent = await import( /* webpackChunkName: "sqlSampleJsonData" */ "../../../sampleData/sqlSampleData.json" ); @@ -68,7 +69,7 @@ export class ContainerSampleGenerator { return database.findCollectionWithId(this.sampleDataFile.collectionId); } - public async populateContainerAsync(collection: ViewModels.Collection): Promise { + public async populateContainerAsync(collection: ViewModels.Collection, shardKey?: string): Promise { if (!collection) { throw new Error("No container to populate"); } @@ -99,7 +100,9 @@ export class ContainerSampleGenerator { await Promise.all( this.sampleDataFile.data.map(async (doc) => { try { - await createDocument(collection, doc); + userContext.apiType === "Mongo" + ? await createMongoDocument(collection.databaseId, collection, shardKey, doc) + : await createDocument(collection, doc); } catch (error) { NotificationConsoleUtils.logConsoleError(error); } diff --git a/src/Explorer/Panes/AddCollectionPanel.tsx b/src/Explorer/Panes/AddCollectionPanel.tsx index d2dfa80ba..bc750ab2e 100644 --- a/src/Explorer/Panes/AddCollectionPanel.tsx +++ b/src/Explorer/Panes/AddCollectionPanel.tsx @@ -346,6 +346,7 @@ export class AddCollectionPanel extends React.Component (this.newDatabaseThroughput = throughput)} setIsAutoscale={(isAutoscale: boolean) => (this.isNewDatabaseAutoscale = isAutoscale)} setIsThroughputCapExceeded={(isThroughputCapExceeded: boolean) => @@ -581,6 +582,7 @@ export class AddCollectionPanel extends React.Component (this.collectionThroughput = throughput)} setIsAutoscale={(isAutoscale: boolean) => (this.isCollectionAutoscale = isAutoscale)} setIsThroughputCapExceeded={(isThroughputCapExceeded: boolean) => @@ -1249,7 +1251,7 @@ export class AddCollectionPanel extends React.Component { iconSrc: QuickStartIcon, title: "Launch quick start", description: "Launch a quick start tutorial to get started with sample data", - showLinkIcon: userContext.apiType === "Mongo", onClick: () => { - userContext.apiType === "Mongo" - ? window.open("http://aka.ms/mongodbquickstart", "_blank") - : this.container.onNewCollectionClicked({ isQuickstart: true }); + this.container.onNewCollectionClicked({ isQuickstart: true }); traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType }); }, }; diff --git a/src/Explorer/Tabs/DocumentsTab.ts b/src/Explorer/Tabs/DocumentsTab.ts index fd51273d7..f75a99fac 100644 --- a/src/Explorer/Tabs/DocumentsTab.ts +++ b/src/Explorer/Tabs/DocumentsTab.ts @@ -811,6 +811,7 @@ export default class DocumentsTab extends TabsBase { ariaLabel: label, hasPopup: false, disabled: !this.newDocumentButton.enabled(), + id: "mongoNewDocumentBtn", }); } diff --git a/src/Explorer/Tabs/Tabs.tsx b/src/Explorer/Tabs/Tabs.tsx index 1439b2fd2..3a4501651 100644 --- a/src/Explorer/Tabs/Tabs.tsx +++ b/src/Explorer/Tabs/Tabs.tsx @@ -120,7 +120,10 @@ const CloseButton = ({ role="button" aria-label="Close Tab" className="cancelButton" - onClick={() => (tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind))} + onClick={(event: React.MouseEvent) => { + event.stopPropagation(); + tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind); + }} tabIndex={active ? 0 : undefined} onKeyPress={({ nativeEvent: e }) => tab.onKeyPressClose(undefined, e)} > diff --git a/src/Explorer/Tree/ResourceTree.tsx b/src/Explorer/Tree/ResourceTree.tsx index 87432ce8b..255c5412c 100644 --- a/src/Explorer/Tree/ResourceTree.tsx +++ b/src/Explorer/Tree/ResourceTree.tsx @@ -531,8 +531,13 @@ export const ResourceTree: React.FC = ({ container }: Resourc } if (userContext.apiType !== "Cassandra" || !isServerlessAccount()) { + let id = ""; + if (collection.isSampleCollection) { + id = database.isDatabaseShared() ? "sampleSettings" : "sampleScaleSettings"; + } + children.push({ - id: collection.isSampleCollection && !database.isDatabaseShared() ? "sampleScaleSettings" : "", + id, label: database.isDatabaseShared() || isServerlessAccount() ? "Settings" : "Scale & Settings", onClick: collection.onSettingsClick.bind(collection), isSelected: () => diff --git a/src/Explorer/Tutorials/MongoQuickstartTutorial.tsx b/src/Explorer/Tutorials/MongoQuickstartTutorial.tsx new file mode 100644 index 000000000..71cf98dbf --- /dev/null +++ b/src/Explorer/Tutorials/MongoQuickstartTutorial.tsx @@ -0,0 +1,202 @@ +import { Link, Stack, TeachingBubble, Text } from "@fluentui/react"; +import { ReactTabKind, useTabs } from "hooks/useTabs"; +import { useTeachingBubble } from "hooks/useTeachingBubble"; +import React from "react"; +import { Action } from "Shared/Telemetry/TelemetryConstants"; +import { traceCancel, traceSuccess } from "Shared/Telemetry/TelemetryProcessor"; +import { userContext } from "UserContext"; + +export const MongoQuickstartTutorial: React.FC = (): JSX.Element => { + const { step, isSampleDBExpanded, isDocumentsTabOpened, sampleCollection, setStep } = useTeachingBubble(); + + const onDimissTeachingBubble = (): void => { + setStep(0); + traceCancel(Action.CancelUITour, { step }); + }; + + if (userContext.apiType !== "Mongo") { + return <>; + } + + switch (step) { + case 1: + return isSampleDBExpanded ? ( + { + sampleCollection.openTab(); + setStep(2); + }, + }} + onDismiss={() => onDimissTeachingBubble()} + footerContent="Step 1 of 8" + > + Start viewing and working with your data by opening Documents under Data + + ) : ( + <> + ); + case 2: + return isDocumentsTabOpened ? ( + setStep(3), + }} + secondaryButtonProps={{ + text: "Previous", + onClick: () => setStep(1), + }} + onDismiss={() => onDimissTeachingBubble()} + footerContent="Step 2 of 8" + > + View documents here using the documents window. You can also use your favorite MongoDB tools and drivers to do + so. + + ) : ( + <> + ); + case 3: + return ( + setStep(4), + }} + secondaryButtonProps={{ + text: "Previous", + onClick: () => setStep(2), + }} + onDismiss={() => onDimissTeachingBubble()} + footerContent="Step 3 of 8" + > + Add new document by copy / pasting JSON or uploading a JSON. You can also use your favorite MongoDB tools and + drivers to do so. + + ); + case 4: + return ( + setStep(5), + }} + secondaryButtonProps={{ + text: "Previous", + onClick: () => setStep(3), + }} + onDismiss={() => onDimissTeachingBubble()} + footerContent="Step 4 of 8" + > + Query your data using the filter function. Azure Cosmos DB API for MongoDB provides comprehensive support for + MongoDB query language constructs. You can also use your favorite MongoDB tools and drivers to do so. + + ); + case 5: + return ( + setStep(6), + }} + secondaryButtonProps={{ + text: "Previous", + onClick: () => setStep(4), + }} + onDismiss={() => onDimissTeachingBubble()} + footerContent="Step 5 of 8" + > + Change throughput provisioned to your collection according to your needs + + ); + case 6: + return ( + setStep(7), + }} + secondaryButtonProps={{ + text: "Previous", + onClick: () => setStep(5), + }} + onDismiss={() => onDimissTeachingBubble()} + footerContent="Step 6 of 8" + > + Use the indexing policy editor to create and edit your indexes. + + ); + case 7: + return ( + setStep(8), + }} + secondaryButtonProps={{ + text: "Previous", + onClick: () => setStep(6), + }} + onDismiss={() => onDimissTeachingBubble()} + footerContent="Step 7 of 8" + > + Visualize your data, store queries in an interactive document + + ); + case 8: + return ( + { + traceSuccess(Action.CompleteUITour); + useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect); + }, + }} + secondaryButtonProps={{ + text: "Previous", + onClick: () => setStep(7), + }} + onDismiss={() => onDimissTeachingBubble()} + footerContent="Step 8 of 8" + > + + + You have finished the tour in data explorer. For next steps, you may want to launch connect and start + connecting with your current app. + + + Share your feedback + + + + ); + default: + return <>; + } +}; diff --git a/src/Explorer/Tutorials/QuickstartTutorial.tsx b/src/Explorer/Tutorials/QuickstartTutorial.tsx index 516a3a518..3cbe6db45 100644 --- a/src/Explorer/Tutorials/QuickstartTutorial.tsx +++ b/src/Explorer/Tutorials/QuickstartTutorial.tsx @@ -4,6 +4,7 @@ import { useTeachingBubble } from "hooks/useTeachingBubble"; import React from "react"; import { Action } from "Shared/Telemetry/TelemetryConstants"; import { traceCancel, traceSuccess } from "Shared/Telemetry/TelemetryProcessor"; +import { userContext } from "UserContext"; export const QuickstartTutorial: React.FC = (): JSX.Element => { const { step, isSampleDBExpanded, isDocumentsTabOpened, sampleCollection, setStep } = useTeachingBubble(); @@ -13,6 +14,10 @@ export const QuickstartTutorial: React.FC = (): JSX.Element => { traceCancel(Action.CancelUITour, { step }); }; + if (userContext.apiType !== "SQL") { + return <>; + } + switch (step) { case 1: return isSampleDBExpanded ? ( diff --git a/src/Main.tsx b/src/Main.tsx index 20a7340ee..57efffaa9 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -1,6 +1,7 @@ // CSS Dependencies import { initializeIcons } from "@fluentui/react"; import "bootstrap/dist/css/bootstrap.css"; +import { MongoQuickstartTutorial } from "Explorer/Tutorials/MongoQuickstartTutorial"; import { QuickstartCarousel } from "Explorer/Tutorials/QuickstartCarousel"; import { QuickstartTutorial } from "Explorer/Tutorials/QuickstartTutorial"; import { useCarousel } from "hooks/useCarousel"; @@ -116,6 +117,7 @@ const App: React.FunctionComponent = () => { {} {} + {} ); }; diff --git a/src/hooks/useTabs.ts b/src/hooks/useTabs.ts index d8528c8a3..96d23c311 100644 --- a/src/hooks/useTabs.ts +++ b/src/hooks/useTabs.ts @@ -132,12 +132,13 @@ export const useTabs: UseStore = create((set, get) => ({ }, closeReactTab: (tabKind: ReactTabKind) => { const { activeReactTab, openedTabs, openedReactTabs } = get(); + const updatedOpenedReactTabs = openedReactTabs.filter((tab: ReactTabKind) => tabKind !== tab); if (activeReactTab === tabKind) { openedTabs?.length > 0 ? set({ activeTab: openedTabs[0], activeReactTab: undefined }) - : set({ activeTab: undefined, activeReactTab: openedReactTabs[0] }); + : set({ activeTab: undefined, activeReactTab: updatedOpenedReactTabs[0] }); } - set({ openedReactTabs: openedReactTabs.filter((tab: ReactTabKind) => tabKind !== tab) }); + set({ openedReactTabs: updatedOpenedReactTabs }); }, }));