Skip to content

Commit 963f846

Browse files
zohuyhieuzo03hieu.h.nguyencursoragent
authored
fix(theme): align CodeSnippets outer tab with Docusaurus storage slot (#1464)
* fix(theme): read code-samples tab from Docusaurus storage slot CodeSnippets used raw localStorage key docusaurus.tab.code-samples, but Docusaurus Tabs persist groupId with a namespaced key via createStorageSlot. Align reads and default tab value; prefer curl when no persisted match. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(theme): use useStorageSlot for code-samples tab persistence Replace createStorageSlot + window guard with Docusaurus useStorageSlot for SSR-safe reads and storage reactivity. Memoize mergedLangs and sync language state when persisted tab or language list changes. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(theme): simplify default language selection in CodeSnippets Update the default language selection logic in CodeSnippets to always return the first language in the list, removing the preference for "curl" when no match is found. This change streamlines the behavior and ensures a consistent fallback. --------- Co-authored-by: hieu.h.nguyen <hieu.h.nguyen@opswat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f68cf21 commit 963f846

1 file changed

Lines changed: 50 additions & 26 deletions

File tree

  • packages/docusaurus-theme-openapi-docs/src/theme/ApiExplorer/CodeSnippets

packages/docusaurus-theme-openapi-docs/src/theme/ApiExplorer/CodeSnippets/index.tsx

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
* LICENSE file in the root directory of this source tree.
66
* ========================================================================== */
77

8-
import React, { useState, useEffect } from "react";
8+
import React, { useEffect, useMemo, useState } from "react";
99

10+
import { useStorageSlot } from "@docusaurus/theme-common";
1011
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
1112
import ApiCodeBlock from "@theme/ApiExplorer/ApiCodeBlock";
1213
import buildPostmanRequest from "@theme/ApiExplorer/buildPostmanRequest";
1314
import CodeTabs from "@theme/ApiExplorer/CodeTabs";
1415
import { useResolvedEncoding } from "@theme/ApiExplorer/EncodingSelection/useResolvedEncoding";
1516
import { useTypedSelector } from "@theme/ApiItem/hooks";
17+
import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
1618
import cloneDeep from "lodash/cloneDeep";
1719
import codegen from "postman-code-generators";
1820
import * as sdk from "postman-collection";
1921

20-
import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
21-
2222
import { CodeSample, Language } from "./code-snippets-types";
2323
import {
2424
getCodeSampleSourceFromLanguage,
@@ -44,6 +44,21 @@ function CodeTab({ children, hidden, className }: any): React.JSX.Element {
4444
);
4545
}
4646

47+
/** Align with Docusaurus `<Tabs groupId="code-samples">` persisted tab value. */
48+
function resolveOuterLanguageFromPersistedTab(
49+
persistedTab: string | null,
50+
langs: Language[]
51+
): Language {
52+
if (langs.length === 1) {
53+
return langs[0];
54+
}
55+
const matched = langs.find((lang) => lang.language === persistedTab);
56+
if (matched) {
57+
return matched;
58+
}
59+
return langs[0];
60+
}
61+
4762
function CodeSnippets({
4863
postman,
4964
codeSamples,
@@ -126,34 +141,43 @@ function CodeSnippets({
126141
(siteConfig?.themeConfig?.languageTabs as Language[] | undefined) ??
127142
languageSet;
128143

129-
// Filter languageSet by user-defined langs
130-
const filteredLanguageSet = languageSet.filter((ls) => {
131-
return userDefinedLanguageSet?.some((lang) => {
132-
return lang.language === ls.language;
144+
const mergedLangs = useMemo(() => {
145+
const filteredLanguageSet = languageSet.filter((ls) => {
146+
return userDefinedLanguageSet?.some((lang) => {
147+
return lang.language === ls.language;
148+
});
133149
});
134-
});
150+
return mergeCodeSampleLanguage(
151+
mergeArraysbyLanguage(userDefinedLanguageSet, filteredLanguageSet),
152+
codeSamples
153+
);
154+
}, [userDefinedLanguageSet, codeSamples]);
135155

136-
// Merge user-defined langs into languageSet
137-
const mergedLangs = mergeCodeSampleLanguage(
138-
mergeArraysbyLanguage(userDefinedLanguageSet, filteredLanguageSet),
139-
codeSamples
140-
);
156+
const [persistedOuterTab] = useStorageSlot("docusaurus.tab.code-samples");
141157

142-
// Read defaultLang from localStorage
143-
const defaultLang: Language[] = mergedLangs.filter(
144-
(lang) =>
145-
lang.language === localStorage.getItem("docusaurus.tab.code-samples")
158+
const initialOuterLanguage = useMemo(
159+
() => resolveOuterLanguageFromPersistedTab(persistedOuterTab, mergedLangs),
160+
[persistedOuterTab, mergedLangs]
146161
);
162+
147163
const [selectedVariant, setSelectedVariant] = useState<string | undefined>();
148164
const [selectedSample, setSelectedSample] = useState<string | undefined>();
149-
const [language, setLanguage] = useState(() => {
150-
// Return first index if only 1 user-defined language exists
151-
if (mergedLangs.length === 1) {
152-
return mergedLangs[0];
153-
}
154-
// Fall back to language in localStorage or first user-defined language
155-
return defaultLang[0] ?? mergedLangs[0];
156-
});
165+
const [language, setLanguage] = useState(() =>
166+
resolveOuterLanguageFromPersistedTab(persistedOuterTab, mergedLangs)
167+
);
168+
169+
useEffect(() => {
170+
const next = resolveOuterLanguageFromPersistedTab(
171+
persistedOuterTab,
172+
mergedLangs
173+
);
174+
setLanguage((prev) => {
175+
if (prev?.language !== next.language) {
176+
return next;
177+
}
178+
return mergedLangs.find((l) => l.language === next.language) ?? next;
179+
});
180+
}, [persistedOuterTab, mergedLangs]);
157181
const [codeText, setCodeText] = useState<string>("");
158182
const [codeSampleCodeText, setCodeSampleCodeText] = useState<string>(() =>
159183
getCodeSampleSourceFromLanguage(language)
@@ -273,7 +297,7 @@ function CodeSnippets({
273297
setSelectedSample: setSelectedSample,
274298
}}
275299
languageSet={mergedLangs}
276-
defaultValue={defaultLang[0]?.language ?? mergedLangs[0].language}
300+
defaultValue={initialOuterLanguage.language}
277301
lazy
278302
>
279303
{mergedLangs.map((lang) => {

0 commit comments

Comments
 (0)