Skip to content

Commit 98c3f4a

Browse files
authored
Support schema property examples (extract) (#1198)
* Add examples.yaml from support-schema-property-examples branch * fix: add examples in property * fix errors without schema * Adjusted the margin of the examples tab. * fix Examples * fix bugs * fix schema type
1 parent 09ac11f commit 98c3f4a

File tree

9 files changed

+831
-175
lines changed

9 files changed

+831
-175
lines changed

demo/examples/tests/examples.yaml

Lines changed: 551 additions & 0 deletions
Large diffs are not rendered by default.

packages/docusaurus-theme-openapi-docs/src/markdown/schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* ========================================================================== */
77

88
import { translate } from "@docusaurus/Translate";
9+
910
import { OPENAPI_SCHEMA_ITEM } from "../theme/translationIds";
1011
import { SchemaObject } from "../types";
1112

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

46+
if (Array.isArray(schema.type)) {
47+
return schema.type.join(" | ");
48+
}
49+
4550
return schema.title ?? schema.type;
4651
}
4752

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.openapi-examples {
2+
> .openapi-tabs__schema-container {
3+
margin: 0.4rem;
4+
5+
@layer docusaurus.infima {
6+
> .margin-top--md {
7+
margin: 0.2rem !important;
8+
}
9+
}
10+
}
11+
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/* ============================================================================
2+
* Copyright (c) Palo Alto Networks
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
* ========================================================================== */
7+
8+
import React from "react";
9+
10+
import { translate } from "@docusaurus/Translate";
11+
import { ExampleObject } from "@theme/ParamsItem";
12+
import SchemaTabs from "@theme/SchemaTabs";
13+
import TabItem from "@theme/TabItem";
14+
import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
15+
16+
const EXAMPLE_CLASS_NAME = "openapi-example";
17+
const EXAMPLES_CLASS_NAME = "openapi-examples";
18+
19+
type ExampleType = string;
20+
type ExamplesType = Record<string, ExampleObject> | string[];
21+
22+
/**
23+
* Example Component Props
24+
*/
25+
type ExampleProps = {
26+
example?: ExampleType;
27+
examples?: ExamplesType;
28+
};
29+
30+
/**
31+
* Example Component
32+
*/
33+
export const Example = ({ example, examples }: ExampleProps) => {
34+
if (example !== undefined) {
35+
return renderExample(example);
36+
}
37+
if (examples !== undefined) {
38+
return renderExamples(examples);
39+
}
40+
return undefined;
41+
};
42+
43+
/**
44+
* Format example value
45+
*
46+
* @param example
47+
* @returns
48+
*/
49+
const formatExample = (example: any) => {
50+
if (typeof example === "object" && example !== null) {
51+
return JSON.stringify(example);
52+
}
53+
return String(example);
54+
};
55+
56+
const renderExample = (example: ExampleType) => {
57+
return (
58+
<div className={EXAMPLE_CLASS_NAME}>
59+
<strong>
60+
{translate({
61+
id: OPENAPI_SCHEMA_ITEM.EXAMPLE,
62+
message: "Example:",
63+
})}{" "}
64+
</strong>
65+
<span>
66+
<code>{formatExample(example)}</code>
67+
</span>
68+
</div>
69+
);
70+
};
71+
72+
const renderExamples = (examples: ExamplesType) => {
73+
if (Array.isArray(examples)) {
74+
return renderStringArrayExamples(examples);
75+
}
76+
return renderExamplesRecord(examples);
77+
};
78+
79+
/**
80+
* Render string examples
81+
*
82+
* @param examples
83+
* @returns
84+
*/
85+
export function renderStringArrayExamples(examples: string[]) {
86+
if (examples.length === 0) {
87+
return undefined;
88+
}
89+
// If there's only one example, display it without tabs
90+
if (examples.length === 1) {
91+
return renderExample(examples[0]);
92+
}
93+
94+
// Multiple examples - use tabs
95+
const exampleEntries = examples.reduce(
96+
(acc, example, index) => ({
97+
...acc,
98+
[`Example ${index + 1}`]: {
99+
value: example,
100+
},
101+
}),
102+
{} as Record<string, ExampleObject>
103+
);
104+
return renderExamplesRecord(exampleEntries);
105+
}
106+
107+
export const renderExamplesRecord = (
108+
examples: Record<string, ExampleObject>
109+
) => {
110+
const exampleEntries = Object.entries(examples);
111+
// If there's only one example, display it without tabs
112+
if (exampleEntries.length === 1) {
113+
const firstExample = exampleEntries[0][1];
114+
if (!firstExample) {
115+
return undefined;
116+
}
117+
return renderExample(firstExample.value);
118+
}
119+
120+
return (
121+
<div className={EXAMPLES_CLASS_NAME}>
122+
<strong>
123+
{translate({
124+
id: OPENAPI_SCHEMA_ITEM.EXAMPLES,
125+
message: "Examples:",
126+
})}
127+
</strong>
128+
<SchemaTabs>
129+
{exampleEntries.map(([exampleName, exampleProperties]) =>
130+
renderExampleObject(exampleName, exampleProperties)
131+
)}
132+
</SchemaTabs>
133+
</div>
134+
);
135+
};
136+
137+
/**
138+
* Render example object
139+
*
140+
* @param exampleName
141+
* @param exampleProperties
142+
* @returns
143+
*/
144+
const renderExampleObject = (
145+
exampleName: string,
146+
exampleProperties: ExampleObject
147+
) => {
148+
return (
149+
// @ts-ignore
150+
<TabItem value={exampleName} label={exampleName}>
151+
{exampleProperties.summary && <p>{exampleProperties.summary}</p>}
152+
{exampleProperties.description && (
153+
<p>
154+
<strong>
155+
{translate({
156+
id: OPENAPI_SCHEMA_ITEM.DESCRIPTION,
157+
message: "Description:",
158+
})}{" "}
159+
</strong>
160+
<span>{exampleProperties.description}</span>
161+
</p>
162+
)}
163+
{exampleProperties.value !== undefined
164+
? renderExample(exampleProperties.value)
165+
: undefined}
166+
</TabItem>
167+
);
168+
};

packages/docusaurus-theme-openapi-docs/src/theme/ParamsItem/index.tsx

Lines changed: 10 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,14 @@
88
import React from "react";
99

1010
import { translate } from "@docusaurus/Translate";
11-
11+
import { Example } from "@theme/Example";
1212
import Markdown from "@theme/Markdown";
13-
import SchemaTabs from "@theme/SchemaTabs";
14-
import TabItem from "@theme/TabItem";
1513
/* eslint-disable import/no-extraneous-dependencies*/
16-
import clsx from "clsx";
1714
import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
15+
import clsx from "clsx";
1816

1917
import { getQualifierMessage, getSchemaName } from "../../markdown/schema";
20-
import { guard, toString } from "../../markdown/utils";
18+
import { guard } from "../../markdown/utils";
2119

2220
export interface ExampleObject {
2321
summary?: string;
@@ -31,7 +29,7 @@ export interface Props {
3129
param: {
3230
description: string;
3331
example: any;
34-
examples: Record<string, ExampleObject>;
32+
examples: Record<string, ExampleObject> | undefined;
3533
name: string;
3634
required: boolean;
3735
deprecated: boolean;
@@ -64,19 +62,14 @@ ${enumDescriptions
6462
};
6563

6664
function ParamsItem({ param, ...rest }: Props) {
67-
const {
68-
description,
69-
example,
70-
examples,
71-
name,
72-
required,
73-
deprecated,
74-
enumDescriptions,
75-
} = param;
65+
const { description, name, required, deprecated, enumDescriptions } = param;
7666

7767
let schema = param.schema;
7868
let defaultValue: string | undefined;
7969

70+
let examples = param.examples ?? (schema?.examples as any[] | undefined);
71+
let example = param.example ?? schema?.example;
72+
8073
if (!schema) {
8174
schema = { type: "any" };
8275
}
@@ -162,60 +155,6 @@ function ParamsItem({ param, ...rest }: Props) {
162155
return undefined;
163156
}
164157

165-
const renderExample = guard(toString(example), (example) => (
166-
<div>
167-
<strong>
168-
{translate({
169-
id: OPENAPI_SCHEMA_ITEM.EXAMPLE,
170-
message: "Example:",
171-
})}{" "}
172-
</strong>
173-
{example}
174-
</div>
175-
));
176-
177-
const renderExamples = guard(examples, (examples) => {
178-
const exampleEntries = Object.entries(examples);
179-
return (
180-
<>
181-
<strong>
182-
{translate({
183-
id: OPENAPI_SCHEMA_ITEM.EXAMPLES,
184-
message: "Examples:",
185-
})}
186-
</strong>
187-
<SchemaTabs>
188-
{exampleEntries.map(([exampleName, exampleProperties]) => (
189-
// @ts-ignore
190-
<TabItem value={exampleName} label={exampleName}>
191-
{exampleProperties.summary && <p>{exampleProperties.summary}</p>}
192-
{exampleProperties.description && (
193-
<p>
194-
<strong>
195-
{translate({
196-
id: OPENAPI_SCHEMA_ITEM.DESCRIPTION,
197-
message: "Description:",
198-
})}{" "}
199-
</strong>
200-
<span>{exampleProperties.description}</span>
201-
</p>
202-
)}
203-
<p>
204-
<strong>
205-
{translate({
206-
id: OPENAPI_SCHEMA_ITEM.EXAMPLE,
207-
message: "Example:",
208-
})}{" "}
209-
</strong>
210-
<code>{exampleProperties.value}</code>
211-
</p>
212-
</TabItem>
213-
))}
214-
</SchemaTabs>
215-
</>
216-
);
217-
});
218-
219158
return (
220159
<div className="openapi-params__list-item">
221160
<span className="openapi-schema__container">
@@ -237,8 +176,8 @@ function ParamsItem({ param, ...rest }: Props) {
237176
{renderDescription}
238177
{renderEnumDescriptions}
239178
{renderDefaultValue()}
240-
{renderExample}
241-
{renderExamples}
179+
<Example example={example} />
180+
<Example examples={examples} />
242181
</div>
243182
);
244183
}

0 commit comments

Comments
 (0)