Skip to content

Commit

Permalink
Add support to x-codeSamples (#697)
Browse files Browse the repository at this point in the history
* add support to x-codeSamples

* fix select variant update

* remove unused cast

* split code samples and variants
  • Loading branch information
lucasassisrosa authored Jan 18, 2024
1 parent ef8ab66 commit 8a42186
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 1,311 deletions.
1 change: 1 addition & 0 deletions demo/examples/petstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ paths:
Console.WriteLine(response.getRawResponse());
}
- lang: PHP
label: Custom
source: |
$form = new \PetStore\Entities\Pet();
$form->setPetType("Dog");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* ============================================================================
* Copyright (c) Palo Alto Networks
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* ========================================================================== */

// https://github.com/github-linguist/linguist/blob/master/lib/linguist/popular.yml
export type CodeSampleLanguage =
| "C"
| "C#"
| "C++"
| "CoffeeScript"
| "CSS"
| "Dart"
| "DM"
| "Elixir"
| "Go"
| "Groovy"
| "HTML"
| "Java"
| "JavaScript"
| "Kotlin"
| "Objective-C"
| "Perl"
| "PHP"
| "PowerShell"
| "Python"
| "Ruby"
| "Rust"
| "Scala"
| "Shell"
| "Swift"
| "TypeScript";

export interface Language {
highlight: string;
language: string;
codeSampleLanguage: CodeSampleLanguage;
logoClass: string;
variant: string;
variants: string[];
options?: { [key: string]: boolean };
sample?: string;
samples?: string[];
samplesSources?: string[];
samplesLabels?: string[];
}

