diff --git a/demo/examples/tests/examples.yaml b/demo/examples/tests/examples.yaml new file mode 100644 index 000000000..d7f68a314 --- /dev/null +++ b/demo/examples/tests/examples.yaml @@ -0,0 +1,503 @@ +openapi: 3.1.0 +info: + title: Examples Demo API + description: Demonstrates various examples schema combinations. + version: 1.0.0 + license: + name: MIT + url: https://opensource.org/licenses/MIT +security: [] +servers: + - url: http://test.local:8080 + description: Local server +tags: + - name: examples + description: examples +paths: + /requestParameters/example: + get: + tags: + - examples + summary: example in request parameters + description: "description of request parameters example" + parameters: + - name: name + description: name example + in: query + schema: + type: string + example: "John Doe" + - name: age + description: age example + in: query + schema: + type: number + example: 25 + responses: + "204": + description: no content + + /requestParameters/examples: + get: + tags: + - examples + summary: examples in request parameters + description: "description of request parameters examples" + parameters: + - name: name + description: name example + in: query + schema: + type: string + examples: + example1: + summary: "name example 1" + description: "name example 1 description" + value: "John Doe" + example2: + summary: "name example 2" + description: "name example 2 description" + value: "Jane Smith" + responses: + "204": + description: no content + + /requestParameters/schema/example: + get: + tags: + - examples + summary: example in request parameters schema + description: "description of request parameters schema example" + parameters: + - name: name + description: name example + in: query + schema: + type: string + example: "John Doe" + - name: age + description: age example + in: query + schema: + type: number + example: 25 + responses: + "204": + description: no content + + /requestParameters/schema/examples: + get: + tags: + - examples + summary: examples in request parameters schema + description: "description of request parameters schema examples" + parameters: + - name: name + description: name example + in: query + schema: + type: string + examples: + - "John Doe" + - "Jane Smith" + - name: age + description: age example + in: query + schema: + type: number + examples: + - 25 + - 30 + responses: + "204": + description: no content + + /requestBody/mediaTypeObject/example: + post: + tags: + - examples + summary: example of media type object in requestBody + description: "description of requestBody media type object example" + requestBody: + description: "description of requestBody" + content: + application/json: + schema: + type: object + properties: + name: + type: string + age: + type: number + isStudent: + type: boolean + example: + name: "John Doe" + age: 25 + isStudent: false + responses: + "204": + description: no content + + /requestBody/mediaTypeObject/examples: + post: + tags: + - examples + summary: examples of media type object in requestBody + description: "description of requestBody media type object examples" + requestBody: + description: "description of requestBody" + content: + application/json: + schema: + type: object + properties: + name: + type: string + age: + type: number + isStudent: + type: boolean + examples: + example1: + value: + name: "John Doe" + age: 25 + isStudent: false + example2: + value: + name: "Jane Smith" + age: 30 + isStudent: true + responses: + "204": + description: no content + + /requestBody/schema/example: + post: + tags: + - examples + summary: example of schema in requestBody + description: "description of requestBody schema example" + requestBody: + description: "description of requestBody" + content: + application/json: + schema: + type: object + properties: + name: + type: string + age: + type: number + isStudent: + type: boolean + example: + name: "John Doe" + age: 25 + isStudent: false + responses: + "204": + description: no content + + /requestBody/schema/examples: + post: + tags: + - examples + summary: examples of schema in requestBody + description: "description of requestBody schema examples" + requestBody: + description: "description of requestBody" + content: + application/json: + schema: + type: object + properties: + name: + type: string + age: + type: number + isStudent: + type: boolean + examples: + - name: "John Doe" + age: 25 + isStudent: false + - name: "Jane Smith" + age: 30 + isStudent: true + responses: + "204": + description: no content + + /requestBody/schema/properties/example: + post: + tags: + - examples + summary: example of properties in requestBody schema + description: "description of requestBody schema properties example" + requestBody: + description: "description of requestBody" + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: "John Doe" + age: + type: number + example: 25 + isStudent: + type: boolean + example: false + responses: + "204": + description: no content + + /requestBody/schema/properties/examples: + post: + tags: + - examples + summary: examples of properties in requestBody schema + description: "description of requestBody schema properties examples" + requestBody: + description: "description of requestBody" + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + examples: + - "John Doe" + - "Jane Smith" + age: + type: number + examples: + - 25 + - 30 + isStudent: + type: boolean + examples: + - true + - false + responses: + "204": + description: no content + + /response/mediaTypeObject/example: + get: + tags: + - examples + summary: example of media type object in response + description: "description of response media type object example" + responses: + "200": + description: successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + age: + type: number + isStudent: + type: boolean + example: + name: "John Doe" + age: 25 + isStudent: false + + /response/mediaTypeObject/examples: + get: + tags: + - examples + summary: examples of media type object in response + description: "description of response media type object examples" + responses: + "200": + description: successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + age: + type: number + isStudent: + type: boolean + examples: + example1: + value: + name: "John Doe" + age: 25 + isStudent: false + example2: + value: + name: "Jane Smith" + age: 30 + isStudent: true + + /response/mediaTypeObject/noSchema: + get: + tags: + - examples + summary: no schema in response + description: "description of response media type object no schema" + responses: + "200": + description: successful response + content: + application/json: + + /response/schema/example: + get: + tags: + - examples + summary: example of schema in response + description: "description of response schema example" + responses: + "200": + description: successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + age: + type: number + isStudent: + type: boolean + example: + name: "John Doe" + age: 25 + isStudent: false + + /response/schema/examples: + get: + tags: + - examples + summary: examples of schema in response + description: "description of response schema examples" + responses: + "200": + description: successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + age: + type: number + isStudent: + type: boolean + examples: + - name: "John Doe" + age: 25 + isStudent: false + - name: "Jane Smith" + age: 30 + isStudent: true + + /response/schema/properties/example: + get: + tags: + - examples + summary: example of schema properties in response + description: "description of response schema properties example" + responses: + "200": + description: successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: "John Doe" + age: + type: number + example: 25 + isStudent: + type: boolean + example: false + + /response/schema/properties/examples: + get: + tags: + - examples + summary: examples of schema properties in response + description: "description of response schema properties examples" + responses: + "200": + description: successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + examples: + - "John Doe" + - "Jane Smith" + age: + type: number + examples: + - 25 + - 30 + isStudent: + type: boolean + examples: + - true + - false + + /response/schema/properties/multipleTypes/examples: + get: + tags: + - examples + summary: examples of schema properties with multiple types in response + description: "description of response schema properties with multiple types examples" + responses: + "200": + description: successful response + content: + application/json: + schema: + type: object + properties: + name: + type: + - string + - "null" + examples: + - "John Doe" + - "Jane Smith" + age: + type: + - number + - "null" + examples: + - 25 + - 30 + isStudent: + type: + - boolean + - "null" + examples: + - true + - false diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/createSchemaExample.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/createSchemaExample.ts index ea0ddcf0c..f8a6b2690 100644 --- a/packages/docusaurus-plugin-openapi-docs/src/openapi/createSchemaExample.ts +++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/createSchemaExample.ts @@ -56,9 +56,9 @@ const primitives: Primitives = { }, }; -type ExampleType = "request" | "response"; +export type ExampleType = "request" | "response"; -interface ExampleContext { +export interface ExampleContext { type: ExampleType; } diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/BaseSchema/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/BaseSchema/index.tsx new file mode 100644 index 000000000..41e55d496 --- /dev/null +++ b/packages/docusaurus-theme-openapi-docs/src/theme/BaseSchema/index.tsx @@ -0,0 +1,164 @@ +/* ============================================================================ + * 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 BrowserOnly from "@docusaurus/BrowserOnly"; +import Details from "@theme/Details"; +import { + ExampleFromSchema, + MimeExample, + MimeExamples, + SchemaExample, + SchemaExamples, +} from "@theme/Examples"; +import Markdown from "@theme/Markdown"; +import MimeTabs from "@theme/MimeTabs"; +import SchemaNode from "@theme/Schema"; +import SchemaTabs from "@theme/SchemaTabs"; +import SkeletonLoader from "@theme/SkeletonLoader"; +import TabItem from "@theme/TabItem"; +import { MediaTypeObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types"; + +interface Props { + style?: React.CSSProperties; + title: string; + body: { + content?: { + [key: string]: MediaTypeObject; + }; + description?: string; + required?: string[] | boolean; + }; + schemaType: "request" | "response"; +} + +const BaseSchemaComponent: React.FC = ({ + title, + body, + style, + schemaType, +}) => { + if ( + body === undefined || + body.content === undefined || + Object.keys(body).length === 0 || + Object.keys(body.content).length === 0 + ) { + return null; + } + + const mimeTypes = Object.keys(body.content); + if (mimeTypes && mimeTypes.length) { + return ( + + {mimeTypes.map((mimeType: any) => { + const mimeExamples = body.content?.[mimeType]?.examples; + const mimeExample = body.content?.[mimeType]?.example; + const schemaExamples = body.content?.[mimeType]?.schema?.examples; + const schemaExample = body.content?.[mimeType]?.schema?.example; + const firstBody = body.content?.[mimeType]?.schema; + + if ( + firstBody === undefined || + (firstBody.properties && + Object.keys(firstBody.properties).length === 0) + ) { + return ( + // @ts-ignore + +
No schema
+
+ ); + } + + if (firstBody) { + const tabTitle = "Schema"; + return ( + // @ts-ignore + + + {/* @ts-ignore */} + +
+ + + {title} + {body.required && ( + + required + + )} + + + + } + > +
+ {body.description && ( +
+ {body.description} +
+ )} +
+
    + +
+
+
+ {firstBody && + ExampleFromSchema({ + schema: firstBody, + mimeType, + context: { type: schemaType }, + })} + + {mimeExamples && + MimeExamples({ examples: mimeExamples, mimeType })} + + {mimeExample && + MimeExample({ example: mimeExample, mimeType })} + + {schemaExamples && + SchemaExamples({ examples: schemaExamples, mimeType })} + + {schemaExample && + SchemaExample({ example: schemaExample, mimeType })} +
+
+ ); + } + return null; + })} +
+ ); + } + return null; +}; + +const BaseSchema: React.FC = (props) => { + return ( + }> + {() => { + return ; + }} + + ); +}; + +export default BaseSchema; diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/ResponseExamples/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/Examples/index.tsx similarity index 56% rename from packages/docusaurus-theme-openapi-docs/src/theme/ResponseExamples/index.tsx rename to packages/docusaurus-theme-openapi-docs/src/theme/Examples/index.tsx index c0df5864e..5d8d779b7 100644 --- a/packages/docusaurus-theme-openapi-docs/src/theme/ResponseExamples/index.tsx +++ b/packages/docusaurus-theme-openapi-docs/src/theme/Examples/index.tsx @@ -10,7 +10,10 @@ import React from "react"; import CodeSamples from "@theme/CodeSamples"; import Markdown from "@theme/Markdown"; import TabItem from "@theme/TabItem"; -import { sampleResponseFromSchema } from "docusaurus-plugin-openapi-docs/lib/openapi/createResponseExample"; +import { + sampleFromSchema, + ExampleContext, +} from "docusaurus-plugin-openapi-docs/lib/openapi/createSchemaExample"; import format from "xml-formatter"; export function json2xml(o: Record, tab: string): string { @@ -50,23 +53,56 @@ export function json2xml(o: Record, tab: string): string { return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, ""); } -interface ResponseExamplesProps { - responseExamples: any; +export function getLanguageFromMimeType(mimeType: string): string { + let language = "shell"; + if (mimeType.endsWith("json")) language = "json"; + if (mimeType.endsWith("xml")) language = "xml"; + return language; +} + +export interface MimeExampleProps { + example: any; + mimeType: string; +} + +export const MimeExample: React.FC = ({ + example, + mimeType, +}) => { + const language = getLanguageFromMimeType(mimeType); + + const isObject = typeof example === "object"; + const exampleContent = isObject ? JSON.stringify(example, null, 2) : example; + + return ( + // @ts-ignore + + {example.summary && ( + + {example.summary} + + )} + + + ); +}; + +export interface MimeExamplesProps { + examples: any; mimeType: string; } -export const ResponseExamples: React.FC = ({ - responseExamples, + +export const MimeExamples: React.FC = ({ + examples, mimeType, }): any => { - let language = "shell"; - if (mimeType.endsWith("json")) language = "json"; - if (mimeType.endsWith("xml")) language = "xml"; + const language = getLanguageFromMimeType(mimeType); - // Map response examples to an array of TabItem elements - const examplesArray = Object.entries(responseExamples).map( + // Map examples to an array of TabItem elements + const examplesArray = Object.entries(examples).map( ([exampleName, exampleValue]: any) => { const isObject = typeof exampleValue.value === "object"; - const responseExample = isObject + const exampleContent = isObject ? JSON.stringify(exampleValue.value, null, 2) : exampleValue.value; @@ -78,7 +114,7 @@ export const ResponseExamples: React.FC = ({ {exampleValue.summary} )} - + ); } @@ -87,34 +123,26 @@ export const ResponseExamples: React.FC = ({ return examplesArray; }; -interface ResponseExampleProps { - responseExample: any; +export interface SchemaExampleProps { + example: any; mimeType: string; } -export const ResponseExample: React.FC = ({ - responseExample, +export const SchemaExample: React.FC = ({ + example, mimeType, }) => { - let language = "shell"; - if (mimeType.endsWith("json")) { - language = "json"; - } - if (mimeType.endsWith("xml")) { - language = "xml"; - } + const language = getLanguageFromMimeType(mimeType); - const isObject = typeof responseExample === "object"; - const exampleContent = isObject - ? JSON.stringify(responseExample, null, 2) - : responseExample; + const isObject = typeof example === "object"; + const exampleContent = isObject ? JSON.stringify(example, null, 2) : example; return ( // @ts-ignore - {responseExample.summary && ( + {example.summary && ( - {responseExample.summary} + {example.summary} )} @@ -122,35 +150,67 @@ export const ResponseExample: React.FC = ({ ); }; -interface ExampleFromSchemaProps { +export interface SchemaExamplesProps { + examples: any[]; + mimeType: string; +} + +export const SchemaExamples: React.FC = ({ + examples, + mimeType, +}) => { + const language = getLanguageFromMimeType(mimeType); + + // Map examples to an array of TabItem elements + const examplesArray = examples.map((example: any, i: number) => { + const exampleName = `Example ${i + 1}`; + const isObject = typeof example === "object"; + const exampleContent = isObject + ? JSON.stringify(example, null, 2) + : example; + + return ( + // @ts-ignore + + + + ); + }); + + return examplesArray; +}; + +export interface ExampleFromSchemaProps { schema: any; mimeType: string; + context: ExampleContext; } export const ExampleFromSchema: React.FC = ({ schema, mimeType, + context, }) => { - const responseExample = sampleResponseFromSchema(schema); + const example = sampleFromSchema(schema, context); if (mimeType.endsWith("xml")) { - let responseExampleObject; + let exampleObject; try { - responseExampleObject = JSON.parse(JSON.stringify(responseExample)); + exampleObject = JSON.parse(JSON.stringify(example)); } catch { return null; } - if (typeof responseExampleObject === "object") { + if (typeof exampleObject === "object") { let xmlExample; try { - xmlExample = format(json2xml(responseExampleObject, ""), { + xmlExample = format(json2xml(exampleObject, ""), { indentation: " ", lineSeparator: "\n", collapseContent: true, }); } catch { - const xmlExampleWithRoot = { root: responseExampleObject }; + const xmlExampleWithRoot = { root: exampleObject }; try { xmlExample = format(json2xml(xmlExampleWithRoot, ""), { indentation: " ", @@ -158,7 +218,7 @@ export const ExampleFromSchema: React.FC = ({ collapseContent: true, }); } catch { - xmlExample = json2xml(responseExampleObject, ""); + xmlExample = json2xml(exampleObject, ""); } } return ( @@ -170,15 +230,12 @@ export const ExampleFromSchema: React.FC = ({ } } - if ( - typeof responseExample === "object" || - typeof responseExample === "string" - ) { + if (typeof example === "object" || typeof example === "string") { return ( // @ts-ignore diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/Markdown/Details/_Details.scss b/packages/docusaurus-theme-openapi-docs/src/theme/Markdown/Details/_Details.scss index 96e992073..41a8f8b92 100644 --- a/packages/docusaurus-theme-openapi-docs/src/theme/Markdown/Details/_Details.scss +++ b/packages/docusaurus-theme-openapi-docs/src/theme/Markdown/Details/_Details.scss @@ -24,11 +24,7 @@ /* Top-Level Details Caret Styling */ .openapi-left-panel__container > .openapi-markdown__details > summary::before, .openapi-markdown__details.mime > summary::before { - top: 0.1rem; -} - -.openapi-markdown__details.response > summary::before { - top: 0.25rem; /* TODO: figure out why this is necessary */ + top: 0.25rem; } /* End of Top-Level Details Caret Styling */ diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/ParamsItem/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/ParamsItem/index.tsx index 863508e3e..5a7e75a23 100644 --- a/packages/docusaurus-theme-openapi-docs/src/theme/ParamsItem/index.tsx +++ b/packages/docusaurus-theme-openapi-docs/src/theme/ParamsItem/index.tsx @@ -17,10 +17,10 @@ import { getQualifierMessage, getSchemaName } from "../../markdown/schema"; import { guard, toString } from "../../markdown/utils"; export interface ExampleObject { - summary?: string; - description?: string; + summary?: string | null; + description?: string | null; value?: any; - externalValue?: string; + externalValue?: string | null; } export interface Props { @@ -53,19 +53,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; + let example = param.example || schema?.example; + if (!schema) { schema = { type: "any" }; } @@ -140,32 +135,75 @@ function ParamsItem({ param, ...rest }: Props) { const renderExample = guard(toString(example), (example) => (
Example: - {example} + {example}
)); + // Helper function to format example value + const formatExample = (example: any) => { + if (typeof example === "object" && example !== null) { + return JSON.stringify(example); + } + return String(example); + }; + + const renderExampleTabItem = ( + exampleName: string, + exampleProperties: ExampleObject + ) => { + return ( + // @ts-ignore + + {exampleProperties.summary &&

{exampleProperties.summary}

} + {exampleProperties.description && ( +

+ Description: + {exampleProperties.description} +

+ )} +

