docusaurus-theme-openapi-docs 4.7.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/lib/index.js +2 -0
  2. package/lib/markdown/schema.js +63 -9
  3. package/lib/theme/ApiExplorer/Accept/index.js +2 -1
  4. package/lib/theme/ApiExplorer/Authorization/index.js +12 -18
  5. package/lib/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.js +0 -4
  6. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.d.ts +5 -1
  7. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.js +190 -37
  8. package/lib/theme/ApiExplorer/Body/index.js +84 -13
  9. package/lib/theme/ApiExplorer/Body/slice.d.ts +136 -544
  10. package/lib/theme/ApiExplorer/CodeSnippets/index.d.ts +2 -1
  11. package/lib/theme/ApiExplorer/CodeSnippets/index.js +4 -0
  12. package/lib/theme/ApiExplorer/CodeTabs/index.js +15 -16
  13. package/lib/theme/ApiExplorer/ContentType/index.js +7 -2
  14. package/lib/theme/ApiExplorer/EncodingSelection/slice.d.ts +17 -0
  15. package/lib/theme/ApiExplorer/EncodingSelection/slice.js +29 -0
  16. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.d.ts +12 -0
  17. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.js +39 -0
  18. package/lib/theme/ApiExplorer/FormItem/_FormItem.scss +0 -5
  19. package/lib/theme/ApiExplorer/FormItem/index.d.ts +1 -4
  20. package/lib/theme/ApiExplorer/FormItem/index.js +2 -26
  21. package/lib/theme/ApiExplorer/FormLabel/_FormLabel.scss +4 -0
  22. package/lib/theme/ApiExplorer/FormLabel/index.d.ts +9 -0
  23. package/lib/theme/ApiExplorer/FormLabel/index.js +50 -0
  24. package/lib/theme/ApiExplorer/FormMultiSelect/index.d.ts +4 -1
  25. package/lib/theme/ApiExplorer/FormMultiSelect/index.js +97 -19
  26. package/lib/theme/ApiExplorer/FormSelect/index.d.ts +6 -1
  27. package/lib/theme/ApiExplorer/FormSelect/index.js +96 -15
  28. package/lib/theme/ApiExplorer/FormTextInput/index.d.ts +4 -1
  29. package/lib/theme/ApiExplorer/FormTextInput/index.js +71 -1
  30. package/lib/theme/ApiExplorer/MethodEndpoint/index.js +28 -0
  31. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.d.ts +4 -1
  32. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.js +11 -3
  33. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.d.ts +4 -1
  34. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.js +4 -1
  35. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.d.ts +4 -1
  36. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.js +6 -2
  37. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.d.ts +4 -1
  38. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.js +6 -2
  39. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.d.ts +4 -1
  40. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.js +8 -3
  41. package/lib/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +0 -9
  42. package/lib/theme/ApiExplorer/ParamOptions/index.d.ts +10 -0
  43. package/lib/theme/ApiExplorer/ParamOptions/index.js +55 -5
  44. package/lib/theme/ApiExplorer/Request/_Request.scss +11 -0
  45. package/lib/theme/ApiExplorer/Request/index.js +19 -5
  46. package/lib/theme/ApiExplorer/Request/makeRequest.d.ts +3 -1
  47. package/lib/theme/ApiExplorer/Request/makeRequest.js +19 -3
  48. package/lib/theme/ApiExplorer/Response/_Response.scss +11 -0
  49. package/lib/theme/ApiExplorer/Response/index.js +98 -12
  50. package/lib/theme/ApiExplorer/Server/index.d.ts +4 -1
  51. package/lib/theme/ApiExplorer/Server/index.js +6 -3
  52. package/lib/theme/ApiExplorer/buildPostmanRequest.d.ts +4 -1
  53. package/lib/theme/ApiExplorer/buildPostmanRequest.js +46 -5
  54. package/lib/theme/ApiExplorer/index.js +1 -0
  55. package/lib/theme/ApiExplorer/persistenceMiddleware.d.ts +2 -0
  56. package/lib/theme/ApiItem/hooks.d.ts +1 -0
  57. package/lib/theme/ApiItem/index.js +2 -1
  58. package/lib/theme/ApiItem/store.d.ts +6 -0
  59. package/lib/theme/ApiItem/store.js +11 -7
  60. package/lib/theme/ApiTabs/index.js +10 -11
  61. package/lib/theme/DiscriminatorTabs/index.js +10 -11
  62. package/lib/theme/MimeTabs/index.js +10 -11
  63. package/lib/theme/OperationTabs/index.js +10 -11
  64. package/lib/theme/ParamsItem/index.js +27 -0
  65. package/lib/theme/RequestSchema/index.js +172 -109
  66. package/lib/theme/ResponseHeaders/index.js +0 -1
  67. package/lib/theme/Schema/index.d.ts +1 -1
  68. package/lib/theme/Schema/index.js +91 -23
  69. package/lib/theme/SchemaItem/index.js +6 -1
  70. package/lib/theme/SchemaTabs/index.d.ts +1 -1
  71. package/lib/theme/SchemaTabs/index.js +31 -12
  72. package/lib/theme/styles.scss +1 -0
  73. package/lib/theme/translationIds.d.ts +3 -0
  74. package/lib/theme/translationIds.js +3 -0
  75. package/package.json +9 -8
  76. package/src/index.ts +2 -0
  77. package/src/markdown/schema.ts +69 -13
  78. package/src/theme/ApiExplorer/Accept/index.tsx +2 -1
  79. package/src/theme/ApiExplorer/Authorization/index.tsx +27 -33
  80. package/src/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.tsx +0 -5
  81. package/src/theme/ApiExplorer/Body/FormBodyItem/index.tsx +115 -37
  82. package/src/theme/ApiExplorer/Body/index.tsx +85 -17
  83. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +9 -1
  84. package/src/theme/ApiExplorer/CodeTabs/index.tsx +19 -19
  85. package/src/theme/ApiExplorer/ContentType/index.tsx +7 -4
  86. package/src/theme/ApiExplorer/EncodingSelection/slice.ts +31 -0
  87. package/src/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.ts +43 -0
  88. package/src/theme/ApiExplorer/FormItem/_FormItem.scss +0 -5
  89. package/src/theme/ApiExplorer/FormItem/index.tsx +2 -16
  90. package/src/theme/ApiExplorer/FormLabel/_FormLabel.scss +4 -0
  91. package/src/theme/ApiExplorer/FormLabel/index.tsx +43 -0
  92. package/src/theme/ApiExplorer/FormMultiSelect/index.tsx +40 -20
  93. package/src/theme/ApiExplorer/FormSelect/index.tsx +41 -15
  94. package/src/theme/ApiExplorer/FormTextInput/index.tsx +15 -1
  95. package/src/theme/ApiExplorer/MethodEndpoint/index.tsx +21 -0
  96. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.tsx +13 -2
  97. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.tsx +12 -1
  98. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.tsx +14 -2
  99. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.tsx +14 -2
  100. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.tsx +16 -3
  101. package/src/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +0 -9
  102. package/src/theme/ApiExplorer/ParamOptions/index.tsx +97 -11
  103. package/src/theme/ApiExplorer/Request/_Request.scss +11 -0
  104. package/src/theme/ApiExplorer/Request/index.tsx +20 -8
  105. package/src/theme/ApiExplorer/Request/makeRequest.ts +19 -3
  106. package/src/theme/ApiExplorer/Response/_Response.scss +11 -0
  107. package/src/theme/ApiExplorer/Response/index.tsx +35 -14
  108. package/src/theme/ApiExplorer/Server/index.tsx +10 -3
  109. package/src/theme/ApiExplorer/buildPostmanRequest.ts +52 -5
  110. package/src/theme/ApiExplorer/index.tsx +1 -0
  111. package/src/theme/ApiItem/index.tsx +2 -1
  112. package/src/theme/ApiItem/store.ts +2 -0
  113. package/src/theme/ApiTabs/index.tsx +14 -19
  114. package/src/theme/DiscriminatorTabs/index.tsx +14 -19
  115. package/src/theme/MimeTabs/index.tsx +15 -19
  116. package/src/theme/OperationTabs/index.tsx +14 -19
  117. package/src/theme/ParamsItem/index.tsx +25 -0
  118. package/src/theme/RequestSchema/index.tsx +141 -83
  119. package/src/theme/ResponseHeaders/index.tsx +1 -2
  120. package/src/theme/Schema/index.tsx +112 -27
  121. package/src/theme/SchemaItem/index.tsx +6 -1
  122. package/src/theme/SchemaTabs/index.tsx +42 -21
  123. package/src/theme/styles.scss +1 -0
  124. package/src/theme/translationIds.ts +3 -0
  125. package/src/theme-classic.d.ts +25 -1
  126. package/src/types.d.ts +7 -0
  127. package/tsconfig.tsbuildinfo +1 -1
