diff --git a/app/components/QuickStartTabs.tsx b/app/components/QuickStartTabs.tsx new file mode 100644 index 0000000..26af5cb --- /dev/null +++ b/app/components/QuickStartTabs.tsx @@ -0,0 +1,206 @@ +"use client"; + +import Link from "next/link"; +import { useRef, useState } from "react"; +import CommandSnippet from "./CommandSnippet"; + +export type QuickStartStep = { + step: string; + description: string; +}; + +type QuickStartTabsProps = { + dockerCommand: string; + dockerSteps: QuickStartStep[]; + vscodeSteps: QuickStartStep[]; + /** Deep link that opens the extension's DocumentDB Local setup wizard. */ + vscodeDeepLinkUrl: string; + /** Marketplace page, for visitors who do not have the extension yet. */ + vscodeMarketplaceUrl: string; + /** Full guide for each path, linked from the footer of the matching panel. */ + dockerDocsUrl: string; + vscodeDocsUrl: string; +}; + +// "Terminal" rather than "Docker": both paths run the same Docker container, and labelling one +// of them "Docker" implies the other avoids it. +const TABS = [ + { id: "terminal", label: "Terminal" }, + { id: "vscode", label: "VS Code" }, +] as const; + +type TabId = (typeof TABS)[number]["id"]; + +function StepList({ steps }: { steps: QuickStartStep[] }) { + return ( +
    + {steps.map((item) => ( +
  1. + + {item.step} + +

    {item.description}

    +
  2. + ))} +