+ Example: + {formatExample(exampleProperties.value)} +

+
+ ); + }; + const renderExamples = guard(examples, (examples) => { - const exampleEntries = Object.entries(examples); + // Handle object-based examples (existing logic) + let exampleEntries: [string, ExampleObject][]; + if (Array.isArray(examples)) { + exampleEntries = examples.map((example, index) => [ + `Example ${index + 1}`, + { value: example, summary: null, description: null }, + ]); + } else { + 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 ( +
+ Example: + + {formatExample(firstExample.value)} + +
+ ); + } + return ( <> Examples: - {exampleEntries.map(([exampleName, exampleProperties]) => ( - // @ts-ignore - - {exampleProperties.summary &&

{exampleProperties.summary}

} - {exampleProperties.description && ( -

- Description: - {exampleProperties.description} -

- )} -

- Example: - {exampleProperties.value} -

-
- ))} + {exampleEntries.map(([exampleName, exampleProperties]) => + renderExampleTabItem(exampleName, exampleProperties) + )}
); diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/RequestSchema/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/RequestSchema/index.tsx index 9eeceb4bd..00ec65e24 100644 --- a/packages/docusaurus-theme-openapi-docs/src/theme/RequestSchema/index.tsx +++ b/packages/docusaurus-theme-openapi-docs/src/theme/RequestSchema/index.tsx @@ -7,13 +7,7 @@ import React from "react"; -import BrowserOnly from "@docusaurus/BrowserOnly"; -import Details from "@theme/Details"; -import Markdown from "@theme/Markdown"; -import MimeTabs from "@theme/MimeTabs"; // Assume these components exist -import SchemaNode from "@theme/Schema"; -import SkeletonLoader from "@theme/SkeletonLoader"; -import TabItem from "@theme/TabItem"; +import BaseSchema from "@theme/BaseSchema"; import { MediaTypeObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types"; interface Props { @@ -28,130 +22,8 @@ interface Props { }; } -const RequestSchemaComponent: React.FC = ({ title, body, style }) => { - if ( - body === undefined || - body.content === undefined || - Object.keys(body).length === 0 || - Object.keys(body.content).length === 0 - ) { - return null; - } - - const mimeTypes = Object.keys(body.content); - - if (mimeTypes.length > 1) { - return ( - - {mimeTypes.map((mimeType) => { - const firstBody = body.content![mimeType].schema; - if ( - firstBody === undefined || - (firstBody.properties && - Object.keys(firstBody.properties).length === 0) - ) { - return null; - } - return ( - // @ts-ignore - -
- -

