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
@@ -233,25 +233,44 @@ function TabContent({ lazy, children, selectedValue }) {
233
233
  return react_1.default.createElement(
234
234
  "div",
235
235
  { className: "margin-top--md" },
236
- childTabs.map((tabItem, i) =>
237
- (0, react_1.cloneElement)(tabItem, {
238
- key: i,
239
- hidden: tabItem.props.value !== selectedValue,
240
- })
241
- )
236
+ childTabs
242
237
  );
243
238
  }
244
239
  function TabsComponent(props) {
245
- const tabs = (0, internal_1.useTabs)(props);
240
+ const tabs = (0, internal_1.useTabsContextValue)(props);
246
241
  return react_1.default.createElement(
247
- "div",
248
- { className: "openapi-tabs__schema-container" },
249
- react_1.default.createElement(TabList, { ...props, ...tabs }),
250
- react_1.default.createElement(TabContent, { ...props, ...tabs })
242
+ internal_1.TabsProvider,
243
+ { value: tabs },
244
+ react_1.default.createElement(
245
+ "div",
246
+ { className: "openapi-tabs__schema-container" },
247
+ react_1.default.createElement(TabList, { ...props, ...tabs }),
248
+ react_1.default.createElement(TabContent, { ...props, ...tabs })
249
+ )
251
250
  );
252
251
  }
253
252
  function SchemaTabs(props) {
254
253
  const isBrowser = (0, useIsBrowser_1.default)();
254
+ const children = Array.isArray(props.children)
255
+ ? props.children.filter(Boolean)
256
+ : props.children
257
+ ? [props.children]
258
+ : [];
259
+ if (children.length === 0) {
260
+ return null;
261
+ }
262
+ let sanitizedChildren;
263
+ try {
264
+ sanitizedChildren = (0, internal_1.sanitizeTabsChildren)(children);
265
+ } catch {
266
+ return null;
267
+ }
268
+ if (
269
+ !sanitizedChildren ||
270
+ (Array.isArray(sanitizedChildren) && sanitizedChildren.length === 0)
271
+ ) {
272
+ return null;
273
+ }
255
274
  return react_1.default.createElement(
256
275
  TabsComponent,
257
276
  // Remount tabs after hydration
@@ -262,6 +281,6 @@ function SchemaTabs(props) {
262
281
  key: String(isBrowser),
263
282
  ...props,
264
283
  },
265
- (0, internal_1.sanitizeTabsChildren)(props.children)
284
+ sanitizedChildren
266
285
  );
267
286
  }
@@ -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";
@@ -60,6 +60,8 @@ export declare const OPENAPI_SCHEMA: {
60
60
  NO_SCHEMA: string;
61
61
  };
