diff --git a/gatsby/__tests__/toc.test.ts b/gatsby/__tests__/toc.test.ts new file mode 100644 index 00000000..db3e9544 --- /dev/null +++ b/gatsby/__tests__/toc.test.ts @@ -0,0 +1,102 @@ +import { mdxAstToToc } from "../toc"; + +describe("mdxAstToToc tag query parsing", () => { + it("does not emit a bogus ?undefined query when the image URL has no query string", () => { + const toc = mdxAstToToc( + [ + { + type: "list", + children: [ + { + type: "listItem", + children: [ + { + type: "paragraph", + children: [ + { type: "text", value: "Data Service" }, + { + type: "image", + alt: "PREVIEW", + url: "/media/tidb-cloud/blank_transparent_placeholder.png", + }, + ], + }, + ], + }, + ], + }, + ] as any, + "en/tidbcloud/master/TOC" + ); + + expect(toc[0].tag).toEqual({ + value: "PREVIEW", + query: undefined, + }); + }); + + it("does not create a tag when the TOC image has no alt text", () => { + const toc = mdxAstToToc( + [ + { + type: "list", + children: [ + { + type: "listItem", + children: [ + { + type: "paragraph", + children: [ + { type: "text", value: "Data Service" }, + { + type: "image", + alt: null, + url: "/media/tidb-cloud/blank_transparent_placeholder.png", + }, + ], + }, + ], + }, + ], + }, + ] as any, + "en/tidbcloud/master/TOC" + ); + + expect(toc[0].tag).toBeUndefined(); + }); + + it("keeps tag query params when the image URL includes them", () => { + const toc = mdxAstToToc( + [ + { + type: "list", + children: [ + { + type: "listItem", + children: [ + { + type: "paragraph", + children: [ + { type: "text", value: "Beta Feature" }, + { + type: "image", + alt: "BETA", + url: "/media/tidb-cloud/blank_transparent_placeholder.png?color=%232d9cd2", + }, + ], + }, + ], + }, + ], + }, + ] as any, + "en/tidbcloud/master/TOC" + ); + + expect(toc[0].tag).toEqual({ + value: "BETA", + query: "?color=%232d9cd2", + }); + }); +}); diff --git a/gatsby/toc.ts b/gatsby/toc.ts index fef4f3d3..c8c71f02 100644 --- a/gatsby/toc.ts +++ b/gatsby/toc.ts @@ -2,6 +2,7 @@ import { ListItem, List, Link, + Image, Paragraph, Text, Content, @@ -157,11 +158,14 @@ function getContentFromLink( const child = content.children[0] as Link | Text; // use `image` as tag - const image = content.children.find((n) => n.type === "image"); - const tag = image && { - value: image.alt!, - query: `?${image.url.split("?")[1]}`, - }; + const image = content.children.find((n): n is Image => n.type === "image"); + const imageQuery = image?.url.split("?")[1]; + const tag = image?.alt + ? { + value: image.alt, + query: imageQuery ? `?${imageQuery}` : undefined, + } + : undefined; if (child.type === "link") { if (child.children.length === 0) { diff --git a/locale/en/translation.json b/locale/en/translation.json index df468a63..0d64a522 100644 --- a/locale/en/translation.json +++ b/locale/en/translation.json @@ -43,8 +43,7 @@ "tidbOperatorReleases": "TiDB Operator Releases", "tiupReleases": "TiUP Releases", "badge": { - "preview": "Preview", - "beta": "Beta" + "preview": "Preview" }, "appdev": "App Dev", "asktug": "Forum", diff --git a/locale/ja/translation.json b/locale/ja/translation.json index d716614d..b536c5d9 100644 --- a/locale/ja/translation.json +++ b/locale/ja/translation.json @@ -43,8 +43,7 @@ "tidbOperatorReleases": "TiDB Operator リリース", "tiupReleases": "TiUP リリース", "badge": { - "preview": "Preview", - "beta": "Beta" + "preview": "Preview" }, "appdev": "アプリ開発", "asktug": "Forum", diff --git a/locale/zh/translation.json b/locale/zh/translation.json index a5e09239..bc6794a1 100644 --- a/locale/zh/translation.json +++ b/locale/zh/translation.json @@ -41,8 +41,7 @@ "tidbOperatorReleases": "TiDB Operator 版本发布记录", "tiupReleases": "TiUP 版本发布记录", "badge": { - "preview": "Preview", - "beta": "Beta" + "preview": "Preview" }, "appdev": "开发指南", "asktug": "社区", diff --git a/src/components/Badge/PreviewBadge.tsx b/src/components/Badge/PreviewBadge.tsx new file mode 100644 index 00000000..93470dfd --- /dev/null +++ b/src/components/Badge/PreviewBadge.tsx @@ -0,0 +1,29 @@ +import Chip from "@mui/material/Chip"; +import { useTheme } from "@mui/material/styles"; + +const PreviewBadge = (props: { label: string }) => { + const theme = useTheme(); + + return ( + + ); +}; + +export default PreviewBadge; diff --git a/src/components/Layout/Header/HeaderNavConfigData.tsx b/src/components/Layout/Header/HeaderNavConfigData.tsx index 80f3beae..82701b8c 100644 --- a/src/components/Layout/Header/HeaderNavConfigData.tsx +++ b/src/components/Layout/Header/HeaderNavConfigData.tsx @@ -2,54 +2,11 @@ import { NavConfig } from "./HeaderNavConfigType"; import { CLOUD_MODE_KEY } from "shared/useCloudPlan"; import { CloudPlan, TOCNamespace } from "shared/interface"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; -import Chip from "@mui/material/Chip"; -import { useTheme } from "@mui/material/styles"; +import PreviewBadge from "components/Badge/PreviewBadge"; import TiDBCloudIcon from "media/icons/cloud-03.svg"; import TiDBIcon from "media/icons/layers-three-01.svg"; -const PreviewBadge = (props: { label: string }) => { - const theme = useTheme(); - return ( - - ); -}; - -const BetaTagBadge = (props: { label: string }) => ( - -); - /** * Default navigation configuration */ @@ -167,7 +124,7 @@ const getDefaultNavConfig = ( leftNavLabel: ( <> {t("navbar.tidbForAI")} - + ), to: "/ai", diff --git a/src/components/Layout/LeftNav/LeftNavTree.tsx b/src/components/Layout/LeftNav/LeftNavTree.tsx index 5412d370..0766daac 100644 --- a/src/components/Layout/LeftNav/LeftNavTree.tsx +++ b/src/components/Layout/LeftNav/LeftNavTree.tsx @@ -7,9 +7,11 @@ import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; import Divider from "@mui/material/Divider"; import { useTheme } from "@mui/material/styles"; +import { useTranslation } from "gatsby-plugin-react-i18next"; import { RepoNavLink, RepoNav } from "shared/interface"; import LinkComponent from "components/Link"; +import PreviewBadge from "components/Badge/PreviewBadge"; import { scrollToElementIfInView } from "shared/utils"; import { alpha, Chip } from "@mui/material"; @@ -190,6 +192,8 @@ export default function ControlledTreeView(props: { }); const theme = useTheme(); + const { t } = useTranslation(); + const previewBadgeLabel = t("navbar.badge.preview"); const [disableTransition, setDisableTransition] = React.useState(false); const previousUrlRef = React.useRef(null); @@ -313,7 +317,7 @@ export default function ControlledTreeView(props: { ) : ( )} - {generateItemLabel(item)} + {generateItemLabel(item, previewBadgeLabel)} ); }; @@ -389,10 +393,22 @@ export default function ControlledTreeView(props: { ); } -const generateItemLabel = ({ content: contents, tag }: RepoNavLink) => { - const tagQuery = new URLSearchParams(tag?.query); - const tagColor = tagQuery.get("color"); - const tagColor02 = tagColor && alpha(tagColor, 0.2); +const generateItemLabel = ( + { content: contents, tag }: RepoNavLink, + previewBadgeLabel: string +) => { + const normalizedTagValue = tag?.value?.trim().toUpperCase(); + const isPreviewTag = normalizedTagValue === "PREVIEW"; + let tagColor: string | null = null; + let tagColor02: string | null = null; + + // PREVIEW intentionally ignores source color overrides to match shared badges. + if (tag && !isPreviewTag) { + const tagQuery = new URLSearchParams(tag.query ?? ""); + tagColor = tagQuery.get("color"); + tagColor02 = tagColor ? alpha(tagColor, 0.2) : null; + } + return ( { ); })} - {tag && ( + {tag && isPreviewTag ? ( + + ) : tag ? ( { fontWeight: 500, }} /> - )} + ) : null} ); };