- {title} - {body.required === true && ( - - required - - )} -

-
- - } - > -
- {body.description && ( -
- {body.description} -
- )} -
-
    - -
-
-
- ); - })} -
- ); - } - - const randomFirstKey = mimeTypes[0]; - const firstBody = - body.content[randomFirstKey].schema ?? body.content![randomFirstKey]; - - if (firstBody === undefined) { - return null; - } - - return ( - - {/* @ts-ignore */} - -
- -

- {title} - {firstBody.type === "array" && ( - array - )} - {body.required && ( - - required - - )} -

-
- - } - > -
- {body.description && ( -
- {body.description} -
- )} -
-
    - -
-
-
-
- ); -}; - const RequestSchema: React.FC = (props) => { - return ( - }> - {() => { - return ; - }} - - ); + return ; }; export default RequestSchema; diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/ResponseSchema/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/ResponseSchema/index.tsx index ac4a64954..023385765 100644 --- a/packages/docusaurus-theme-openapi-docs/src/theme/ResponseSchema/index.tsx +++ b/packages/docusaurus-theme-openapi-docs/src/theme/ResponseSchema/index.tsx @@ -7,19 +7,7 @@ import React from "react"; -import BrowserOnly from "@docusaurus/BrowserOnly"; -import Details from "@theme/Details"; -import Markdown from "@theme/Markdown"; -import MimeTabs from "@theme/MimeTabs"; // Assume these components exist -import { - ExampleFromSchema, - ResponseExample, - ResponseExamples, -} from "@theme/ResponseExamples"; -import SchemaNode from "@theme/Schema"; -import SchemaTabs from "@theme/SchemaTabs"; -import SkeletonLoader from "@theme/SkeletonLoader"; -import TabItem from "@theme/TabItem"; +import BaseSchema from "@theme/BaseSchema"; import { MediaTypeObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types"; interface Props { @@ -34,111 +22,8 @@ interface Props { }; } -const ResponseSchemaComponent: React.FC = ({ - title, - body, - style, -}): any => { - if ( - body === undefined || - body.content === undefined || - Object.keys(body).length === 0 || - Object.keys(body.content).length === 0 - ) { - return null; - } - - // Get all MIME types, including vendor-specific - const mimeTypes = Object.keys(body.content); - if (mimeTypes && mimeTypes.length) { - return ( - - {mimeTypes.map((mimeType: any) => { - const responseExamples = body.content![mimeType].examples; - const responseExample = body.content![mimeType].example; - const firstBody: any = - body.content![mimeType].schema ?? body.content![mimeType]; - - if ( - firstBody === undefined && - responseExample === undefined && - responseExamples === undefined - ) { - return undefined; - } - - if (firstBody) { - return ( - // @ts-ignore - - - {/* @ts-ignore */} - -
- - - {title} - {body.required === true && ( - - required - - )} - - - - } - > -
- {body.description && ( -
- {body.description} -
- )} -
-
    - -