// https://redocly.com/docs/api-reference-docs/specification-extensions/x-code-samples
export interface CodeSample {
source: string;
lang: CodeSampleLanguage;
label?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,14 @@ import CodeTabs from "@theme/ApiExplorer/CodeTabs";
import { useTypedSelector } from "@theme/ApiItem/hooks";
import merge from "lodash/merge";

export interface Language {
highlight: string;
language: string;
logoClass: string;
variant: string;
variants: string[];
options: { [key: string]: boolean };
source?: string;
}
import { CodeSample, Language } from "./code-snippets-types";
import { mergeCodeSampleLanguage } from "./languages";

export const languageSet: Language[] = [
{
highlight: "bash",
language: "curl",
codeSampleLanguage: "Shell",
logoClass: "bash",
options: {
longFormat: false,
Expand All @@ -42,6 +36,7 @@ export const languageSet: Language[] = [
{
highlight: "python",
language: "python",
codeSampleLanguage: "Python",
logoClass: "python",
options: {
followRedirect: true,
Expand All @@ -53,6 +48,7 @@ export const languageSet: Language[] = [
{
highlight: "go",
language: "go",
codeSampleLanguage: "Go",
logoClass: "go",
options: {
followRedirect: true,
Expand All @@ -64,6 +60,7 @@ export const languageSet: Language[] = [
{
highlight: "javascript",
language: "nodejs",
codeSampleLanguage: "JavaScript",
logoClass: "nodejs",
options: {
ES6_enabled: true,
Expand All @@ -76,6 +73,7 @@ export const languageSet: Language[] = [
{
highlight: "ruby",
language: "ruby",
codeSampleLanguage: "Ruby",
logoClass: "ruby",
options: {
followRedirect: true,
Expand All @@ -87,6 +85,7 @@ export const languageSet: Language[] = [
{
highlight: "csharp",
language: "csharp",
codeSampleLanguage: "C#",
logoClass: "csharp",
options: {
followRedirect: true,
Expand All @@ -98,6 +97,7 @@ export const languageSet: Language[] = [
{
highlight: "php",
language: "php",
codeSampleLanguage: "PHP",
logoClass: "php",
options: {
followRedirect: true,
Expand All @@ -109,6 +109,7 @@ export const languageSet: Language[] = [
{
highlight: "java",
language: "java",
codeSampleLanguage: "Java",
logoClass: "java",
options: {
followRedirect: true,
Expand All @@ -120,6 +121,7 @@ export const languageSet: Language[] = [
{
highlight: "powershell",
language: "powershell",
codeSampleLanguage: "PowerShell",
logoClass: "powershell",
options: {
followRedirect: true,
Expand All @@ -132,10 +134,10 @@ export const languageSet: Language[] = [

export interface Props {
postman: sdk.Request;
codeSamples: any; // TODO: Type this...
codeSamples: CodeSample[];
}

function CodeTab({ children, hidden, className, onClick }: any): JSX.Element {
function CodeTab({ children, hidden, className }: any): JSX.Element {
return (
<div role="tabpanel" className={className} {...{ hidden }}>
{children}
Expand Down Expand Up @@ -165,7 +167,6 @@ function CodeSnippets({ postman, codeSamples }: Props) {
const langs = [
...((siteConfig?.themeConfig?.languageTabs as Language[] | undefined) ??
languageSet),
...codeSamples,
];

// Filter languageSet by user-defined langs
Expand All @@ -176,14 +177,18 @@ function CodeSnippets({ postman, codeSamples }: Props) {
});

// Merge user-defined langs into languageSet
const mergedLangs = merge(filteredLanguageSet, langs);
const mergedLangs = mergeCodeSampleLanguage(
merge(filteredLanguageSet, langs),
codeSamples
);

// Read defaultLang from localStorage
const defaultLang: Language[] = mergedLangs.filter(
(lang) =>
lang.language === localStorage.getItem("docusaurus.tab.code-samples")
);
const [selectedVariant, setSelectedVariant] = useState();
const [selectedVariant, setSelectedVariant] = useState<string | undefined>();
const [selectedSample, setSelectedSample] = useState<string | undefined>();
const [language, setLanguage] = useState(() => {
// Return first index if only 1 user-defined language exists
if (mergedLangs.length === 1) {
Expand All @@ -192,9 +197,23 @@ function CodeSnippets({ postman, codeSamples }: Props) {
// Fall back to language in localStorage or first user-defined language
return defaultLang[0] ?? mergedLangs[0];
});
const [codeText, setCodeText] = useState("");
const [codeText, setCodeText] = useState<string>("");
const [codeSampleCodeText, setCodeSampleCodeText] = useState<string>("");

useEffect(() => {
// initial active language is custom code sample
if (
language &&
language.sample &&
language.samples &&
language.samplesSources
) {
const sampleIndex = language.samples.findIndex(
(smp) => smp === language.sample
);
setCodeSampleCodeText(language.samplesSources[sampleIndex]);
}

if (language && !!language.options) {
const postmanRequest = buildPostmanRequest(postman, {
queryParams,
Expand All @@ -219,8 +238,6 @@ function CodeSnippets({ postman, codeSamples }: Props) {
setCodeText(snippet);
}
);
} else if (language && !!language.source) {
setCodeText(language.source);
} else if (language && !language.options) {
const langSource = mergedLangs.filter(
(lang) => lang.language === language.language
Expand Down Expand Up @@ -271,8 +288,8 @@ function CodeSnippets({ postman, codeSamples }: Props) {
auth,
mergedLangs,
]);

useEffect(() => {
// no dependencies was intentionlly set for this particular hook. it's safe as long as if conditions are set
useEffect(function onSelectedVariantUpdate() {
if (selectedVariant && selectedVariant !== language.variant) {
const postmanRequest = buildPostmanRequest(postman, {
queryParams,
Expand Down Expand Up @@ -300,6 +317,22 @@ function CodeSnippets({ postman, codeSamples }: Props) {
}
});

// no dependencies was intentionlly set for this particular hook. it's safe as long as if conditions are set
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(function onSelectedSampleUpdate() {
if (
language.samples &&
language.samplesSources &&
selectedSample &&
selectedSample !== language.sample
) {
const sampleIndex = language.samples.findIndex(
(smp) => smp === selectedSample
);
setCodeSampleCodeText(language.samplesSources[sampleIndex]);
}
});

if (language === undefined) {
return null;
}
Expand All @@ -324,6 +357,46 @@ function CodeSnippets({ postman, codeSamples }: Props) {
className: `openapi-tabs__code-item--${lang.logoClass}`,
}}
>
{lang.samples && (
<CodeTabs
className="openapi-tabs__code-container-inner"
action={{
setLanguage: setLanguage,
setSelectedSample: setSelectedSample,
}}
includeSample={true}
currentLanguage={lang.language}
defaultValue={selectedSample}
lazy
>
{lang.samples.map((sample, index) => {
return (
<CodeTab
value={sample}
label={
lang.samplesLabels
? lang.samplesLabels[index]
: sample
}
key={`${lang.language}-${lang.sample}`}
attributes={{
className: `openapi-tabs__code-item--sample`,
}}
>
{/* @ts-ignore */}
<ApiCodeBlock
language={lang.highlight}
className="openapi-explorer__code-block"
showLineNumbers={true}
>
{codeSampleCodeText}
</ApiCodeBlock>
</CodeTab>
);
})}
</CodeTabs>
)}

<CodeTabs
className="openapi-tabs__code-container-inner"
action={{
Expand All @@ -335,7 +408,7 @@ function CodeSnippets({ postman, codeSamples }: Props) {
defaultValue={selectedVariant}
lazy
>
{lang.variants.map((variant) => {
{lang.variants.map((variant, index) => {
return (
<CodeTab
value={variant.toLowerCase()}
Expand Down
Loading

0 comments on commit 8a42186

Please sign in to comment.