62
62
  export declare const OPENAPI_SCHEMA_ITEM: {
63
+ CHARACTERS: string;
64
+ NON_EMPTY: string;
63
65
  REQUIRED: string;
64
66
  DEPRECATED: string;
65
67
  NULLABLE: string;
@@ -71,6 +73,7 @@ export declare const OPENAPI_SCHEMA_ITEM: {
71
73
  ENUM_VALUE: string;
72
74
  ENUM_DESCRIPTION: string;
73
75
  POSSIBLE_VALUES: string;
76
+ EXPRESSION: string;
74
77
  ONE_OF: string;
75
78
  ANY_OF: string;
76
79
  };
@@ -84,6 +84,8 @@ exports.OPENAPI_SCHEMA = {
84
84
  NO_SCHEMA: "theme.openapi.schema.noSchema",
85
85
  };
86
86
  exports.OPENAPI_SCHEMA_ITEM = {
87
+ CHARACTERS: "theme.openapi.schemaItem.characters",
88
+ NON_EMPTY: "theme.openapi.schemaItem.nonEmpty",
87
89
  REQUIRED: "theme.openapi.schemaItem.required",
88
90
  DEPRECATED: "theme.openapi.schemaItem.deprecated",
89
91
  NULLABLE: "theme.openapi.schemaItem.nullable",
@@ -95,6 +97,7 @@ exports.OPENAPI_SCHEMA_ITEM = {
95
97
  ENUM_VALUE: "theme.openapi.schemaItem.enumValue",
96
98
  ENUM_DESCRIPTION: "theme.openapi.schemaItem.enumDescription",
97
99
  POSSIBLE_VALUES: "theme.openapi.schemaItem.possibleValues",
100
+ EXPRESSION: "theme.openapi.schemaItem.expression",
98
101
  ONE_OF: "theme.openapi.schemaItem.oneOf",
99
102
  ANY_OF: "theme.openapi.schemaItem.anyOf",
100
103
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docusaurus-theme-openapi-docs",
3
3
  "description": "OpenAPI theme for Docusaurus.",
4
- "version": "4.7.1",
4
+ "version": "5.0.0",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "openapi",
@@ -28,9 +28,9 @@
28
28
  "watch": "concurrently --names \"lib,lib-next,tsc\" --kill-others \"yarn babel:lib --watch\" \"yarn babel:lib-next --watch\" \"yarn tsc --watch\""
29
29
  },
30
30
  "devDependencies": {
31
- "@docusaurus/theme-classic": "^3.5.0",
32
- "@docusaurus/theme-common": "^3.5.0",
33
- "@docusaurus/types": "^3.5.0",
31
+ "@docusaurus/theme-classic": "^3.10.0",
32
+ "@docusaurus/theme-common": "^3.10.0",
33
+ "@docusaurus/types": "^3.10.0",
34
34
  "@types/crypto-js": "^4.2.2",
35
35
  "@types/file-saver": "^2.0.7",
36
36
  "@types/lodash": "^4.17.20",
@@ -38,7 +38,7 @@
38
38
  "@types/postman-collection": "^3.5.11",
39
39
  "@types/react-modal": "^3.16.3",
40
40
  "concurrently": "^9.2.0",
41
- "docusaurus-plugin-openapi-docs": "^4.7.1",
41
+ "docusaurus-plugin-openapi-docs": "^5.0.0",
42
42
  "docusaurus-plugin-sass": "^0.2.6",
43
43
  "eslint-plugin-prettier": "^5.5.1"
44
44
  },
@@ -53,6 +53,7 @@
53
53
  "file-saver": "^2.0.5",
54
54
  "lodash": "^4.17.21",
55
55
  "pako": "^2.1.0",
56
+ "path-browserify": "^1.0.1",
56
57
  "postman-code-generators": "^2.0.0",
57
58
  "postman-collection": "^5.0.2",
58
59
  "prism-react-renderer": "^2.4.1",
@@ -72,8 +73,8 @@
72
73
  "xml-formatter": "^3.6.6"
73
74
  },
74
75
  "peerDependencies": {
75
- "@docusaurus/theme-common": "^3.5.0",
76
- "docusaurus-plugin-openapi-docs": "^4.0.0",
76
+ "@docusaurus/theme-common": "^3.10.0",
77
+ "docusaurus-plugin-openapi-docs": "^5.0.0",
77
78
  "docusaurus-plugin-sass": "^0.2.3",
78
79
  "react": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0",
79
80
  "react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -81,5 +82,5 @@
81
82
  "engines": {
82
83
  "node": ">=14"
83
84
  },
84
- "gitHead": "0a89b9db05458cff9591eb065160e26c05f64a18"
85
+ "gitHead": "516bd48c628fb16b7fe90d8f996168ab75ce5562"
85
86
  }
package/src/index.ts CHANGED
@@ -42,6 +42,7 @@ export default function docusaurusThemeOpenAPI(): Plugin<void> {
42
42
  resolve: {
43
43
  fallback: {
44
44
  buffer: require.resolve("buffer/"),
45
+ path: require.resolve("path-browserify"),
45
46
  },
46
47
  },
47
48
  plugins: [
@@ -71,6 +72,7 @@ export default function docusaurusThemeOpenAPI(): Plugin<void> {
71
72
  resolve: {
72
73
  fallback: {
73
74
  buffer: require.resolve("buffer/"),
75
+ path: require.resolve("path-browserify"),
74
76
  },
75
77
  },
76
78
  plugins: [
@@ -10,6 +10,44 @@ import { translate } from "@docusaurus/Translate";
10
10
  import { OPENAPI_SCHEMA_ITEM } from "../theme/translationIds";
11
11
  import { SchemaObject } from "../types";
12
12
 
13
+ /**
14
+ * Extracts enum values from a schema, including when wrapped in allOf.
15
+ */
16
+ function getEnumFromSchema(schema: SchemaObject): any[] | undefined {
17
+ if (schema.enum) {
18
+ return schema.enum;
19
+ }
20
+
21
+ if (schema.allOf && Array.isArray(schema.allOf)) {
22
+ for (const item of schema.allOf) {
23
+ if (item.enum) {
24
+ return item.enum;
25
+ }
26
+ }
27
+ }
28
+
29
+ return undefined;
30
+ }
31
+
32
+ /**
33
+ * Extracts the type from a schema, including when wrapped in allOf.
34
+ */
35
+ function getTypeFromSchema(schema: SchemaObject): string | undefined {
36
+ if (schema.type) {
37
+ return schema.type as string;
38
+ }
39
+
40
+ if (schema.allOf && Array.isArray(schema.allOf)) {
41
+ for (const item of schema.allOf) {
42
+ if (item.type) {
43
+ return item.type as string;
44
+ }
45
+ }
46
+ }
47
+
48
+ return undefined;
49
+ }
50
+
13
51
  function prettyName(schema: SchemaObject, circular?: boolean) {
14
52
  // Handle enum-only schemas (valid in JSON Schema)
15
53
  // When enum is present without explicit type, treat as string
@@ -28,6 +66,12 @@ function prettyName(schema: SchemaObject, circular?: boolean) {
28
66
  return schema.allOf[0];
29
67
  }
30
68
  }
69
+ // Check if allOf contains an enum - if so, return the type from allOf
70
+ const enumFromAllOf = getEnumFromSchema(schema);
71
+ if (enumFromAllOf) {
72
+ const typeFromAllOf = getTypeFromSchema(schema);
73
+ return typeFromAllOf ?? "string";
74
+ }
31
75
  return "object";
32
76
  }
33
77
 
@@ -92,11 +136,11 @@ export function getQualifierMessage(schema?: SchemaObject): string | undefined {
92
136
 
93
137
  let qualifierGroups = [];
94
138
 
95
- if (schema.items && schema.items.enum) {
96
- if (schema.items.enum) {
97
- qualifierGroups.push(
98
- `[${schema.items.enum.map((e) => `\`${e}\``).join(", ")}]`
99
- );
139
+ // Check for enum in array items (directly or inside allOf)
140
+ if (schema.items) {
141
+ const itemsEnum = getEnumFromSchema(schema.items as SchemaObject);
142
+ if (itemsEnum) {
143
+ qualifierGroups.push(`[${itemsEnum.map((e) => `\`${e}\``).join(", ")}]`);
100
144
  }
101
145
  }
102
146
 
@@ -104,14 +148,22 @@ export function getQualifierMessage(schema?: SchemaObject): string | undefined {
104
148
  let lengthQualifier = "";
105
149
  let minLength;
106
150
  let maxLength;
151
+ const charactersMessage = translate({
152
+ id: OPENAPI_SCHEMA_ITEM.CHARACTERS,
153
+ message: "characters",
154
+ });
155
+ const nonEmptyMessage = translate({
156
+ id: OPENAPI_SCHEMA_ITEM.NON_EMPTY,
157
+ message: "non-empty",
158
+ });
107
159
  if (schema.minLength && schema.minLength > 1) {
108
- minLength = `\`>= ${schema.minLength} characters\``;
160
+ minLength = `\`>= ${schema.minLength} ${charactersMessage}\``;
109
161
  }
110
162
  if (schema.minLength && schema.minLength === 1) {
111
- minLength = `\`non-empty\``;
163
+ minLength = `\`${nonEmptyMessage}\``;
112
164
  }
113
165
  if (schema.maxLength) {
114
- maxLength = `\`<= ${schema.maxLength} characters\``;
166
+ maxLength = `\`<= ${schema.maxLength} ${charactersMessage}\``;
115
167
  }
116
168
 
117
169
  if (minLength && !maxLength) {
@@ -165,9 +217,11 @@ export function getQualifierMessage(schema?: SchemaObject): string | undefined {
165
217
  }
166
218
 
167
219
  if (schema.pattern) {
168
- qualifierGroups.push(
169
- `Value must match regular expression \`${schema.pattern}\``
170
- );
220
+ const expressionMessage = translate({
221
+ id: OPENAPI_SCHEMA_ITEM.EXPRESSION,
222
+ message: "Value must match regular expression",
223
+ });
224
+ qualifierGroups.push(`${expressionMessage} \`${schema.pattern}\``);
171
225
  }
172
226
 
173
227
  // Check if discriminator mapping
@@ -177,8 +231,10 @@ export function getQualifierMessage(schema?: SchemaObject): string | undefined {
177
231
  qualifierGroups.push(`[${values.map((e) => `\`${e}\``).join(", ")}]`);
178
232
  }
179
233
 
180
- if (schema.enum) {
181
- qualifierGroups.push(`[${schema.enum.map((e) => `\`${e}\``).join(", ")}]`);
234
+ // Check for enum directly on schema or inside allOf
235
+ const schemaEnum = getEnumFromSchema(schema);
236
+ if (schemaEnum) {
237
+ qualifierGroups.push(`[${schemaEnum.map((e) => `\`${e}\``).join(", ")}]`);
182
238
  }
183
239
 
184
240
  if (schema.minItems) {
@@ -23,8 +23,9 @@ function Accept() {
23
23
  }
24
24
 
25
25
  return (
26
- <FormItem label="Accept">
26
+ <FormItem>
27
27
  <FormSelect
28
+ label="Accept"
28
29
  value={value}
29
30
  options={options}
30
31
  onChange={(e: any) => dispatch(setAccept(e.target.value))}
@@ -34,13 +34,12 @@ function Authorization() {
34
34
  return (
35
35
  <div>
36
36
  {optionKeys.length > 1 && (
37
- <FormItem
38
- label={translate({
39
- id: OPENAPI_AUTH.SECURITY_SCHEME,
40
- message: "Security Scheme",
41
- })}
42
- >
37
+ <FormItem>
43
38
  <FormSelect
39
+ label={translate({
40
+ id: OPENAPI_AUTH.SECURITY_SCHEME,
41
+ message: "Security Scheme",
42
+ })}
44
43
  options={optionKeys}
45
44
  value={selected}
46
45
  onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
@@ -52,14 +51,12 @@ function Authorization() {
52
51
  {selectedAuth.map((a: any) => {
53
52
  if (a.type === "http" && a.scheme === "bearer") {
54
53
  return (
55
- <FormItem
56
- label={translate({
57
- id: OPENAPI_AUTH.BEARER_TOKEN,
58
- message: "Bearer Token",
59
- })}
60
- key={a.key + "-bearer"}
61
- >
54
+ <FormItem key={a.key + "-bearer"}>
62
55
  <FormTextInput
56
+ label={translate({
57
+ id: OPENAPI_AUTH.BEARER_TOKEN,
58
+ message: "Bearer Token",
59
+ })}
63
60
  placeholder={translate({
64
61
  id: OPENAPI_AUTH.BEARER_TOKEN,
65
62
  message: "Bearer Token",
@@ -83,14 +80,12 @@ function Authorization() {
83
80
 
84
81
  if (a.type === "oauth2") {
85
82
  return (
86
- <FormItem
87
- label={translate({
88
- id: OPENAPI_AUTH.BEARER_TOKEN,
89
- message: "Bearer Token",
90
- })}
91
- key={a.key + "-oauth2"}
92
- >
83
+ <FormItem key={a.key + "-oauth2"}>
93
84
  <FormTextInput
85
+ label={translate({
86
+ id: OPENAPI_AUTH.BEARER_TOKEN,
87
+ message: "Bearer Token",
88
+ })}
94
89
  placeholder={translate({
95
90
  id: OPENAPI_AUTH.BEARER_TOKEN,
96
91
  message: "Bearer Token",
@@ -115,13 +110,12 @@ function Authorization() {
115
110
  if (a.type === "http" && a.scheme === "basic") {
116
111
  return (
117
112
  <React.Fragment key={a.key + "-basic"}>
118
- <FormItem
119
- label={translate({
120
- id: OPENAPI_AUTH.USERNAME,
121
- message: "Username",
122
- })}
123
- >
113
+ <FormItem>
124
114
  <FormTextInput
115
+ label={translate({
116
+ id: OPENAPI_AUTH.USERNAME,
117
+ message: "Username",
118
+ })}
125
119
  placeholder={translate({
126
120
  id: OPENAPI_AUTH.USERNAME,
127
121
  message: "Username",
@@ -139,13 +133,12 @@ function Authorization() {
139
133
  }}
140
134
  />
141
135
  </FormItem>
142
- <FormItem
143
- label={translate({
144
- id: OPENAPI_AUTH.PASSWORD,
145
- message: "Password",
146
- })}
147
- >
136
+ <FormItem>
148
137
  <FormTextInput
138
+ label={translate({
139
+ id: OPENAPI_AUTH.PASSWORD,
140
+ message: "Password",
141
+ })}
149
142
  placeholder={translate({
150
143
  id: OPENAPI_AUTH.PASSWORD,
151
144
  message: "Password",
@@ -170,8 +163,9 @@ function Authorization() {
170
163
 
171
164
  if (a.type === "apiKey") {
172
165
  return (
173
- <FormItem label={`${a.key}`} key={a.key + "-apikey"}>
166
+ <FormItem key={a.key + "-apikey"}>
174
167
  <FormTextInput
168
+ label={`${a.key}`}
175
169
  placeholder={`${a.key}`}
176
170
  password
177
171
  value={data[a.key].apiKey ?? ""}
@@ -43,11 +43,6 @@ export default function FileArrayFormBodyItem({
43
43
  return;
44
44
  }
45
45
 
46
- let maxIndex = 0;
47
-
48
- newItems.keys().forEach((item) => {
49
- maxIndex = item > maxIndex ? item : maxIndex;
50
- });
51
46
  newItems.set(index, {
52
47
  src: `/path/to/${file.name}`,
53
48
  content: file,
@@ -5,9 +5,10 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  * ========================================================================== */
7
7
 
8
- import React from "react";
8
+ import React, { useEffect, useState } from "react";
9
9
 
10
10
  import FormFileUpload from "@theme/ApiExplorer/FormFileUpload";
11
+ import FormLabel from "@theme/ApiExplorer/FormLabel";
11
12
  import FormSelect from "@theme/ApiExplorer/FormSelect";
12
13
  import FormTextInput from "@theme/ApiExplorer/FormTextInput";
13
14
  import LiveApp from "@theme/ApiExplorer/LiveEditor";
@@ -16,70 +17,139 @@ import type { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/ty
16
17
 
17
18
  import FileArrayFormBodyItem from "../FileArrayFormBodyItem";
18
19
  import { clearFormBodyKey, setFileFormBody, setStringFormBody } from "../slice";
20
+ import { setFieldEncoding } from "../../EncodingSelection/slice";
19
21
 
20
22
  interface FormBodyItemProps {
21
23
  schemaObject: SchemaObject;
22
24
  id: string;
23
25
  schema: SchemaObject;
26
+ label?: string;
27
+ required?: boolean;
28
+ exampleValue?: SchemaObject["example"];
29
+ fieldEncoding?: string;
24
30
  }
25
31
 
26
32
  export default function FormBodyItem({
27
33
  schemaObject,
28
34
  id,
29
35
  schema,
36
+ label,
37
+ required,
38
+ exampleValue,
39
+ fieldEncoding,
30
40
  }: FormBodyItemProps): React.JSX.Element {
31
41
  const dispatch = useTypedDispatch();
32
42
 
43
+ // Parse comma-separated encoding contentType into selectable options
44
+ const encodingOptions = fieldEncoding
45
+ ? fieldEncoding
46
+ .split(",")
47
+ .map((t) => t.trim())
48
+ .filter(Boolean)
49
+ : [];
50
+ const hasMultipleEncodings = encodingOptions.length > 1;
51
+
52
+ // Initialize with the first declared content type
53
+ const [selectedEncoding, setSelectedEncoding] = useState<string>(
54
+ encodingOptions[0] ?? ""
55
+ );
56
+
57
+ // Seed Redux with the first declared encoding on mount so the code snippet
58
+ // reflects a content type immediately, even before the user interacts.
59
+ // The empty dep array is intentional: `fieldEncoding` comes from a static
60
+ // spec value that never changes for the lifetime of this component instance,
61
+ // and re-seeding on every render would fight user selections.
62
+ useEffect(() => {
63
+ if (encodingOptions[0]) {
64
+ dispatch(
65
+ setFieldEncoding({ field: id, contentType: encodingOptions[0] })
66
+ );
67
+ }
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ }, []);
70
+ const [value, setValue] = useState(() => {
71
+ let initialValue = exampleValue ?? "";
72
+
73
+ if (schemaObject.type === "object" && exampleValue) {
74
+ initialValue = JSON.stringify(exampleValue, null, 2);
75
+ }
76
+
77
+ return initialValue;
78
+ });
79
+
80
+ useEffect(() => {
81
+ if (value) {
82
+ dispatch(setStringFormBody({ key: id, value }));
83
+ } else {
84
+ dispatch(clearFormBodyKey(id));
85
+ }
86
+ // eslint-disable-next-line react-hooks/exhaustive-deps
87
+ }, []);
88
+
33
89
  if (
34
90
  schemaObject.type === "array" &&
35
91
  schemaObject.items?.format === "binary"
36
92
  ) {
37
93
  return (
38
- <FileArrayFormBodyItem id={id} description={schemaObject.description} />
94
+ <>
95
+ {label && <FormLabel label={label} required={required} />}
96
+ <FileArrayFormBodyItem id={id} description={schemaObject.description} />
97
+ </>
39
98
  );
40
99
  }
41
100
 
42
101
  if (schemaObject.format === "binary") {
43
102
  return (
44
- <FormFileUpload
45
- placeholder={schemaObject.description || id}
46
- onChange={(file: any) => {
47
- if (file === undefined) {
48
- dispatch(clearFormBodyKey(id));
49
- return;
50
- }
51
- dispatch(
52
- setFileFormBody({
53
- key: id,
54
- value: {
55
- src: `/path/to/${file.name}`,
56
- content: file,
57
- },
58
- })
59
- );
60
- }}
61
- />
103
+ <>
104
+ {label && <FormLabel label={label} required={required} />}
105
+ {hasMultipleEncodings && (
106
+ <div style={{ marginTop: "0.5rem" }}>
107
+ <FormSelect
108
+ label="Content-Type"
109
+ options={encodingOptions}
110
+ value={selectedEncoding}
111
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
112
+ const ct = e.target.value;
113
+ setSelectedEncoding(ct);
114
+ dispatch(setFieldEncoding({ field: id, contentType: ct }));
115
+ }}
116
+ />
117
+ </div>
118
+ )}
119
+ <FormFileUpload
120
+ placeholder={schemaObject.description || id}
121
+ onChange={(file: any) => {
122
+ if (file === undefined) {
123
+ dispatch(clearFormBodyKey(id));
124
+ return;
125
+ }
126
+ dispatch(
127
+ setFileFormBody({
128
+ key: id,
129
+ value: {
130
+ src: `/path/to/${file.name}`,
131
+ content: file,
132
+ },
133
+ })
134
+ );
135
+ }}
136
+ />
137
+ </>
62
138
  );
63
139
  }
64
140
 
65
- if (
66
- schemaObject.type === "object" &&
67
- (schemaObject.example || schemaObject.examples)
68
- ) {
69
- const objectExample = JSON.stringify(
70
- schemaObject.example ?? schemaObject.examples[0],
71
- null,
72
- 2
73
- );
74
-
141
+ if (schemaObject.type === "object") {
75
142
  return (
76
- <LiveApp
77
- action={(code: string) =>
78
- dispatch(setStringFormBody({ key: id, value: code }))
79
- }
80
- >
81
- {objectExample}
82
- </LiveApp>
143
+ <>
144
+ {label && <FormLabel label={label} required={required} />}
145
+ <LiveApp
146
+ action={(code: string) =>
147
+ dispatch(setStringFormBody({ key: id, value: code }))
148
+ }
149
+ >
150
+ {value}
151
+ </LiveApp>
152
+ </>
83
153
  );
84
154
  }
85
155
 
@@ -89,9 +159,13 @@ export default function FormBodyItem({
89
159
  ) {
90
160
  return (
91
161
  <FormSelect
162
+ label={label}
163
+ required={required}
164
+ value={value}
92
165
  options={["---", ...schemaObject.enum]}
93
166
  onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
94
167
  const val = e.target.value;
168
+ setValue(val);
95
169
  if (val === "---") {
96
170
  dispatch(clearFormBodyKey(id));
97
171
  } else {
@@ -109,12 +183,16 @@ export default function FormBodyItem({
109
183
  // TODO: support all the other types.
110
184
  return (
111
185
  <FormTextInput
186
+ label={label}
187
+ required={required}
188
+ value={value}
112
189
  paramName={id}
113
190
  isRequired={
114
191
  Array.isArray(schema.required) && schema.required.includes(id)
115
192
  }
116
193
  placeholder={schemaObject.description || id}
117
194
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
195
+ setValue(e.target.value);
118
196
  dispatch(setStringFormBody({ key: id, value: e.target.value }));
119
197
  }}
120
198
  />