+ ); +} + +export default function QuickStartTabs({ + dockerCommand, + dockerSteps, + vscodeSteps, + vscodeDeepLinkUrl, + vscodeMarketplaceUrl, + dockerDocsUrl, + vscodeDocsUrl, +}: QuickStartTabsProps) { + const [activeTab, setActiveTab] = useState("terminal"); + const tabRefs = useRef>({}); + + const selectTab = (id: TabId) => { + setActiveTab(id); + tabRefs.current[id]?.focus(); + }; + + // Arrow keys move between tabs, which is what a tablist is expected to do; without it the + // only way through is Tab, and that leaves the panel. Home/End jump to the ends, per the + // ARIA authoring practices for tabs. + const onTabKeyDown = (event: React.KeyboardEvent) => { + const currentIndex = TABS.findIndex((tab) => tab.id === activeTab); + + switch (event.key) { + case "ArrowRight": + case "ArrowLeft": { + event.preventDefault(); + const delta = event.key === "ArrowRight" ? 1 : -1; + selectTab(TABS[(currentIndex + delta + TABS.length) % TABS.length].id); + break; + } + case "Home": + event.preventDefault(); + selectTab(TABS[0].id); + break; + case "End": + event.preventDefault(); + selectTab(TABS[TABS.length - 1].id); + break; + default: + break; + } + }; + + return ( +
+
+ {TABS.map((tab) => { + const isActive = tab.id === activeTab; + + return ( + + ); + })} +
+ + + + + +
+ + {activeTab === "vscode" + ? "Full VS Code guide" + : "Full Docker guide"} + +
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index a8a7098..2d301d8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,11 @@ import Image from "next/image"; import Link from "next/link"; -import CommandSnippet from "./components/CommandSnippet"; -import { documentdbKubernetesOperatorQuickStartUrl } from "./services/externalLinks"; +import QuickStartTabs from "./components/QuickStartTabs"; +import { + documentdbKubernetesOperatorQuickStartUrl, + documentdbVsCodeExtensionMarketplaceUrl, + documentdbVsCodeLocalQuickStartDeepLink, +} from "./services/externalLinks"; import { getMetadata } from "./services/metadataService"; import { documentdbGitHubForks, @@ -39,18 +43,39 @@ const quickRunCommand = `docker run -dt --name documentdb \\ --username \\ --password `; -const quickStartSteps = [ +const dockerQuickStartSteps = [ + { + step: "01", + description: + "Run the command above. Docker pulls the image on the first run, so give it about a minute.", + }, + { + step: "02", + description: + "Connect on port 10260 with mongosh, any MongoDB driver, or your app.", + }, + { + step: "03", + description: + "Run your first query. The Docker guide has connection strings and sample data.", + }, +]; + +const vscodeQuickStartSteps = [ { step: "01", - description: "Run DocumentDB Local with Docker.", + description: + "Install the free DocumentDB for VS Code extension from the Marketplace.", }, { step: "02", - description: "Connect on port 10260 with your app, shell, or client.", + description: + "Select Open setup in VS Code and allow it to open the link. The wizard starts DocumentDB Local with defaults you can review.", }, { step: "03", - description: "Continue with the docs or Linux packages for the setup you need.", + description: + "You get a container on port 10260 with generated credentials. Select Open Connection to browse data and run your first query.", }, ]; @@ -316,7 +341,7 @@ export default function Home() {
-
+

Open source document database @@ -372,43 +397,22 @@ export default function Home() { Quick start

- Run locally with Docker + Run DocumentDB locally

- Start DocumentDB Local with Docker, then connect on port - 10260. + Run it from your terminal, or let the VS Code extension set it + up for you. Both start the same DocumentDB Local container.

- -
    - {quickStartSteps.map((item) => ( -
  1. - - {item.step} - -

    - {item.description} -

    -
  2. - ))} -
-
- - Docker quick start - - - Download packages - -
+
diff --git a/app/services/externalLinks.ts b/app/services/externalLinks.ts index 9445af0..7a3ada9 100644 --- a/app/services/externalLinks.ts +++ b/app/services/externalLinks.ts @@ -13,3 +13,9 @@ export const documentdbKubernetesOperatorQuickStartUrl = export const documentdbKubernetesOperatorGitHubUrl = 'https://github.com/documentdb/documentdb-kubernetes-operator'; + +export const documentdbVsCodeExtensionMarketplaceUrl = + 'https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-documentdb'; + +export const documentdbVsCodeLocalQuickStartDeepLink = + 'vscode://ms-azuretools.vscode-documentdb/local'; diff --git a/tests/quickStart.test.ts b/tests/quickStart.test.ts new file mode 100644 index 0000000..b7d9b09 --- /dev/null +++ b/tests/quickStart.test.ts @@ -0,0 +1,98 @@ +import { createElement } from 'react'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { describe, expect, it } from 'vitest'; +import Home from '../app/page'; +import { + documentdbVsCodeExtensionMarketplaceUrl, + documentdbVsCodeLocalQuickStartDeepLink, +} from '../app/services/externalLinks'; + +const html = renderToStaticMarkup(createElement(Home)); + +describe('homepage local quick start', () => { + it('preserves the public anchor and the terminal path as the default tab', () => { + expect(html).toContain('id="run-with-docker"'); + expect(html).toMatch( + /id="quickstart-tab-terminal" aria-selected="true" aria-controls="quickstart-panel-terminal" tabindex="0"/, + ); + expect(html).toMatch( + /id="quickstart-tab-vscode" aria-selected="false" aria-controls="quickstart-panel-vscode" tabindex="-1"/, + ); + expect(html).toMatch( + /id="quickstart-panel-terminal" aria-labelledby="quickstart-tab-terminal">/, + ); + expect(html).toMatch( + /id="quickstart-panel-vscode" aria-labelledby="quickstart-tab-vscode" hidden=""/, + ); + expect(html).toContain('docker run -dt --name documentdb'); + expect(html).toContain('-p 10260:10260'); + expect(html).toContain( + 'ghcr.io/documentdb/documentdb/documentdb-local:latest', + ); + }); + + it('labels the tabs by interface rather than implying one path avoids Docker', () => { + expect(html).toContain('>Terminal'); + expect(html).toContain('>VS Code'); + expect(html).toContain( + 'Both start the same DocumentDB Local container.', + ); + }); + + it('leads with installing the extension, then opening setup', () => { + expect(documentdbVsCodeExtensionMarketplaceUrl).toBe( + 'https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-documentdb', + ); + expect(documentdbVsCodeLocalQuickStartDeepLink).toBe( + 'vscode://ms-azuretools.vscode-documentdb/local', + ); + const marketplace = html.indexOf( + `href="${documentdbVsCodeExtensionMarketplaceUrl}"`, + ); + const setup = html.indexOf( + `href="${documentdbVsCodeLocalQuickStartDeepLink}"`, + ); + expect(marketplace).toBeGreaterThan(-1); + expect(setup).toBeGreaterThan(marketplace); + expect(html).toContain('Install the extension'); + expect(html).toContain('Open setup in VS Code'); + }); + + it('states that the VS Code path still needs Docker and changes nothing else', () => { + expect(html).toContain( + 'Needs Docker Desktop or Docker Engine on the same machine as VS Code.', + ); + expect(html).toContain('It never installs Docker or changes your system.'); + }); + + it('describes the setup outcome without pinning an extension version', () => { + expect(html).toContain( + 'Install the free DocumentDB for VS Code extension from the Marketplace.', + ); + expect(html).toContain( + 'The wizard starts DocumentDB Local with defaults you can review.', + ); + expect(html).toContain( + 'You get a container on port 10260 with generated credentials.', + ); + // A pinned minimum version on the homepage rots; the guide carries it instead. + expect(html).not.toContain('0.10.1'); + }); + + it('gives each path a concrete first query and a full guide', () => { + expect(html).toContain( + 'Connect on port 10260 with mongosh, any MongoDB driver, or your app.', + ); + expect(html).toContain('Run your first query.'); + expect(html).toContain('href="/docs/getting-started/docker"'); + expect(html).toContain('Full Docker guide'); + }); + + it('provides a Command Palette fallback that names the likely cause', () => { + expect(html).toContain( + 'If nothing happens, check that the extension is installed and up to', + ); + expect(html).toContain('DocumentDB: Set up DocumentDB Local'); + expect(html).toContain('from the Command Palette.'); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..21215b0 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + oxc: { + jsx: { + runtime: 'automatic', + }, + }, +});