docusaurus-theme-openapi-docs 4.7.0 → 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 (148) 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/Authorization/slice.d.ts +1 -1
  6. package/lib/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.js +0 -4
  7. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.d.ts +6 -2
  8. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.js +191 -38
  9. package/lib/theme/ApiExplorer/Body/index.d.ts +1 -1
  10. package/lib/theme/ApiExplorer/Body/index.js +86 -15
  11. package/lib/theme/ApiExplorer/Body/resolveSchemaWithSelections.d.ts +1 -1
  12. package/lib/theme/ApiExplorer/Body/slice.d.ts +136 -544
  13. package/lib/theme/ApiExplorer/CodeSnippets/index.d.ts +2 -1
  14. package/lib/theme/ApiExplorer/CodeSnippets/index.js +4 -0
  15. package/lib/theme/ApiExplorer/CodeTabs/index.js +15 -16
  16. package/lib/theme/ApiExplorer/ContentType/index.js +7 -2
  17. package/lib/theme/ApiExplorer/EncodingSelection/slice.d.ts +17 -0
  18. package/lib/theme/ApiExplorer/EncodingSelection/slice.js +29 -0
  19. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.d.ts +12 -0
  20. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.js +39 -0
  21. package/lib/theme/ApiExplorer/FormItem/_FormItem.scss +0 -5
  22. package/lib/theme/ApiExplorer/FormItem/index.d.ts +1 -4
  23. package/lib/theme/ApiExplorer/FormItem/index.js +2 -26
  24. package/lib/theme/ApiExplorer/FormLabel/_FormLabel.scss +4 -0
  25. package/lib/theme/ApiExplorer/FormLabel/index.d.ts +9 -0
  26. package/lib/theme/ApiExplorer/FormLabel/index.js +50 -0
  27. package/lib/theme/ApiExplorer/FormMultiSelect/index.d.ts +4 -1
  28. package/lib/theme/ApiExplorer/FormMultiSelect/index.js +97 -19
  29. package/lib/theme/ApiExplorer/FormSelect/index.d.ts +6 -1
  30. package/lib/theme/ApiExplorer/FormSelect/index.js +96 -15
  31. package/lib/theme/ApiExplorer/FormTextInput/index.d.ts +4 -1
  32. package/lib/theme/ApiExplorer/FormTextInput/index.js +71 -1
  33. package/lib/theme/ApiExplorer/MethodEndpoint/index.js +28 -0
  34. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.d.ts +4 -1
  35. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.js +11 -3
  36. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.d.ts +4 -1
  37. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.js +4 -1
  38. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.d.ts +4 -1
  39. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.js +6 -2
  40. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.d.ts +4 -1
  41. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.js +6 -2
  42. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.d.ts +4 -1
  43. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.js +8 -3
  44. package/lib/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +0 -9
  45. package/lib/theme/ApiExplorer/ParamOptions/index.d.ts +10 -0
  46. package/lib/theme/ApiExplorer/ParamOptions/index.js +55 -5
  47. package/lib/theme/ApiExplorer/ParamOptions/slice.d.ts +1 -1
  48. package/lib/theme/ApiExplorer/Request/_Request.scss +11 -0
  49. package/lib/theme/ApiExplorer/Request/index.d.ts +1 -1
  50. package/lib/theme/ApiExplorer/Request/index.js +19 -5
  51. package/lib/theme/ApiExplorer/Request/makeRequest.d.ts +3 -1
  52. package/lib/theme/ApiExplorer/Request/makeRequest.js +19 -3
  53. package/lib/theme/ApiExplorer/Response/_Response.scss +11 -0
  54. package/lib/theme/ApiExplorer/Response/index.d.ts +1 -1
  55. package/lib/theme/ApiExplorer/Response/index.js +98 -12
  56. package/lib/theme/ApiExplorer/SecuritySchemes/index.js +2 -2
  57. package/lib/theme/ApiExplorer/Server/index.d.ts +4 -1
  58. package/lib/theme/ApiExplorer/Server/index.js +6 -3
  59. package/lib/theme/ApiExplorer/Server/slice.d.ts +1 -1
  60. package/lib/theme/ApiExplorer/buildPostmanRequest.d.ts +5 -2
  61. package/lib/theme/ApiExplorer/buildPostmanRequest.js +46 -5
  62. package/lib/theme/ApiExplorer/index.d.ts +1 -1
  63. package/lib/theme/ApiExplorer/index.js +1 -0
  64. package/lib/theme/ApiExplorer/persistenceMiddleware.d.ts +2 -0
  65. package/lib/theme/ApiItem/hooks.d.ts +1 -0
  66. package/lib/theme/ApiItem/index.js +2 -1
  67. package/lib/theme/ApiItem/store.d.ts +6 -0
  68. package/lib/theme/ApiItem/store.js +11 -7
  69. package/lib/theme/ApiTabs/index.js +10 -11
  70. package/lib/theme/DiscriminatorTabs/index.js +10 -11
  71. package/lib/theme/MimeTabs/index.js +10 -11
  72. package/lib/theme/OperationTabs/index.js +10 -11
  73. package/lib/theme/ParamsDetails/index.js +2 -2
  74. package/lib/theme/ParamsItem/index.js +27 -0
  75. package/lib/theme/RequestSchema/index.d.ts +1 -1
  76. package/lib/theme/RequestSchema/index.js +174 -111
  77. package/lib/theme/ResponseHeaders/index.js +0 -1
  78. package/lib/theme/ResponseSchema/index.d.ts +1 -1
  79. package/lib/theme/Schema/index.d.ts +1 -1
  80. package/lib/theme/Schema/index.js +91 -23
  81. package/lib/theme/SchemaItem/index.js +6 -1
  82. package/lib/theme/SchemaTabs/index.d.ts +1 -1
  83. package/lib/theme/SchemaTabs/index.js +31 -12
  84. package/lib/theme/StatusCodes/index.d.ts +1 -1
  85. package/lib/theme/styles.scss +1 -0
  86. package/lib/theme/translationIds.d.ts +3 -0
  87. package/lib/theme/translationIds.js +3 -0
  88. package/package.json +9 -8
  89. package/src/index.ts +2 -0
  90. package/src/markdown/schema.ts +69 -13
  91. package/src/theme/ApiExplorer/Accept/index.tsx +2 -1
  92. package/src/theme/ApiExplorer/Authorization/index.tsx +27 -33
  93. package/src/theme/ApiExplorer/Authorization/slice.ts +1 -1
  94. package/src/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.tsx +2 -5
  95. package/src/theme/ApiExplorer/Body/FormBodyItem/index.tsx +119 -39
  96. package/src/theme/ApiExplorer/Body/index.tsx +88 -21
  97. package/src/theme/ApiExplorer/Body/resolveSchemaWithSelections.ts +1 -1
  98. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +9 -1
  99. package/src/theme/ApiExplorer/CodeTabs/index.tsx +19 -19
  100. package/src/theme/ApiExplorer/ContentType/index.tsx +7 -4
  101. package/src/theme/ApiExplorer/EncodingSelection/slice.ts +31 -0
  102. package/src/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.ts +43 -0
  103. package/src/theme/ApiExplorer/FormItem/_FormItem.scss +0 -5
  104. package/src/theme/ApiExplorer/FormItem/index.tsx +2 -17
  105. package/src/theme/ApiExplorer/FormLabel/_FormLabel.scss +4 -0
  106. package/src/theme/ApiExplorer/FormLabel/index.tsx +43 -0
  107. package/src/theme/ApiExplorer/FormMultiSelect/index.tsx +40 -20
  108. package/src/theme/ApiExplorer/FormSelect/index.tsx +41 -15
  109. package/src/theme/ApiExplorer/FormTextInput/index.tsx +15 -1
  110. package/src/theme/ApiExplorer/MethodEndpoint/index.tsx +21 -0
  111. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.tsx +13 -2
  112. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.tsx +12 -1
  113. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.tsx +14 -2
  114. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.tsx +14 -2
  115. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.tsx +16 -3
  116. package/src/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +0 -9
  117. package/src/theme/ApiExplorer/ParamOptions/index.tsx +97 -11
  118. package/src/theme/ApiExplorer/ParamOptions/slice.ts +1 -1
  119. package/src/theme/ApiExplorer/Request/_Request.scss +11 -0
  120. package/src/theme/ApiExplorer/Request/index.tsx +22 -10
  121. package/src/theme/ApiExplorer/Request/makeRequest.ts +19 -3
  122. package/src/theme/ApiExplorer/Response/_Response.scss +11 -0
  123. package/src/theme/ApiExplorer/Response/index.tsx +37 -17
  124. package/src/theme/ApiExplorer/SecuritySchemes/index.tsx +2 -3
  125. package/src/theme/ApiExplorer/Server/index.tsx +10 -3
  126. package/src/theme/ApiExplorer/Server/slice.ts +1 -1
  127. package/src/theme/ApiExplorer/buildPostmanRequest.ts +53 -6
  128. package/src/theme/ApiExplorer/index.tsx +2 -1
  129. package/src/theme/ApiItem/index.tsx +3 -2
  130. package/src/theme/ApiItem/store.ts +2 -0
  131. package/src/theme/ApiTabs/index.tsx +14 -19
  132. package/src/theme/DiscriminatorTabs/index.tsx +14 -19
  133. package/src/theme/MimeTabs/index.tsx +15 -19
  134. package/src/theme/OperationTabs/index.tsx +14 -19
  135. package/src/theme/ParamsDetails/index.tsx +2 -3
  136. package/src/theme/ParamsItem/index.tsx +25 -0
  137. package/src/theme/RequestSchema/index.tsx +144 -87
  138. package/src/theme/ResponseHeaders/index.tsx +1 -2
  139. package/src/theme/ResponseSchema/index.tsx +1 -1
  140. package/src/theme/Schema/index.tsx +112 -27
  141. package/src/theme/SchemaItem/index.tsx +6 -1
  142. package/src/theme/SchemaTabs/index.tsx +42 -21
  143. package/src/theme/StatusCodes/index.tsx +1 -1
  144. package/src/theme/styles.scss +1 -0
  145. package/src/theme/translationIds.ts +3 -0
  146. package/src/theme-classic.d.ts +25 -1
  147. package/src/types.d.ts +15 -52
  148. package/tsconfig.tsbuildinfo +1 -1
