![Overview of the getting started journey rocket](/getting-started/getting-started_getting-started_journey-overview.webp)
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 99402a88e4..21372f184f 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -8,6 +8,7 @@ const redirects = require("./redirects");
const ArchivedVersions = require("./archiveVersions.json");
const { pluginPacksAndIntegrationsData } = require("./plugins/packs-integrations");
const { pluginImportFontAwesomeIcons } = require("./plugins/font-awesome");
+import path from "path";
/** @type {import('@docusaurus/types').Config} */
const config = {
@@ -412,3 +413,13 @@ const config = {
},
};
module.exports = config;
+
+export default function (context, options) {
+ return {
+ name: "@docusaurus/plugin-content-docs",
+ getPathsToWatch() {
+ const contentPath = path.resolve(context.siteDir, options.path);
+ return [`${contentPath}/_partials/*/*.{mdx}`];
+ },
+ };
+}
diff --git a/package.json b/package.json
index 991b067230..104865aed8 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start --host 0.0.0.0 --port 9000",
- "build": "npm run generate-api-docs && docusaurus build",
+ "build": "npm run generate-api-docs && npm run generate-partials && docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
@@ -18,6 +18,7 @@
"generate-api-docs": "npm run run-api-parser && docusaurus gen-api-docs palette && docusaurus gen-api-docs emc",
"clean-api-docs": "docusaurus clean-api-docs palette && docusaurus clean-api-docs emc",
"run-api-parser": "node utils/api-parser/index.js",
+ "generate-partials": "./scripts/generate-partials.sh",
"lint": "eslint . --ext .js,.ts,.jsx,.tsx",
"lint:fix": "npm run lint -- --fix",
"format": "prettier --write \"**/*.{js,jsx,json,ts,tsx,md,mdx,css}\"",
diff --git a/scripts/generate-partials.sh b/scripts/generate-partials.sh
new file mode 100755
index 0000000000..a2e14dc9ee
--- /dev/null
+++ b/scripts/generate-partials.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Enable error handling
+set -e
+
+echo "Starting generation of _partials/index.ts."
+
+# # Remove the index.ts file if it exists already.
+rm -f _partials/index.ts
+
+# Make the _partials folder an index.ts file
+mkdir -p _partials
+touch _partials/index.ts
+
+# Create the file and add the generated warning.
+echo "// This file is generated. DO NOT EDIT!" >> _partials/index.ts
+
+# Find all the MDX files recursively in the _partials folder.
+# Loop through each file.
+find _partials -name "*.mdx" -print0 | while read -d $'\0' path
+do
+ module_name=$(basename ${path} .mdx | tr -d '_' | tr -d '-')
+ echo "export * as ${module_name}${RANDOM} from '@site/${path}';" >> _partials/index.ts
+done
+
+echo "Completed generation of _partials/index.ts."
diff --git a/src/components/PartialsComponent/PartialsComponent.test.tsx b/src/components/PartialsComponent/PartialsComponent.test.tsx
new file mode 100644
index 0000000000..ca1588ab16
--- /dev/null
+++ b/src/components/PartialsComponent/PartialsComponent.test.tsx
@@ -0,0 +1,41 @@
+import React, { FunctionComponent } from "react";
+import { render, screen } from "@testing-library/react";
+
+let category = "testCat1";
+let name = "nameCat1";
+let propValue = "testValue1";
+
+const Partial: React.FunctionComponent<{}> = ({}) => (
+
+
{category}
+
{name}
+
{propValue}
+
+);
+
+jest.mock("./PartialsImporter", () => {
+ return jest.fn(() => {
+ const mapKey = category.concat("#").concat(name);
+ const pmap: PartialsMap = {};
+ pmap[mapKey] = Partial as FunctionComponent;
+ return pmap;
+ });
+});
+
+import PartialsComponent from "./PartialsComponent";
+import { PartialsMap } from "./PartialsImporter";
+
+describe("Partials Component", () => {
+ it("partial exists", () => {
+ render(
);
+ expect(screen.getByText(category)).toBeInTheDocument();
+ expect(screen.getByText(name)).toBeInTheDocument();
+ expect(screen.getByText(propValue)).toBeInTheDocument();
+ });
+
+ it("partial does not exist", () => {
+ expect(() => render(
)).toThrow(
+ "No partial found for name unknownName in category unknownCat."
+ );
+ });
+});
diff --git a/src/components/PartialsComponent/PartialsComponent.tsx b/src/components/PartialsComponent/PartialsComponent.tsx
new file mode 100644
index 0000000000..3c5b180109
--- /dev/null
+++ b/src/components/PartialsComponent/PartialsComponent.tsx
@@ -0,0 +1,30 @@
+import React from "react";
+import ImportPartials from "./PartialsImporter";
+
+interface ComponentProperties {
+ [key: string]: string;
+}
+
+const AllPartials = ImportPartials();
+
+export default function PartialsComponent(details: ComponentProperties): React.ReactElement {
+ const mapKey = details.category.concat("#").concat(details.name);
+
+ if (!AllPartials[mapKey]) {
+ throw new Error(
+ "No partial found for name ".concat(details.name).concat(" in category ").concat(details.category).concat(".")
+ );
+ }
+
+ // Map elements to object properties
+ const propAttribute: { [key: string]: string } = {};
+ for (const key in details) {
+ // Don't send category and name to the partial
+ if (key == "category" || key == "name") {
+ continue;
+ }
+ propAttribute[key] = details[key];
+ }
+
+ return React.createElement(AllPartials[mapKey], propAttribute);
+}
diff --git a/src/components/PartialsComponent/PartialsImporter.tsx b/src/components/PartialsComponent/PartialsImporter.tsx
new file mode 100644
index 0000000000..b9441d67a7
--- /dev/null
+++ b/src/components/PartialsComponent/PartialsImporter.tsx
@@ -0,0 +1,51 @@
+import { FunctionComponent } from "react";
+// Import all the partials as one module.
+import * as PartialModules from "@site/_partials";
+
+export interface PartialsMap {
+ [key: string]: FunctionComponent;
+}
+
+interface Modules {
+ [key: string]: Module;
+}
+
+interface Module {
+ frontMatter: {
+ partial_category: string;
+ partial_name: string;
+ };
+ default: FunctionComponent;
+}
+
+export default function ImportPartials(): PartialsMap {
+ const pmap: PartialsMap = {};
+ const allPartialModules: Modules = PartialModules;
+
+ // The keys are the names of each exported module in _partials/index.ts
+ const partialKeys: string[] = Object.keys(allPartialModules);
+ partialKeys.map(function (pkey) {
+ const currentPartial: Module = allPartialModules[pkey];
+ const catFrontMatter = currentPartial.frontMatter.partial_category;
+ const nameFrontMatter = currentPartial.frontMatter.partial_name;
+
+ if (!catFrontMatter || !nameFrontMatter) {
+ throw new Error("Please specify partial_category and partial_name for ".concat(pkey).concat("."));
+ }
+
+ const mapKey = catFrontMatter.concat("#").concat(nameFrontMatter);
+ if (pmap[mapKey]) {
+ throw new Error(
+ "Duplicate partial defined for name "
+ .concat(nameFrontMatter)
+ .concat(" in category ")
+ .concat(catFrontMatter)
+ .concat(".")
+ );
+ }
+
+ pmap[mapKey] = currentPartial.default;
+ });
+
+ return pmap;
+}
diff --git a/src/components/PartialsComponent/index.ts b/src/components/PartialsComponent/index.ts
new file mode 100644
index 0000000000..2726299b81
--- /dev/null
+++ b/src/components/PartialsComponent/index.ts
@@ -0,0 +1,3 @@
+import PartialsComponent from "./PartialsComponent";
+
+export default PartialsComponent;
diff --git a/src/components/VersionedLink/CheckVersion.tsx b/src/components/VersionedLink/CheckVersion.tsx
new file mode 100644
index 0000000000..0f98918398
--- /dev/null
+++ b/src/components/VersionedLink/CheckVersion.tsx
@@ -0,0 +1,14 @@
+import { useActivePluginAndVersion } from "@docusaurus/plugin-content-docs/client";
+
+export default function GetVersionPath() {
+ const activePlugin = useActivePluginAndVersion();
+ if (
+ activePlugin != undefined &&
+ activePlugin.activeVersion != undefined &&
+ activePlugin.activeVersion.name != "current"
+ ) {
+ return activePlugin.activeVersion.path;
+ }
+
+ return "";
+}
diff --git a/src/components/VersionedLink/VersionedLink.test.tsx b/src/components/VersionedLink/VersionedLink.test.tsx
new file mode 100644
index 0000000000..61b824cd5b
--- /dev/null
+++ b/src/components/VersionedLink/VersionedLink.test.tsx
@@ -0,0 +1,59 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import VersionedLink from "./VersionedLink";
+
+let prevVersionPath = "";
+jest.mock("./CheckVersion", () => {
+ return jest.fn(() => {
+ return prevVersionPath;
+ });
+});
+
+describe("Versioned link", () => {
+ it("latest version", () => {
+ prevVersionPath = "";
+ const text = "Test link";
+ const url = "/path/url";
+ render(
);
+ const link = screen.getByText(text).getAttribute("href");
+ expect(link).not.toBeNull();
+ expect(link).toBe(url);
+ });
+
+ it("previous version", () => {
+ prevVersionPath = "/v4.3.1";
+ const text = "Test link";
+ const url = "/path/url";
+ render(
);
+ const link = screen.getByText(text).getAttribute("href");
+ expect(link).not.toBeNull();
+ expect(link).toBe(prevVersionPath.concat(url));
+ });
+
+ it("url with back dots", () => {
+ prevVersionPath = "";
+ const text = "Test link";
+ const url = "../path/url";
+ expect(() => render(
)).toThrow(
+ "Versioned links should provide the path of the destination URL from root, without any `./` or `..` references."
+ );
+ });
+
+ it("url with back relative reference", () => {
+ prevVersionPath = "";
+ const text = "Test link";
+ const url = "./path/url";
+ expect(() => render(
)).toThrow(
+ "Versioned links should provide the path of the destination URL from root, without any `./` or `..` references."
+ );
+ });
+
+ it("url with external domain", () => {
+ prevVersionPath = "";
+ const text = "Test link";
+ const url = "https://google.com";
+ expect(() => render(
)).toThrow(
+ "Versioned links should not be used for external URLs. Please use the default markdown syntax instead."
+ );
+ });
+});
diff --git a/src/components/VersionedLink/VersionedLink.tsx b/src/components/VersionedLink/VersionedLink.tsx
new file mode 100644
index 0000000000..ee67c639ed
--- /dev/null
+++ b/src/components/VersionedLink/VersionedLink.tsx
@@ -0,0 +1,26 @@
+import React from "react";
+import GetVersionPath from "./CheckVersion";
+
+interface ComponentProperties {
+ [key: string]: string;
+}
+
+export default function VersionedLink(props: ComponentProperties) {
+ const path = GetVersionPath();
+ if (props.url.includes("..") || props.url.includes("./")) {
+ throw new Error(
+ "Versioned links should provide the path of the destination URL from root, without any `./` or `..` references."
+ );
+ }
+ if (props.url.includes("https") || props.url.includes("http")) {
+ throw new Error(
+ "Versioned links should not be used for external URLs. Please use the default markdown syntax instead."
+ );
+ }
+ if (path != "") {
+ const versionedURL = path.concat(props.url);
+ return
{props.text};
+ }
+
+ return
{props.text};
+}
diff --git a/src/components/VersionedLink/index.ts b/src/components/VersionedLink/index.ts
new file mode 100644
index 0000000000..d6c9d7b137
--- /dev/null
+++ b/src/components/VersionedLink/index.ts
@@ -0,0 +1,3 @@
+import VersionedLink from "./VersionedLink";
+
+export default VersionedLink;
diff --git a/src/theme/MDXComponents/MDXComponents.ts b/src/theme/MDXComponents/MDXComponents.ts
index a0b9996943..cf7717bd5a 100644
--- a/src/theme/MDXComponents/MDXComponents.ts
+++ b/src/theme/MDXComponents/MDXComponents.ts
@@ -12,6 +12,8 @@ import TOCInline from "@theme/TOCInline";
import { TechnicalPreviewReleaseNote as TpBadge } from "@site/src/components/Badges";
import SimpleCardGrid from "@site/src/components/SimpleCardGrid/index";
import ReleaseNotesVersions from "@site/src/components/ReleaseNotesVersions/index";
+import PartialsComponent from "@site/src/components/PartialsComponent";
+import VersionedLink from "@site/src/components/VersionedLink";
export default {
...MDXComponents,
@@ -28,4 +30,6 @@ export default {
TpBadge,
SimpleCardGrid,
ReleaseNotesVersions,
+ PartialsComponent,
+ VersionedLink,
};
diff --git a/tsconfig.json b/tsconfig.json
index 3eb9b67a6f..5d490025fa 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,10 +1,10 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@tsconfig/docusaurus/tsconfig.json",
- "include": ["src", "declarations.d.ts", "utils"],
+ "include": ["src", "declarations.d.ts", "utils", "**/*.mdx",],
"exclude": ["src/deprecated"],
"compilerOptions": {
- "types": ["node", "jest", "@testing-library/jest-dom"],
+ "types": ["node", "jest", "@testing-library/jest-dom", "mdx"],
"esModuleInterop": true,
"target": "es6",
"module": "Node16",