Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
551 changes: 551 additions & 0 deletions demo/examples/tests/examples.yaml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions packages/docusaurus-theme-openapi-docs/src/markdown/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* ========================================================================== */

import { translate } from "@docusaurus/Translate";

import { OPENAPI_SCHEMA_ITEM } from "../theme/translationIds";
import { SchemaObject } from "../types";

Expand Down Expand Up @@ -42,6 +43,10 @@ function prettyName(schema: SchemaObject, circular?: boolean) {
// return schema.type;
}

if (Array.isArray(schema.type)) {
return schema.type.join(" | ");
}

return schema.title ?? schema.type;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.openapi-examples {
> .openapi-tabs__schema-container {
margin: 0.4rem;

@layer docusaurus.infima {
> .margin-top--md {
margin: 0.2rem !important;
}
}
}
}
168 changes: 168 additions & 0 deletions packages/docusaurus-theme-openapi-docs/src/theme/Example/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/* ============================================================================
* 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.
* ========================================================================== */

import React from "react";

import { translate } from "@docusaurus/Translate";
import { ExampleObject } from "@theme/ParamsItem";
import SchemaTabs from "@theme/SchemaTabs";
import TabItem from "@theme/TabItem";
import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";

const EXAMPLE_CLASS_NAME = "openapi-example";
const EXAMPLES_CLASS_NAME = "openapi-examples";

type ExampleType = string;
type ExamplesType = Record<string, ExampleObject> | string[];

/**
* Example Component Props
*/
type ExampleProps = {
example?: ExampleType;
examples?: ExamplesType;
};

/**
* Example Component
*/
export const Example = ({ example, examples }: ExampleProps) => {
if (example !== undefined) {
return renderExample(example);
}
if (examples !== undefined) {
return renderExamples(examples);
}
return undefined;
};

/**
* Format example value
*
* @param example
* @returns
*/
const formatExample = (example: any) => {
if (typeof example === "object" && example !== null) {
return JSON.stringify(example);
}
return String(example);
};

const renderExample = (example: ExampleType) => {
return (
<div className={EXAMPLE_CLASS_NAME}>
<strong>
{translate({
id: OPENAPI_SCHEMA_ITEM.EXAMPLE,
message: "Example:",
})}{" "}
</strong>
<span>
<code>{formatExample(example)}</code>
</span>
</div>
);
};

const renderExamples = (examples: ExamplesType) => {
if (Array.isArray(examples)) {
return renderStringArrayExamples(examples);
}
return renderExamplesRecord(examples);
};

/**
* Render string examples
*
* @param examples
* @returns
*/
export function renderStringArrayExamples(examples: string[]) {
if (examples.length === 0) {
return undefined;
}
// If there's only one example, display it without tabs
if (examples.length === 1) {
return renderExample(examples[0]);
}

// Multiple examples - use tabs
const exampleEntries = examples.reduce(
(acc, example, index) => ({
...acc,
[`Example ${index + 1}`]: {
value: example,
},
}),
{} as Record<string, ExampleObject>
);
return renderExamplesRecord(exampleEntries);
}

export const renderExamplesRecord = (
examples: Record<string, ExampleObject>
) => {
const exampleEntries = Object.entries(examples);
// If there's only one example, display it without tabs
if (exampleEntries.length === 1) {
const firstExample = exampleEntries[0][1];
if (!firstExample) {
return undefined;
}
return renderExample(firstExample.value);
}

return (
<div className={EXAMPLES_CLASS_NAME}>
<strong>
{translate({
id: OPENAPI_SCHEMA_ITEM.EXAMPLES,
message: "Examples:",
})}
</strong>
<SchemaTabs>
{exampleEntries.map(([exampleName, exampleProperties]) =>
renderExampleObject(exampleName, exampleProperties)
)}
</SchemaTabs>
</div>
);
};

/**
* Render example object
*
* @param exampleName
* @param exampleProperties
* @returns
*/
const renderExampleObject = (
exampleName: string,
exampleProperties: ExampleObject
) => {
return (
// @ts-ignore
<TabItem value={exampleName} label={exampleName}>
{exampleProperties.summary && <p>{exampleProperties.summary}</p>}
{exampleProperties.description && (
<p>
<strong>
{translate({
id: OPENAPI_SCHEMA_ITEM.DESCRIPTION,
message: "Description:",
})}{" "}
</strong>
<span>{exampleProperties.description}</span>
</p>
)}
{exampleProperties.value !== undefined
? renderExample(exampleProperties.value)
: undefined}
</TabItem>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@
import React from "react";

import { translate } from "@docusaurus/Translate";

import { Example } from "@theme/Example";
import Markdown from "@theme/Markdown";
import SchemaTabs from "@theme/SchemaTabs";
import TabItem from "@theme/TabItem";
/* eslint-disable import/no-extraneous-dependencies*/
import clsx from "clsx";
import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
import clsx from "clsx";

import { getQualifierMessage, getSchemaName } from "../../markdown/schema";
import { guard, toString } from "../../markdown/utils";
import { guard } from "../../markdown/utils";

export interface ExampleObject {
summary?: string;
Expand All @@ -31,7 +29,7 @@ export interface Props {
param: {
description: string;
example: any;
examples: Record<string, ExampleObject>;
examples: Record<string, ExampleObject> | undefined;
name: string;
required: boolean;
deprecated: boolean;
Expand Down Expand Up @@ -64,19 +62,14 @@ ${enumDescriptions
};

function ParamsItem({ param, ...rest }: Props) {
const {
description,
example,
examples,
name,
required,
deprecated,
enumDescriptions,
} = param;
const { description, name, required, deprecated, enumDescriptions } = param;

let schema = param.schema;
let defaultValue: string | undefined;

let examples = param.examples ?? (schema?.examples as any[] | undefined);
let example = param.example ?? schema?.example;

if (!schema) {
schema = { type: "any" };
}
Expand Down Expand Up @@ -162,60 +155,6 @@ function ParamsItem({ param, ...rest }: Props) {
return undefined;
}

const renderExample = guard(toString(example), (example) => (
<div>
<strong>
{translate({
id: OPENAPI_SCHEMA_ITEM.EXAMPLE,
message: "Example:",
})}{" "}
</strong>
{example}
</div>
));

const renderExamples = guard(examples, (examples) => {
const exampleEntries = Object.entries(examples);
return (
<>
<strong>
{translate({
id: OPENAPI_SCHEMA_ITEM.EXAMPLES,
message: "Examples:",
})}
</strong>
<SchemaTabs>
{exampleEntries.map(([exampleName, exampleProperties]) => (
// @ts-ignore
<TabItem value={exampleName} label={exampleName}>
{exampleProperties.summary && <p>{exampleProperties.summary}</p>}
{exampleProperties.description && (
<p>
<strong>
{translate({
id: OPENAPI_SCHEMA_ITEM.DESCRIPTION,
message: "Description:",
})}{" "}
</strong>
<span>{exampleProperties.description}</span>
</p>
)}
<p>
<strong>
{translate({
id: OPENAPI_SCHEMA_ITEM.EXAMPLE,
message: "Example:",
})}{" "}
</strong>
<code>{exampleProperties.value}</code>
</p>
</TabItem>
))}
</SchemaTabs>
</>
);
});

return (
<div className="openapi-params__list-item">
<span className="openapi-schema__container">
Expand All @@ -237,8 +176,8 @@ function ParamsItem({ param, ...rest }: Props) {
{renderDescription}
{renderEnumDescriptions}
{renderDefaultValue()}
{renderExample}
{renderExamples}
<Example example={example} />
<Example examples={examples} />
</div>
);
}
Expand Down
Loading