-
-
- {firstBody && - ExampleFromSchema({ - schema: firstBody, - mimeType: mimeType, - })} - - {responseExamples && - ResponseExamples({ responseExamples, mimeType })} - - {responseExample && - ResponseExample({ responseExample, mimeType })} -
-
- ); - } - return undefined; - })} -
- ); - } - return undefined; -}; - const ResponseSchema: React.FC = (props) => { - return ( - }> - {() => { - return ; - }} - - ); + return ; }; export default ResponseSchema; diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/SchemaItem/index.tsx b/packages/docusaurus-theme-openapi-docs/src/theme/SchemaItem/index.tsx index a77d1f3ee..599e6e573 100644 --- a/packages/docusaurus-theme-openapi-docs/src/theme/SchemaItem/index.tsx +++ b/packages/docusaurus-theme-openapi-docs/src/theme/SchemaItem/index.tsx @@ -8,6 +8,8 @@ import React, { ReactNode } from "react"; import Markdown from "@theme/Markdown"; +import SchemaTabs from "@theme/SchemaTabs"; +import TabItem from "@theme/TabItem"; import clsx from "clsx"; import { guard } from "../../markdown/utils"; @@ -63,6 +65,7 @@ export default function SchemaItem(props: Props) { let schemaDescription; let defaultValue: string | undefined; let example: string | undefined; + let examples: string[] | undefined; let nullable; let enumDescriptions: [string, string][] = []; let constValue: string | undefined; @@ -73,6 +76,7 @@ export default function SchemaItem(props: Props) { enumDescriptions = transformEnumDescriptions(schema["x-enumDescriptions"]); defaultValue = schema.default; example = schema.example; + examples = schema.examples; nullable = schema.nullable || (Array.isArray(schema.type) && schema.type.includes("null")); // support JSON Schema nullable @@ -163,6 +167,53 @@ export default function SchemaItem(props: Props) { return undefined; } + // Helper function to format example value + const formatExample = (example: any) => { + if (typeof example === "object" && example !== null) { + return JSON.stringify(example); + } + return String(example); + }; + + function renderExamples() { + if (examples && examples.length > 0) { + // If there's only one example, display it without tabs + if (examples.length === 1) { + return ( +
+ Example: + + {formatExample(examples[0])} + +
+ ); + } + + // Multiple examples - use tabs + return ( +
+ Examples: + + {examples.map((example, index) => ( + // @ts-ignore + +

+ Example: + {formatExample(example)} +

+
+ ))} +
+
+ ); + } + return undefined; + } + function renderConstValue() { if (constValue !== undefined) { if (typeof constValue === "string") { @@ -213,6 +264,7 @@ export default function SchemaItem(props: Props) { {renderConstValue()} {renderDefaultValue()} {renderExample()} + {renderExamples()} {collapsibleSchemaContent ?? collapsibleSchemaContent} );