@@ -12,7 +12,13 @@ import { translate } from "@docusaurus/Translate";
12
12
  import Details from "@theme/Details";
13
13
  import Markdown from "@theme/Markdown";
14
14
  import MimeTabs from "@theme/MimeTabs"; // Assume these components exist
15
+ import {
16
+ ExampleFromSchema,
17
+ ResponseExample,
18
+ ResponseExamples,
19
+ } from "@theme/ResponseExamples";
15
20
  import SchemaNode from "@theme/Schema";
21
+ import SchemaTabs from "@theme/SchemaTabs";
16
22
  import SkeletonLoader from "@theme/SkeletonLoader";
17
23
  import TabItem from "@theme/TabItem";
18
24
  import { OPENAPI_REQUEST, OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
@@ -46,7 +52,11 @@ const RequestSchemaComponent: React.FC<Props> = ({ title, body, style }) => {
46
52
  return (
47
53
  <MimeTabs className="openapi-tabs__mime" schemaType="request" lazy>
48
54
  {mimeTypes.map((mimeType) => {
49
- const firstBody = body.content![mimeType].schema;
55
+ const mediaTypeObject = body.content![mimeType];
56
+ const firstBody = mediaTypeObject?.schema;
57
+ const requestExamples = mediaTypeObject?.examples;
58
+ const requestExample = mediaTypeObject?.example;
59
+
50
60
  if (
51
61
  firstBody === undefined ||
52
62
  (firstBody.properties &&
@@ -57,49 +67,73 @@ const RequestSchemaComponent: React.FC<Props> = ({ title, body, style }) => {
57
67
  return (
58
68
  // @ts-ignore
59
69
  <TabItem key={mimeType} label={mimeType} value={mimeType}>
60
- <div style={{ marginTop: "1rem" }}>
61
- <Details
62
- className="openapi-markdown__details mime"
63
- data-collapsed={false}
64
- open={true}
65
- style={style}
66
- summary={
67
- <>
68
- <summary>
69
- <h3 className="openapi-markdown__details-summary-header-body">
70
- {translate({
71
- id: OPENAPI_REQUEST.BODY_TITLE,
72
- message: title,
73
- })}
74
- {body.required === true && (
75
- <span className="openapi-schema__required">
70
+ <SchemaTabs className="openapi-tabs__schema">
71
+ {/* @ts-ignore */}
72
+ <TabItem key={title} label={title} value={title}>
73
+ <div style={{ marginTop: "1rem" }}>
74
+ <Details
75
+ className="openapi-markdown__details mime"
76
+ data-collapsed={false}
77
+ open={true}
78
+ style={style}
79
+ summary={
80
+ <>
81
+ <summary>
82
+ <h3 className="openapi-markdown__details-summary-header-body">
76
83
  {translate({
77
- id: OPENAPI_SCHEMA_ITEM.REQUIRED,
78
- message: "required",
84
+ id: OPENAPI_REQUEST.BODY_TITLE,
85
+ message: title,
79
86
  })}
80
- </span>
81
- )}
82
- </h3>
83
- </summary>
84
- </>
85
- }
86
- >
87
- <div style={{ textAlign: "left", marginLeft: "1rem" }}>
88
- {body.description && (
89
- <div style={{ marginTop: "1rem", marginBottom: "1rem" }}>
90
- <Markdown>{body.description}</Markdown>
87
+ {body.required === true && (
88
+ <span className="openapi-schema__required">
89
+ {translate({
90
+ id: OPENAPI_SCHEMA_ITEM.REQUIRED,
91
+ message: "required",
92
+ })}
93
+ </span>
94
+ )}
95
+ </h3>
96
+ </summary>
97
+ </>
98
+ }
99
+ >
100
+ <div style={{ textAlign: "left", marginLeft: "1rem" }}>
101
+ {body.description && (
102
+ <div
103
+ style={{ marginTop: "1rem", marginBottom: "1rem" }}
104
+ >
105
+ <Markdown>{body.description}</Markdown>
106
+ </div>
107
+ )}
91
108
  </div>
92
- )}
109
+ <ul style={{ marginLeft: "1rem" }}>
110
+ <SchemaNode
111
+ schema={firstBody}
112
+ schemaType="request"
113
+ schemaPath="requestBody"
114
+ />
115
+ </ul>
116
+ </Details>
93
117
  </div>
94
- <ul style={{ marginLeft: "1rem" }}>
95
- <SchemaNode
96
- schema={firstBody}
97
- schemaType="request"
98
- schemaPath="requestBody"
99
- />
100
- </ul>
101
- </Details>
102
- </div>
118
+ </TabItem>
119
+ {firstBody &&
120
+ !requestExample &&
121
+ !requestExamples &&
122
+ ExampleFromSchema({
123
+ schema: firstBody,
124
+ mimeType: mimeType,
125
+ })}
126
+ {requestExamples &&
127
+ ResponseExamples({
128
+ responseExamples: requestExamples,
129
+ mimeType,
130
+ })}
131
+ {requestExample &&
132
+ ResponseExample({
133
+ responseExample: requestExample,
134
+ mimeType,
135
+ })}
136
+ </SchemaTabs>
103
137
  </TabItem>
104
138
  );
105
139
  })}
@@ -108,8 +142,10 @@ const RequestSchemaComponent: React.FC<Props> = ({ title, body, style }) => {
108
142
  }
109
143
 
110
144
  const randomFirstKey = mimeTypes[0];
111
- const firstBody =
112
- body.content[randomFirstKey].schema ?? body.content![randomFirstKey];
145
+ const mediaTypeObject = body.content[randomFirstKey];
146
+ const firstBody = mediaTypeObject?.schema ?? body.content![randomFirstKey];
147
+ const requestExamples = mediaTypeObject?.examples;
148
+ const requestExample = mediaTypeObject?.example;
113
149
 
114
150
  if (firstBody === undefined) {
115
151
  return null;
@@ -119,50 +155,72 @@ const RequestSchemaComponent: React.FC<Props> = ({ title, body, style }) => {
119
155
  <MimeTabs className="openapi-tabs__mime" schemaType="request">
120
156
  {/* @ts-ignore */}
121
157
  <TabItem label={randomFirstKey} value={`${randomFirstKey}-schema`}>
122
- <Details
123
- className="openapi-markdown__details mime"
124
- data-collapsed={false}
125
- open={true}
126
- style={style}
127
- summary={
128
- <>
129
- <summary>
130
- <h3 className="openapi-markdown__details-summary-header-body">
131
- {translate({
132
- id: OPENAPI_REQUEST.BODY_TITLE,
133
- message: title,
134
- })}
135
- {firstBody.type === "array" && (
136
- <span style={{ opacity: "0.6" }}> array</span>
137
- )}
138
- {body.required && (
139
- <strong className="openapi-schema__required">
158
+ <SchemaTabs className="openapi-tabs__schema">
159
+ {/* @ts-ignore */}
160
+ <TabItem key={title} label={title} value={title}>
161
+ <Details
162
+ className="openapi-markdown__details mime"
163
+ data-collapsed={false}
164
+ open={true}
165
+ style={style}
166
+ summary={
167
+ <>
168
+ <summary>
169
+ <h3 className="openapi-markdown__details-summary-header-body">
140
170
  {translate({
141
- id: OPENAPI_SCHEMA_ITEM.REQUIRED,
142
- message: "required",
171
+ id: OPENAPI_REQUEST.BODY_TITLE,
172
+ message: title,
143
173
  })}
144
- </strong>
145
- )}
146
- </h3>
147
- </summary>
148
- </>
149
- }
150
- >
151
- <div style={{ textAlign: "left", marginLeft: "1rem" }}>
152
- {body.description && (
153
- <div style={{ marginTop: "1rem", marginBottom: "1rem" }}>
154
- <Markdown>{body.description}</Markdown>
174
+ {firstBody.type === "array" && (
175
+ <span style={{ opacity: "0.6" }}> array</span>
176
+ )}
177
+ {body.required && (
178
+ <strong className="openapi-schema__required">
179
+ {translate({
180
+ id: OPENAPI_SCHEMA_ITEM.REQUIRED,
181
+ message: "required",
182
+ })}
183
+ </strong>
184
+ )}
185
+ </h3>
186
+ </summary>
187
+ </>
188
+ }
189
+ >
190
+ <div style={{ textAlign: "left", marginLeft: "1rem" }}>
191
+ {body.description && (
192
+ <div style={{ marginTop: "1rem", marginBottom: "1rem" }}>
193
+ <Markdown>{body.description}</Markdown>
194
+ </div>
195
+ )}
155
196
  </div>
156
- )}
157
- </div>
158
- <ul style={{ marginLeft: "1rem" }}>
159
- <SchemaNode
160
- schema={firstBody}
161
- schemaType="request"
162
- schemaPath="requestBody"
163
- />
164
- </ul>
165
- </Details>
197
+ <ul style={{ marginLeft: "1rem" }}>
198
+ <SchemaNode
199
+ schema={firstBody}
200
+ schemaType="request"
201
+ schemaPath="requestBody"
202
+ />
203
+ </ul>
204
+ </Details>
205
+ </TabItem>
206
+ {firstBody &&
207
+ !requestExample &&
208
+ !requestExamples &&
209
+ ExampleFromSchema({
210
+ schema: firstBody,
211
+ mimeType: randomFirstKey,
212
+ })}
213
+ {requestExamples &&
214
+ ResponseExamples({
215
+ responseExamples: requestExamples,
216
+ mimeType: randomFirstKey,
217
+ })}
218
+ {requestExample &&
219
+ ResponseExample({
220
+ responseExample: requestExample,
221
+ mimeType: randomFirstKey,
222
+ })}
223
+ </SchemaTabs>
166
224
  </TabItem>
167
225
  </MimeTabs>
168
226
  );
@@ -9,7 +9,7 @@ import React from "react";
9
9
 
10
10
  import SchemaItem from "@theme/SchemaItem";
11
11
 
12
- import { getQualifierMessage, getSchemaName } from "../../markdown/schema";
12
+ import { getSchemaName } from "../../markdown/schema";
13
13
 
14
14
  interface ResponseHeadersProps {
15
15
  description?: string;
@@ -35,7 +35,6 @@ export const ResponseHeaders: React.FC<{
35
35
  name={name}
36
36
  collapsible={false}
37
37
  schemaName={getSchemaName(schema)}
38
- qualifierMessage={getQualifierMessage(schema)}
39
38
  schema={schema}
40
39
  discriminator={false}
41
40
  children={null}
@@ -21,16 +21,11 @@ import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
21
21
  // eslint-disable-next-line import/no-extraneous-dependencies
22
22
  import { merge } from "allof-merge";
23
23
  import clsx from "clsx";
24
- import {
25
- getQualifierMessage,
26
- getSchemaName,
27
- } from "docusaurus-plugin-openapi-docs/lib/markdown/schema";
28
- import type {
29
- SchemaObject,
30
- SchemaType,
31
- } from "docusaurus-plugin-openapi-docs/src/openapi/types";
32
24
  import isEmpty from "lodash/isEmpty";
33
25
 
26
+ import { getQualifierMessage, getSchemaName } from "../../markdown/schema";
27
+ import type { SchemaObject } from "../../types.d";
28
+
34
29
  // eslint-disable-next-line import/no-extraneous-dependencies
35
30
  // const jsonSchemaMergeAllOf = require("json-schema-merge-allof");
36
31
 
@@ -41,7 +36,81 @@ const mergeAllOf = (allOf: any) => {
41
36
 
42
37
  const mergedSchemas = merge(allOf, { onMergeError });
43
38
 
44
- return mergedSchemas;
39
+ return mergedSchemas ?? {};
40
+ };
41
+
42
+ /**
43
+ * Recursively searches for a property in a schema, including nested
44
+ * oneOf, anyOf, and allOf structures. This is needed for discriminators
45
+ * where the property definition may be in a nested schema.
46
+ */
47
+ const findProperty = (
48
+ schema: SchemaObject,
49
+ propertyName: string
50
+ ): SchemaObject | undefined => {
51
+ // Check direct properties first
52
+ if (schema.properties?.[propertyName]) {
53
+ return schema.properties[propertyName];
54
+ }
55
+
56
+ // Search in oneOf schemas
57
+ if (schema.oneOf) {
58
+ for (const subschema of schema.oneOf) {
59
+ const found = findProperty(subschema as SchemaObject, propertyName);
60
+ if (found) return found;
61
+ }
62
+ }
63
+
64
+ // Search in anyOf schemas
65
+ if (schema.anyOf) {
66
+ for (const subschema of schema.anyOf) {
67
+ const found = findProperty(subschema as SchemaObject, propertyName);
68
+ if (found) return found;
69
+ }
70
+ }
71
+
72
+ // Search in allOf schemas
73
+ if (schema.allOf) {
74
+ for (const subschema of schema.allOf) {
75
+ const found = findProperty(subschema as SchemaObject, propertyName);
76
+ if (found) return found;
77
+ }
78
+ }
79
+
80
+ return undefined;
81
+ };
82
+
83
+ /**
84
+ * Recursively searches for a discriminator in a schema, including nested
85
+ * oneOf, anyOf, and allOf structures.
86
+ */
87
+ const findDiscriminator = (schema: SchemaObject): any | undefined => {
88
+ if (schema.discriminator) {
89
+ return schema.discriminator;
90
+ }
91
+
92
+ if (schema.oneOf) {
93
+ for (const subschema of schema.oneOf) {
94
+ const found = findDiscriminator(subschema as SchemaObject);
95
+ if (found) return found;
96
+ }
97
+ }
98
+
99
+ if (schema.anyOf) {
100
+ for (const subschema of schema.anyOf) {
101
+ const found = findDiscriminator(subschema as SchemaObject);
102
+ if (found) return found;
103
+ }
104
+ }
105
+
106
+ if (schema.allOf) {
107
+ for (const subschema of schema.allOf) {
108
+ const found = findDiscriminator(subschema as SchemaObject);
109
+ if (found) return found;
110
+ }
111
+ }
112
+
113
+ return undefined;
45
114
  };
46
115
 
47
116
  interface MarkdownProps {
@@ -140,6 +209,14 @@ const AnyOneOf: React.FC<SchemaProps> = ({
140
209
  schemaPath,
141
210
  }) => {
142
211
  const key = schema.oneOf ? "oneOf" : "anyOf";
212
+ const schemaArray = schema[key];
213
+
214
+ // Empty oneOf/anyOf arrays are valid in OpenAPI specs but would cause the
215
+ // Tabs component to throw "requires at least one TabItem". Return null instead.
216
+ if (!schemaArray || !Array.isArray(schemaArray) || schemaArray.length === 0) {
217
+ return null;
218
+ }
219
+
143
220
  const type = schema.oneOf
144
221
  ? translate({ id: OPENAPI_SCHEMA_ITEM.ONE_OF, message: "oneOf" })
145
222
  : translate({ id: OPENAPI_SCHEMA_ITEM.ANY_OF, message: "anyOf" });
@@ -222,7 +299,6 @@ const AnyOneOf: React.FC<SchemaProps> = ({
222
299
  collapsible={false}
223
300
  name={undefined}
224
301
  schemaName={computedSchemaName}
225
- qualifierMessage={getQualifierMessage(anyOneSchema)}
226
302
  schema={anyOneSchema}
227
303
  discriminator={false}
228
304
  children={null}
@@ -239,7 +315,6 @@ const AnyOneOf: React.FC<SchemaProps> = ({
239
315
  collapsible={false}
240
316
  name={undefined}
241
317
  schemaName={computedSchemaName}
242
- qualifierMessage={getQualifierMessage(anyOneSchema)}
243
318
  schema={anyOneSchema}
244
319
  discriminator={false}
245
320
  children={null}
@@ -313,13 +388,17 @@ const Properties: React.FC<SchemaProps> = ({
313
388
  discriminator["mapping"] = inferredMapping;
314
389
  }
315
390
  if (Object.keys(schema.properties as {}).length === 0) {
391
+ // Hide placeholder only for discriminator cleanup artifacts; preserve
392
+ // empty object rendering for schemas that intentionally define no properties.
393
+ if (discriminator) {
394
+ return null;
395
+ }
316
396
  return (
317
397
  <SchemaItem
318
398
  collapsible={false}
319
399
  name=""
320
400
  required={false}
321
401
  schemaName="object"
322
- qualifierMessage={undefined}
323
402
  schema={{}}
324
403
  />
325
404
  );
@@ -442,10 +521,9 @@ const DiscriminatorNode: React.FC<DiscriminatorNodeProps> = ({
442
521
  let discriminatedSchemas: any = {};
443
522
  let inferredMapping: any = {};
444
523
 
445
- // default to empty object if no parent-level properties exist
446
- const discriminatorProperty = schema.properties
447
- ? schema.properties![discriminator.propertyName]
448
- : {};
524
+ // Search for the discriminator property in the schema, including nested structures
525
+ const discriminatorProperty =
526
+ findProperty(schema, discriminator.propertyName) ?? {};
449
527
 
450
528
  if (schema.allOf) {
451
529
  const mergedSchemas = mergeAllOf(schema) as SchemaObject;
@@ -533,7 +611,6 @@ const AdditionalProperties: React.FC<SchemaProps> = ({
533
611
  name="property name*"
534
612
  required={false}
535
613
  schemaName="any"
536
- qualifierMessage={getQualifierMessage(schema)}
537
614
  schema={schema}
538
615
  collapsible={false}
539
616
  discriminator={false}
@@ -579,7 +656,6 @@ const AdditionalProperties: React.FC<SchemaProps> = ({
579
656
  name="property name*"
580
657
  required={false}
581
658
  schemaName={schemaName}
582
- qualifierMessage={getQualifierMessage(schema)}
583
659
  schema={additionalProperties}
584
660
  collapsible={false}
585
661
  discriminator={false}
@@ -689,7 +765,6 @@ const Items: React.FC<{
689
765
  collapsible={false}
690
766
  name="" // No name for array items
691
767
  schemaName={getSchemaName(itemsSchema)}
692
- qualifierMessage={getQualifierMessage(itemsSchema)}
693
768
  schema={itemsSchema}
694
769
  discriminator={false}
695
770
  children={null}
@@ -849,7 +924,6 @@ const SchemaEdge: React.FC<SchemaEdgeProps> = ({
849
924
  Array.isArray(required) ? required.includes(name) : required
850
925
  }
851
926
  schemaName={schema.allOf[0]}
852
- qualifierMessage={undefined}
853
927
  schema={schema.allOf[0]}
854
928
  discriminator={false}
855
929
  children={null}
@@ -921,7 +995,6 @@ const SchemaEdge: React.FC<SchemaEdgeProps> = ({
921
995
  name={name}
922
996
  required={Array.isArray(required) ? required.includes(name) : required}
923
997
  schemaName={mergedSchemaName}
924
- qualifierMessage={getQualifierMessage(mergedSchemas)}
925
998
  schema={mergedSchemas}
926
999
  discriminator={false}
927
1000
  children={null}
@@ -935,7 +1008,6 @@ const SchemaEdge: React.FC<SchemaEdgeProps> = ({
935
1008
  name={name}
936
1009
  required={Array.isArray(required) ? required.includes(name) : required}
937
1010
  schemaName={schemaName}
938
- qualifierMessage={getQualifierMessage(schema)}
939
1011
  schema={schema}
940
1012
  discriminator={false}
941
1013
  children={null}
@@ -997,12 +1069,24 @@ const SchemaNode: React.FC<SchemaProps> = ({
997
1069
  return null;
998
1070
  }
999
1071
 
1000
- if (schema.discriminator) {
1001
- const { discriminator } = schema;
1072
+ // Resolve discriminator recursively so nested oneOf/anyOf/allOf compositions
1073
+ // can still render discriminator tabs.
1074
+ let workingSchema = schema;
1075
+ const resolvedDiscriminator =
1076
+ schema.discriminator ?? findDiscriminator(schema);
1077
+ if (schema.allOf && !schema.discriminator && resolvedDiscriminator) {
1078
+ workingSchema = mergeAllOf(schema) as SchemaObject;
1079
+ }
1080
+ if (!workingSchema.discriminator && resolvedDiscriminator) {
1081
+ workingSchema.discriminator = resolvedDiscriminator;
1082
+ }
1083
+
1084
+ if (workingSchema.discriminator) {
1085
+ const { discriminator } = workingSchema;
1002
1086
  return (
1003
1087
  <DiscriminatorNode
1004
1088
  discriminator={discriminator}
1005
- schema={schema}
1089
+ schema={workingSchema}
1006
1090
  schemaType={schemaType}
1007
1091
  />
1008
1092
  );
@@ -1127,7 +1211,6 @@ const SchemaNode: React.FC<SchemaProps> = ({
1127
1211
  name={schema.type}
1128
1212
  required={Boolean(schema.required)}
1129
1213
  schemaName={schemaName}
1130
- qualifierMessage={getQualifierMessage(schema)}
1131
1214
  schema={schema}
1132
1215
  discriminator={false}
1133
1216
  children={null}
@@ -1140,7 +1223,9 @@ const SchemaNode: React.FC<SchemaProps> = ({
1140
1223
 
1141
1224
  export default SchemaNode;
1142
1225
 
1143
- type PrimitiveSchemaType = Exclude<SchemaType, "object" | "array">;
1226
+ type PrimitiveSchemaType =
1227
+ | Exclude<NonNullable<SchemaObject["type"]>, "object" | "array">
1228
+ | "null";
1144
1229
 
1145
1230
  const PRIMITIVE_TYPES: Record<PrimitiveSchemaType, true> = {
1146
1231
  string: true,
@@ -13,6 +13,7 @@ import Markdown from "@theme/Markdown";
13
13
  import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
14
14
  import clsx from "clsx";
15
15
 
16
+ import { getQualifierMessage } from "../../markdown/schema";
16
17
  import { guard } from "../../markdown/utils";
17
18
 
18
19
  export interface Props {
@@ -130,7 +131,11 @@ export default function SchemaItem(props: Props) {
130
131
  </>
131
132
  ));
132
133
 
133
- const renderQualifierMessage = guard(qualifierMessage, (message) => (
134
+ // Generate qualifierMessage from schema if not provided
135
+ const effectiveQualifierMessage =
136
+ qualifierMessage ?? (schema ? getQualifierMessage(schema) : undefined);
137
+
138
+ const renderQualifierMessage = guard(effectiveQualifierMessage, (message) => (
134
139
  <>
135
140
  <Markdown>{message}</Markdown>
136
141
  </>
@@ -16,11 +16,12 @@ import React, {
16
16
 
17
17
  import {
18
18
  sanitizeTabsChildren,
19
+ type TabItemProps,
19
20
  TabProps,
21
+ TabsProvider,
20
22
  useScrollPositionBlocker,
21
- useTabs,
23
+ useTabsContextValue,
22
24
  } from "@docusaurus/theme-common/internal";
23
- import { TabItemProps } from "@docusaurus/theme-common/lib/utils/tabsUtils";
24
25
  import useIsBrowser from "@docusaurus/useIsBrowser";
25
26
  import clsx from "clsx";
26
27
  import flatten from "lodash/flatten";
@@ -40,7 +41,7 @@ function TabList({
40
41
  selectValue,
41
42
  tabValues,
42
43
  onChange,
43
- }: SchemaTabsProps & ReturnType<typeof useTabs>) {
44
+ }: SchemaTabsProps & ReturnType<typeof useTabsContextValue>) {
44
45
  const tabRefs: (HTMLLIElement | null)[] = [];
45
46
  const { blockElementScrollPositionUntilNextRender } =
46
47
  useScrollPositionBlocker();
@@ -188,7 +189,7 @@ function TabContent({
188
189
  lazy,
189
190
  children,
190
191
  selectedValue,
191
- }: SchemaTabsProps & ReturnType<typeof useTabs>) {
192
+ }: SchemaTabsProps & ReturnType<typeof useTabsContextValue>) {
192
193
  const childTabs = (Array.isArray(children) ? children : [children]).filter(
193
194
  Boolean
194
195
  ) as ReactElement<TabItemProps>[];
@@ -203,28 +204,48 @@ function TabContent({
203
204
  }
204
205
  return cloneElement(selectedTabItem, { className: "margin-top--md" });
205
206
  }
206
- return (
207
- <div className="margin-top--md">
208
- {childTabs.map((tabItem, i) =>
209
- cloneElement(tabItem, {
210
- key: i,
211
- hidden: tabItem.props.value !== selectedValue,
212
- })
213
- )}
214
- </div>
215
- );
207
+ return <div className="margin-top--md">{childTabs}</div>;
216
208
  }
217
209
  function TabsComponent(props: SchemaTabsProps): React.JSX.Element {
218
- const tabs = useTabs(props);
210
+ const tabs = useTabsContextValue(props);
219
211
  return (
220
- <div className="openapi-tabs__schema-container">
221
- <TabList {...props} {...tabs} />
222
- <TabContent {...props} {...tabs} />
223
- </div>
212
+ <TabsProvider value={tabs}>
213
+ <div className="openapi-tabs__schema-container">
214
+ <TabList {...props} {...tabs} />
215
+ <TabContent {...props} {...tabs} />
216
+ </div>
217
+ </TabsProvider>
224
218
  );
225
219
  }
226
- export default function SchemaTabs(props: SchemaTabsProps): React.JSX.Element {
220
+ export default function SchemaTabs(
221
+ props: SchemaTabsProps
222
+ ): React.JSX.Element | null {
227
223
  const isBrowser = useIsBrowser();
224
+
225
+ const children = Array.isArray(props.children)
226
+ ? props.children.filter(Boolean)
227
+ : props.children
228
+ ? [props.children]
229
+ : [];
230
+
231
+ if (children.length === 0) {
232
+ return null;
233
+ }
234
+
235
+ let sanitizedChildren;
236
+ try {
237
+ sanitizedChildren = sanitizeTabsChildren(children);
238
+ } catch {
239
+ return null;
240
+ }
241
+
242
+ if (
243
+ !sanitizedChildren ||
244
+ (Array.isArray(sanitizedChildren) && sanitizedChildren.length === 0)
245
+ ) {
246
+ return null;
247
+ }
248
+
228
249
  return (
229
250
  <TabsComponent
230
251
  // Remount tabs after hydration
@@ -232,7 +253,7 @@ export default function SchemaTabs(props: SchemaTabsProps): React.JSX.Element {
232
253
  key={String(isBrowser)}
233
254
  {...props}
234
255
  >
235
- {sanitizeTabsChildren(props.children)}
256
+ {sanitizedChildren}
236
257
  </TabsComponent>
237
258
  );
238
259
  }
@@ -9,6 +9,7 @@
9
9
  @use "./ApiExplorer/FloatingButton/FloatingButton";
10
10
  @use "./ApiExplorer/FormFileUpload/FormFileUpload";
11
11
  @use "./ApiExplorer/FormItem/FormItem";
12
+ @use "./ApiExplorer/FormLabel/FormLabel";
12
13
  @use "./ApiExplorer/FormMultiSelect/FormMultiSelect";
13
14
  @use "./ApiExplorer/FormSelect/FormSelect";
14
15
  @use "./ApiExplorer/FormTextInput/FormTextInput";