@@ -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 ?? ""}
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { createSlice, PayloadAction } from "@reduxjs/toolkit";
9
9
  import { createStorage, hashArray } from "@theme/ApiExplorer/storage-utils";
10
- import {
10
+ import type {
11
11
  SecurityRequirementObject,
12
12
  SecuritySchemeObject,
13
13
  } from "docusaurus-plugin-openapi-docs/src/openapi/types";
@@ -6,8 +6,10 @@
6
6
  * ========================================================================== */
7
7
 
8
8
  import React, { useState } from "react";
9
+
9
10
  import FormFileUpload from "@theme/ApiExplorer/FormFileUpload";
10
11
  import { useTypedDispatch } from "@theme/ApiItem/hooks";
12
+
11
13
  import { FileContent, setFileArrayFormBody } from "../slice";
12
14
 
13
15
  interface FileArrayFormItemProps {
@@ -41,11 +43,6 @@ export default function FileArrayFormBodyItem({
41
43
  return;
42
44
  }
43
45
 
44
- let maxIndex = 0;
45
-
46
- newItems.keys().forEach((item) => {
47
- maxIndex = item > maxIndex ? item : maxIndex;
48
- });
49
46
  newItems.set(index, {
50
47
  src: `/path/to/${file.name}`,
51
48
  content: file,
@@ -5,79 +5,151 @@
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
  import FormFileUpload from "@theme/ApiExplorer/FormFileUpload";
11
+ import FormLabel from "@theme/ApiExplorer/FormLabel";
10
12
  import FormSelect from "@theme/ApiExplorer/FormSelect";
11
13
  import FormTextInput from "@theme/ApiExplorer/FormTextInput";
12
14
  import LiveApp from "@theme/ApiExplorer/LiveEditor";
13
15
  import { useTypedDispatch } from "@theme/ApiItem/hooks";
14
- import { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
15
- import { clearFormBodyKey, setFileFormBody, setStringFormBody } from "../slice";
16
+ import type { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
17
+
16
18
  import FileArrayFormBodyItem from "../FileArrayFormBodyItem";
19
+ import { clearFormBodyKey, setFileFormBody, setStringFormBody } from "../slice";
20
+ import { setFieldEncoding } from "../../EncodingSelection/slice";
17
21
 
18
22
  interface FormBodyItemProps {
19
23
  schemaObject: SchemaObject;
20
24
  id: string;
21
25
  schema: SchemaObject;
26
+ label?: string;
27
+ required?: boolean;
28
+ exampleValue?: SchemaObject["example"];
29
+ fieldEncoding?: string;
22
30
  }
23
31
 
24
32
  export default function FormBodyItem({
25
33
  schemaObject,
26
34
  id,
27
35
  schema,
36
+ label,
37
+ required,
38
+ exampleValue,
39
+ fieldEncoding,
28
40
  }: FormBodyItemProps): React.JSX.Element {
29
41
  const dispatch = useTypedDispatch();
30
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
+
31
89
  if (
32
90
  schemaObject.type === "array" &&
33
91
  schemaObject.items?.format === "binary"
34
92
  ) {
35
93
  return (
36
- <FileArrayFormBodyItem id={id} description={schemaObject.description} />
94
+ <>
95
+ {label && <FormLabel label={label} required={required} />}
96
+ <FileArrayFormBodyItem id={id} description={schemaObject.description} />
97
+ </>
37
98
  );
38
99
  }
39
100
 
40
101
  if (schemaObject.format === "binary") {
41
102
  return (
42
- <FormFileUpload
43
- placeholder={schemaObject.description || id}
44
- onChange={(file: any) => {
45
- if (file === undefined) {
46
- dispatch(clearFormBodyKey(id));
47
- return;
48
- }
49
- dispatch(
50
- setFileFormBody({
51
- key: id,
52
- value: {
53
- src: `/path/to/${file.name}`,
54
- content: file,
55
- },
56
- })
57
- );
58
- }}
59
- />
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
+ </>
60
138
  );
61
139
  }
62
140
 
63
- if (
64
- schemaObject.type === "object" &&
65
- (schemaObject.example || schemaObject.examples)
66
- ) {
67
- const objectExample = JSON.stringify(
68
- schemaObject.example ?? schemaObject.examples[0],
69
- null,
70
- 2
71
- );
72
-
141
+ if (schemaObject.type === "object") {
73
142
  return (
74
- <LiveApp
75
- action={(code: string) =>
76
- dispatch(setStringFormBody({ key: id, value: code }))
77
- }
78
- >
79
- {objectExample}
80
- </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
+ </>
81
153
  );
82
154
  }
83
155
 
@@ -87,9 +159,13 @@ export default function FormBodyItem({
87
159
  ) {
88
160
  return (
89
161
  <FormSelect
162
+ label={label}
163
+ required={required}
164
+ value={value}
90
165
  options={["---", ...schemaObject.enum]}
91
166
  onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
92
167
  const val = e.target.value;
168
+ setValue(val);
93
169
  if (val === "---") {
94
170
  dispatch(clearFormBodyKey(id));
95
171
  } else {
@@ -107,12 +183,16 @@ export default function FormBodyItem({
107
183
  // TODO: support all the other types.
108
184
  return (
109
185
  <FormTextInput
186
+ label={label}
187
+ required={required}
188
+ value={value}
110
189
  paramName={id}
111
190
  isRequired={
112
191
  Array.isArray(schema.required) && schema.required.includes(id)
113
192
  }
114
193
  placeholder={schemaObject.description || id}
115
194
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
195
+ setValue(e.target.value);
116
196
  dispatch(setStringFormBody({ key: id, value: e.target.value }));
117
197
  }}
118
198
  />
@@ -8,7 +8,6 @@
8
8
  import React, { useEffect, useMemo } from "react";
9
9
 
10
10
  import { translate } from "@docusaurus/Translate";
11
-
12
11
  import json2xml from "@theme/ApiExplorer/Body/json2xml";
13
12
  import FormFileUpload from "@theme/ApiExplorer/FormFileUpload";
14
13
  import FormItem from "@theme/ApiExplorer/FormItem";
@@ -18,13 +17,13 @@ import Markdown from "@theme/Markdown";
18
17
  import SchemaTabs from "@theme/SchemaTabs";
19
18
  import TabItem from "@theme/TabItem";
20
19
  import { OPENAPI_BODY, OPENAPI_REQUEST } from "@theme/translationIds";
21
- import { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
22
- import { sampleFromSchema } from "docusaurus-plugin-openapi-docs/src/openapi/createSchemaExample";
20
+ import { sampleFromSchema } from "docusaurus-plugin-openapi-docs/lib/openapi/createSchemaExample";
21
+ import type { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
23
22
  import format from "xml-formatter";
24
23
 
25
- import { clearRawBody, setFileRawBody, setStringRawBody } from "./slice";
26
24
  import FormBodyItem from "./FormBodyItem";
27
25
  import { resolveSchemaWithSelections } from "./resolveSchemaWithSelections";
26
+ import { clearRawBody, setFileRawBody, setStringRawBody } from "./slice";
28
27
 
29
28
  export interface Props {
30
29
  jsonRequestBodyExample: string;
@@ -94,6 +93,8 @@ function Body({
94
93
  const rawSchema = requestBodyMetadata?.content?.[contentType]?.schema;
95
94
  const example = requestBodyMetadata?.content?.[contentType]?.example;
96
95
  const examples = requestBodyMetadata?.content?.[contentType]?.examples;
96
+ const encoding: Record<string, { contentType?: string }> | undefined =
97
+ requestBodyMetadata?.content?.[contentType]?.encoding;
97
98
 
98
99
  // Resolve the schema based on user's anyOf/oneOf tab selections
99
100
  const schema = useMemo(() => {
@@ -317,23 +318,89 @@ function Body({
317
318
  ) {
318
319
  return (
319
320
  <FormItem className="openapi-explorer__form-item-body-container">
320
- {Object.entries(schema.properties ?? {}).map(([key, val]: any) => {
321
- return (
322
- <FormItem
323
- key={key}
324
- label={key}
325
- required={
326
- Array.isArray(schema.required) && schema.required.includes(key)
327
- }
328
- >
329
- <FormBodyItem
330
- schemaObject={val}
331
- id={key}
332
- schema={schema}
333
- ></FormBodyItem>
334
- </FormItem>
335
- );
336
- })}
321
+ <SchemaTabs className="openapi-tabs__schema" lazy>
322
+ {/* @ts-ignore */}
323
+ <TabItem
324
+ label={translate({
325
+ id: OPENAPI_BODY.EXAMPLE_FROM_SCHEMA,
326
+ message: "Example (from schema)",
327
+ })}
328
+ value="Example (from schema)"
329
+ default
330
+ >
331
+ {Object.entries(schema.properties ?? {}).map(([key, val]: any) => {
332
+ return (
333
+ <FormItem key={key}>
334
+ <FormBodyItem
335
+ schemaObject={val}
336
+ id={key}
337
+ schema={schema}
338
+ exampleValue={val.example}
339
+ label={key}
340
+ required={
341
+ Array.isArray(schema.required) &&
342
+ schema.required.includes(key)
343
+ }
344
+ fieldEncoding={encoding?.[key]?.contentType}
345
+ ></FormBodyItem>
346
+ </FormItem>
347
+ );
348
+ })}
349
+ </TabItem>
350
+ {example && (
351
+ // @ts-ignore
352
+ <TabItem label="Example" value="example">
353
+ {Object.entries(schema.properties ?? {}).map(
354
+ ([schemaKey, schemaVal]: any) => {
355
+ return (
356
+ <FormItem key={schemaKey}>
357
+ <FormBodyItem
358
+ schemaObject={schemaVal}
359
+ id={schemaKey}
360
+ schema={schema}
361
+ exampleValue={example[schemaKey]}
362
+ label={schemaKey}
363
+ required={
364
+ Array.isArray(schema.required) &&
365
+ schema.required.includes(schemaKey)
366
+ }
367
+ fieldEncoding={encoding?.[schemaKey]?.contentType}
368
+ ></FormBodyItem>
369
+ </FormItem>
370
+ );
371
+ }
372
+ )}
373
+ </TabItem>
374
+ )}
375
+ {examples &&
376
+ Object.entries(examples).map(([key, value]) => {
377
+ return (
378
+ // @ts-ignore
379
+ <TabItem label={key} value={key} key={key}>
380
+ {Object.entries(schema.properties ?? {}).map(
381
+ ([schemaKey, schemaVal]: any) => {
382
+ return (
383
+ <FormItem key={schemaKey}>
384
+ <FormBodyItem
385
+ schemaObject={schemaVal}
386
+ id={schemaKey}
387
+ schema={schema}
388
+ exampleValue={value.value?.[schemaKey]}
389
+ label={schemaKey}
390
+ required={
391
+ Array.isArray(schema.required) &&
392
+ schema.required.includes(schemaKey)
393
+ }
394
+ fieldEncoding={encoding?.[schemaKey]?.contentType}
395
+ ></FormBodyItem>
396
+ </FormItem>
397
+ );
398
+ }
399
+ )}
400
+ </TabItem>
401
+ );
402
+ })}
403
+ </SchemaTabs>
337
404
  </FormItem>
338
405
  );
339
406
  }
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  * ========================================================================== */
7
7
 
8
- import { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
8
+ import type { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
9
9
  import merge from "lodash/merge";
10
10
 
11
11
  export interface SchemaSelections {
@@ -11,6 +11,7 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
11
11
  import ApiCodeBlock from "@theme/ApiExplorer/ApiCodeBlock";
12
12
  import buildPostmanRequest from "@theme/ApiExplorer/buildPostmanRequest";
13
13
  import CodeTabs from "@theme/ApiExplorer/CodeTabs";
14
+ import { useResolvedEncoding } from "@theme/ApiExplorer/EncodingSelection/useResolvedEncoding";
14
15
  import { useTypedSelector } from "@theme/ApiItem/hooks";
15
16
  import cloneDeep from "lodash/cloneDeep";
16
17
  import codegen from "postman-code-generators";
@@ -30,6 +31,7 @@ export interface Props {
30
31
  postman: sdk.Request;
31
32
  codeSamples: CodeSample[];
32
33
  maskCredentials?: boolean;
34
+ requestBody?: import("docusaurus-plugin-openapi-docs/src/openapi/types").RequestBodyObject;
33
35
  }
34
36
 
35
37
  function CodeTab({ children, hidden, className }: any): React.JSX.Element {
@@ -44,6 +46,7 @@ function CodeSnippets({
44
46
  postman,
45
47
  codeSamples,
46
48
  maskCredentials: propMaskCredentials,
49
+ requestBody,
47
50
  }: Props) {
48
51
  const { siteConfig } = useDocusaurusContext();
49
52
 
@@ -76,7 +79,9 @@ function CodeSnippets({
76
79
  const authOptions =
77
80
  clonedAuth?.options?.[key] ??
78
81
  clonedAuth?.options?.[comboAuthId];
79
- placeholder = authOptions?.find((opt: any) => opt.key === key)?.name;
82
+ placeholder = authOptions?.find(
83
+ (opt: any) => opt.key === key
84
+ )?.name;
80
85
  obj[key] = cleanCredentials(obj[key]);
81
86
  } else {
82
87
  obj[key] = `<${placeholder ?? key}>`;
@@ -93,6 +98,8 @@ function CodeSnippets({
93
98
  })()
94
99
  : auth;
95
100
 
101
+ const encoding = useResolvedEncoding(requestBody);
102
+
96
103
  // Create a Postman request object using cleanedAuth or original auth
97
104
  const cleanedPostmanRequest = buildPostmanRequest(postman, {
98
105
  queryParams,
@@ -104,6 +111,7 @@ function CodeSnippets({
104
111
  body,
105
112
  server,
106
113
  auth: cleanedAuth,
114
+ encoding,
107
115
  });
108
116
 
109
117
  // User-defined languages array
@@ -9,11 +9,12 @@ import React, { cloneElement, ReactElement, useEffect, useRef } from "react";
9
9
 
10
10
  import {
11
11
  sanitizeTabsChildren,
12
+ type TabItemProps,
12
13
  type TabProps,
14
+ TabsProvider,
13
15
  useScrollPositionBlocker,
14
- useTabs,
16
+ useTabsContextValue,
15
17
  } from "@docusaurus/theme-common/internal";
16
- import { TabItemProps } from "@docusaurus/theme-common/lib/utils/tabsUtils";
17
18
  import useIsBrowser from "@docusaurus/useIsBrowser";
18
19
  import clsx from "clsx";
19
20
 
@@ -43,7 +44,7 @@ function TabList({
43
44
  selectedValue,
44
45
  selectValue,
45
46
  tabValues,
46
- }: CodeTabsProps & ReturnType<typeof useTabs>) {
47
+ }: CodeTabsProps & ReturnType<typeof useTabsContextValue>) {
47
48
  const tabRefs = useRef<(HTMLLIElement | null)[]>([]);
48
49
  const tabsScrollContainerRef = useRef<any>(null);
49
50
  const { blockElementScrollPositionUntilNextRender } =
@@ -192,7 +193,8 @@ function TabContent({
192
193
  lazy,
193
194
  children,
194
195
  selectedValue,
195
- }: CodeTabsProps & ReturnType<typeof useTabs>): React.JSX.Element | null {
196
+ }: CodeTabsProps &
197
+ ReturnType<typeof useTabsContextValue>): React.JSX.Element | null {
196
198
  const childTabs = (Array.isArray(children) ? children : [children]).filter(
197
199
  Boolean
198
200
  ) as ReactElement<TabItemProps>[];
@@ -207,28 +209,26 @@ function TabContent({
207
209
  return cloneElement(selectedTabItem, { className: "margin-top--md" });
208
210
  }
209
211
  return (
210
- <div className="margin-top--md openapi-tabs__code-content">
211
- {childTabs.map((tabItem, i) =>
212
- cloneElement(tabItem, {
213
- key: i,
214
- hidden: tabItem.props.value !== selectedValue,
215
- })
216
- )}
217
- </div>
212
+ <div className="margin-top--md openapi-tabs__code-content">{childTabs}</div>
218
213
  );
219
214
  }
220
215
 
221
216
  function TabsComponent(props: CodeTabsProps & Props): React.JSX.Element {
222
- const tabs = useTabs(props);
217
+ const tabs = useTabsContextValue(props);
223
218
  const { className } = props;
224
219
 
225
220
  return (
226
- <div
227
- className={clsx("tabs-container openapi-tabs__code-container", className)}
228
- >
229
- <TabList {...props} {...tabs} />
230
- <TabContent {...props} {...tabs} />
231
- </div>
221
+ <TabsProvider value={tabs}>
222
+ <div
223
+ className={clsx(
224
+ "tabs-container openapi-tabs__code-container",
225
+ className
226
+ )}
227
+ >
228
+ <TabList {...props} {...tabs} />
229
+ <TabContent {...props} {...tabs} />
230
+ </div>
231
+ </TabsProvider>
232
232
  );
233
233
  }
234
234
 
@@ -12,6 +12,7 @@ import FormSelect from "@theme/ApiExplorer/FormSelect";
12
12
  import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
13
13
 
14
14
  import { setContentType } from "./slice";
15
+ import { clearEncodingSelection } from "@theme/ApiExplorer/EncodingSelection/slice";
15
16
 
16
17
  function ContentType() {
17
18
  const value = useTypedSelector((state: any) => state.contentType.value);
@@ -23,13 +24,15 @@ function ContentType() {
23
24
  }
24
25
 
25
26
  return (
26
- <FormItem label="Content-Type">
27
+ <FormItem>
27
28
  <FormSelect
29
+ label="Content-Type"
28
30
  value={value}
29
31
  options={options}
30
- onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
31
- dispatch(setContentType(e.target.value))
32
- }
32
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
33
+ dispatch(setContentType(e.target.value));
34
+ dispatch(clearEncodingSelection());
35
+ }}
33
36
  />
34
37
  </FormItem>
35
38
  );