docusaurus-theme-openapi-docs 4.7.1 → 5.0.1

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 (129) 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/ApiCodeBlock/CopyButton/index.js +16 -5
  5. package/lib/theme/ApiExplorer/Authorization/index.js +12 -18
  6. package/lib/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.js +0 -4
  7. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.d.ts +5 -1
  8. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.js +190 -37
  9. package/lib/theme/ApiExplorer/Body/index.js +84 -13
  10. package/lib/theme/ApiExplorer/Body/slice.d.ts +136 -544
  11. package/lib/theme/ApiExplorer/CodeSnippets/index.d.ts +2 -1
  12. package/lib/theme/ApiExplorer/CodeSnippets/index.js +4 -0
  13. package/lib/theme/ApiExplorer/CodeTabs/index.js +15 -16
  14. package/lib/theme/ApiExplorer/ContentType/index.js +7 -2
  15. package/lib/theme/ApiExplorer/EncodingSelection/slice.d.ts +17 -0
  16. package/lib/theme/ApiExplorer/EncodingSelection/slice.js +29 -0
  17. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.d.ts +12 -0
  18. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.js +39 -0
  19. package/lib/theme/ApiExplorer/FormItem/_FormItem.scss +0 -5
  20. package/lib/theme/ApiExplorer/FormItem/index.d.ts +1 -4
  21. package/lib/theme/ApiExplorer/FormItem/index.js +2 -26
  22. package/lib/theme/ApiExplorer/FormLabel/_FormLabel.scss +4 -0
  23. package/lib/theme/ApiExplorer/FormLabel/index.d.ts +9 -0
  24. package/lib/theme/ApiExplorer/FormLabel/index.js +50 -0
  25. package/lib/theme/ApiExplorer/FormMultiSelect/index.d.ts +4 -1
  26. package/lib/theme/ApiExplorer/FormMultiSelect/index.js +97 -19
  27. package/lib/theme/ApiExplorer/FormSelect/index.d.ts +6 -1
  28. package/lib/theme/ApiExplorer/FormSelect/index.js +96 -15
  29. package/lib/theme/ApiExplorer/FormTextInput/index.d.ts +4 -1
  30. package/lib/theme/ApiExplorer/FormTextInput/index.js +71 -1
  31. package/lib/theme/ApiExplorer/MethodEndpoint/index.js +28 -0
  32. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.d.ts +4 -1
  33. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.js +11 -3
  34. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.d.ts +4 -1
  35. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.js +4 -1
  36. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.d.ts +4 -1
  37. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.js +6 -2
  38. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.d.ts +4 -1
  39. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.js +6 -2
  40. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.d.ts +4 -1
  41. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.js +8 -3
  42. package/lib/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +0 -9
  43. package/lib/theme/ApiExplorer/ParamOptions/index.d.ts +10 -0
  44. package/lib/theme/ApiExplorer/ParamOptions/index.js +55 -5
  45. package/lib/theme/ApiExplorer/Request/_Request.scss +11 -0
  46. package/lib/theme/ApiExplorer/Request/index.js +19 -5
  47. package/lib/theme/ApiExplorer/Request/makeRequest.d.ts +3 -1
  48. package/lib/theme/ApiExplorer/Request/makeRequest.js +19 -3
  49. package/lib/theme/ApiExplorer/Response/_Response.scss +11 -0
  50. package/lib/theme/ApiExplorer/Response/index.js +98 -12
  51. package/lib/theme/ApiExplorer/Server/index.d.ts +4 -1
  52. package/lib/theme/ApiExplorer/Server/index.js +6 -3
  53. package/lib/theme/ApiExplorer/buildPostmanRequest.d.ts +4 -1
  54. package/lib/theme/ApiExplorer/buildPostmanRequest.js +46 -5
  55. package/lib/theme/ApiExplorer/index.js +1 -0
  56. package/lib/theme/ApiExplorer/persistenceMiddleware.d.ts +2 -0
  57. package/lib/theme/ApiItem/hooks.d.ts +1 -0
  58. package/lib/theme/ApiItem/index.js +2 -1
  59. package/lib/theme/ApiItem/store.d.ts +6 -0
  60. package/lib/theme/ApiItem/store.js +11 -7
  61. package/lib/theme/ApiTabs/index.js +10 -11
  62. package/lib/theme/DiscriminatorTabs/index.js +10 -11
  63. package/lib/theme/MimeTabs/index.js +10 -11
  64. package/lib/theme/OperationTabs/index.js +10 -11
  65. package/lib/theme/ParamsItem/index.js +27 -0
  66. package/lib/theme/RequestSchema/index.js +172 -109
  67. package/lib/theme/ResponseHeaders/index.js +0 -1
  68. package/lib/theme/Schema/index.d.ts +1 -1
  69. package/lib/theme/Schema/index.js +91 -23
  70. package/lib/theme/SchemaItem/index.js +6 -1
  71. package/lib/theme/SchemaTabs/index.d.ts +1 -1
  72. package/lib/theme/SchemaTabs/index.js +31 -12
  73. package/lib/theme/styles.scss +1 -0
  74. package/lib/theme/translationIds.d.ts +3 -0
  75. package/lib/theme/translationIds.js +3 -0
  76. package/package.json +9 -8
  77. package/src/index.ts +2 -0
  78. package/src/markdown/schema.ts +69 -13
  79. package/src/theme/ApiExplorer/Accept/index.tsx +2 -1
  80. package/src/theme/ApiExplorer/ApiCodeBlock/CopyButton/index.tsx +15 -3
  81. package/src/theme/ApiExplorer/Authorization/index.tsx +27 -33
  82. package/src/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.tsx +0 -5
  83. package/src/theme/ApiExplorer/Body/FormBodyItem/index.tsx +115 -37
  84. package/src/theme/ApiExplorer/Body/index.tsx +85 -17
  85. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +9 -1
  86. package/src/theme/ApiExplorer/CodeTabs/index.tsx +19 -19
  87. package/src/theme/ApiExplorer/ContentType/index.tsx +7 -4
  88. package/src/theme/ApiExplorer/EncodingSelection/slice.ts +31 -0
  89. package/src/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.ts +43 -0
  90. package/src/theme/ApiExplorer/FormItem/_FormItem.scss +0 -5
  91. package/src/theme/ApiExplorer/FormItem/index.tsx +2 -16
  92. package/src/theme/ApiExplorer/FormLabel/_FormLabel.scss +4 -0
  93. package/src/theme/ApiExplorer/FormLabel/index.tsx +43 -0
  94. package/src/theme/ApiExplorer/FormMultiSelect/index.tsx +40 -20
  95. package/src/theme/ApiExplorer/FormSelect/index.tsx +41 -15
  96. package/src/theme/ApiExplorer/FormTextInput/index.tsx +15 -1
  97. package/src/theme/ApiExplorer/MethodEndpoint/index.tsx +21 -0
  98. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.tsx +13 -2
  99. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.tsx +12 -1
  100. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.tsx +14 -2
  101. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.tsx +14 -2
  102. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.tsx +16 -3
  103. package/src/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +0 -9
  104. package/src/theme/ApiExplorer/ParamOptions/index.tsx +97 -11
  105. package/src/theme/ApiExplorer/Request/_Request.scss +11 -0
  106. package/src/theme/ApiExplorer/Request/index.tsx +20 -8
  107. package/src/theme/ApiExplorer/Request/makeRequest.ts +19 -3
  108. package/src/theme/ApiExplorer/Response/_Response.scss +11 -0
  109. package/src/theme/ApiExplorer/Response/index.tsx +35 -14
  110. package/src/theme/ApiExplorer/Server/index.tsx +10 -3
  111. package/src/theme/ApiExplorer/buildPostmanRequest.ts +52 -5
  112. package/src/theme/ApiExplorer/index.tsx +1 -0
  113. package/src/theme/ApiItem/index.tsx +2 -1
  114. package/src/theme/ApiItem/store.ts +2 -0
  115. package/src/theme/ApiTabs/index.tsx +14 -19
  116. package/src/theme/DiscriminatorTabs/index.tsx +14 -19
  117. package/src/theme/MimeTabs/index.tsx +15 -19
  118. package/src/theme/OperationTabs/index.tsx +14 -19
  119. package/src/theme/ParamsItem/index.tsx +25 -0
  120. package/src/theme/RequestSchema/index.tsx +141 -83
  121. package/src/theme/ResponseHeaders/index.tsx +1 -2
  122. package/src/theme/Schema/index.tsx +112 -27
  123. package/src/theme/SchemaItem/index.tsx +6 -1
  124. package/src/theme/SchemaTabs/index.tsx +42 -21
  125. package/src/theme/styles.scss +1 -0
  126. package/src/theme/translationIds.ts +3 -0
  127. package/src/theme-classic.d.ts +25 -1
  128. package/src/types.d.ts +7 -0
  129. package/tsconfig.tsbuildinfo +1 -1
@@ -6,7 +6,7 @@
6
6
  * ========================================================================== */
7
7
 
8
8
  // @ts-nocheck
9
- import React, { useState } from "react";
9
+ import React, { useState, useId } from "react";
10
10
 
11
11
  import { useDoc } from "@docusaurus/plugin-content-docs/client";
12
12
  import { translate } from "@docusaurus/Translate";
@@ -25,6 +25,7 @@ import {
25
25
  clearHeaders,
26
26
  } from "@theme/ApiExplorer/Response/slice";
27
27
  import Server from "@theme/ApiExplorer/Server";
28
+ import { useResolvedEncoding } from "@theme/ApiExplorer/EncodingSelection/useResolvedEncoding";
28
29
  import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
29
30
  import { OPENAPI_REQUEST } from "@theme/translationIds";
30
31
  import type { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
@@ -43,6 +44,7 @@ function Request({ item }: { item: ApiItem }) {
43
44
  const { siteConfig } = useDocusaurusContext();
44
45
  const themeConfig = siteConfig.themeConfig as ThemeConfig;
45
46
  const requestTimeout = themeConfig.api?.requestTimeout;
47
+ const requestCredentials = themeConfig.api?.requestCredentials;
46
48
  // Frontmatter proxy (per-spec) takes precedence over theme config proxy (site-wide)
47
49
  const proxy = frontMatterProxy ?? themeConfig.api?.proxy;
48
50
 
@@ -66,6 +68,8 @@ function Request({ item }: { item: ApiItem }) {
66
68
  const [expandParams, setExpandParams] = useState(true);
67
69
  const [expandServer, setExpandServer] = useState(true);
68
70
 
71
+ const serverLabelId = useId();
72
+
69
73
  const allParams = [
70
74
  ...pathParams,
71
75
  ...queryParams,
@@ -73,6 +77,8 @@ function Request({ item }: { item: ApiItem }) {
73
77
  ...headerParams,
74
78
  ];
75
79
 
80
+ const encoding = useResolvedEncoding(item.requestBody);
81
+
76
82
  const postmanRequest = buildPostmanRequest(postman, {
77
83
  queryParams,
78
84
  pathParams,
@@ -83,6 +89,7 @@ function Request({ item }: { item: ApiItem }) {
83
89
  body,
84
90
  server,
85
91
  auth,
92
+ encoding,
86
93
  });
87
94
 
88
95
  const delay = (ms: number) =>
@@ -99,7 +106,7 @@ function Request({ item }: { item: ApiItem }) {
99
106
  (param: { in: "path" | "query" | "header" | "cookie" }) => {
100
107
  const paramType = param.in;
101
108
  const paramsArray: ParameterObject[] = paramsObject[paramType];
102
- paramsArray.push(param as ParameterObject);
109
+ paramsArray?.push(param as ParameterObject);
103
110
  }
104
111
  );
105
112
 
@@ -171,7 +178,9 @@ function Request({ item }: { item: ApiItem }) {
171
178
  postmanRequest,
172
179
  proxy,
173
180
  body,
174
- requestTimeout
181
+ requestTimeout,
182
+ requestCredentials,
183
+ encoding
175
184
  );
176
185
  if (res.headers.get("content-type")?.includes("text/event-stream")) {
177
186
  await handleEventStream(res);
@@ -249,7 +258,8 @@ function Request({ item }: { item: ApiItem }) {
249
258
  })}
250
259
  </span>
251
260
  {allDetailsExpanded ? (
252
- <span
261
+ <button
262
+ type="button"
253
263
  className="openapi-explorer__expand-details-btn"
254
264
  onClick={collapseAllDetails}
255
265
  >
@@ -257,9 +267,10 @@ function Request({ item }: { item: ApiItem }) {
257
267
  id: OPENAPI_REQUEST.COLLAPSE_ALL,
258
268
  message: "Collapse all",
259
269
  })}
260
- </span>
270
+ </button>
261
271
  ) : (
262
- <span
272
+ <button
273
+ type="button"
263
274
  className="openapi-explorer__expand-details-btn"
264
275
  onClick={expandAllDetails}
265
276
  >
@@ -267,7 +278,7 @@ function Request({ item }: { item: ApiItem }) {
267
278
  id: OPENAPI_REQUEST.EXPAND_ALL,
268
279
  message: "Expand all",
269
280
  })}
270
- </span>
281
+ </button>
271
282
  )}
272
283
  </div>
273
284
  <div className="openapi-explorer__details-outer-container">
@@ -277,6 +288,7 @@ function Request({ item }: { item: ApiItem }) {
277
288
  className="openapi-explorer__details-container"
278
289
  >
279
290
  <summary
291
+ id={serverLabelId}
280
292
  className="openapi-explorer__details-summary"
281
293
  onClick={(e) => {
282
294
  e.preventDefault();
@@ -288,7 +300,7 @@ function Request({ item }: { item: ApiItem }) {
288
300
  message: "Base URL",
289
301
  })}
290
302
  </summary>
291
- <Server />
303
+ <Server labelId={serverLabelId} />
292
304
  </details>
293
305
  )}
294
306
  {showAuth && (
@@ -114,7 +114,9 @@ async function makeRequest(
114
114
  request: sdk.Request,
115
115
  proxy: string | undefined,
116
116
  _body: Body,
117
- timeout: number = DEFAULT_REQUEST_TIMEOUT
117
+ timeout: number = DEFAULT_REQUEST_TIMEOUT,
118
+ credentials?: RequestCredentials,
119
+ encoding?: Record<string, { contentType?: string }>
118
120
  ) {
119
121
  const headers = request.toJSON().header;
120
122
 
@@ -226,12 +228,25 @@ async function makeRequest(
226
228
  const members = (request.body as any)?.formdata?.members;
227
229
  if (Array.isArray(members)) {
228
230
  for (const data of members) {
231
+ const partContentType = encoding?.[data.key]?.contentType
232
+ ?.split(",")[0]
233
+ .trim();
229
234
  if (data.key && data.value.content) {
230
- myBody.append(data.key, data.value.content);
235
+ const blob = partContentType
236
+ ? new Blob([data.value.content], { type: partContentType })
237
+ : data.value.content;
238
+ myBody.append(data.key, blob);
231
239
  }
232
240
  // handle generic key-value payload
233
241
  if (data.key && typeof data.value === "string") {
234
- myBody.append(data.key, data.value);
242
+ if (partContentType) {
243
+ myBody.append(
244
+ data.key,
245
+ new Blob([data.value], { type: partContentType })
246
+ );
247
+ } else {
248
+ myBody.append(data.key, data.value);
249
+ }
235
250
  }
236
251
  }
237
252
  }
@@ -252,6 +267,7 @@ async function makeRequest(
252
267
  method: request.method,
253
268
  headers: myHeaders,
254
269
  body: myBody,
270
+ ...(credentials && { credentials }),
255
271
  };
256
272
 
257
273
  let finalUrl = request.url.toString();
@@ -43,6 +43,17 @@
43
43
  }
44
44
 
45
45
  .openapi-explorer__response-clear-btn {
46
+ -webkit-appearance: none;
47
+ -moz-appearance: none;
48
+ appearance: none;
49
+ padding: 0;
50
+ cursor: pointer;
51
+ border: 0px solid transparent;
52
+ background-color: transparent;
53
+ text-transform: inherit;
54
+ font-weight: inherit;
55
+ font-size: inherit;
56
+
46
57
  &:hover {
47
58
  cursor: pointer;
48
59
  }
@@ -9,13 +9,13 @@ import React from "react";
9
9
 
10
10
  import { useDoc } from "@docusaurus/plugin-content-docs/client";
11
11
  import { usePrismTheme } from "@docusaurus/theme-common";
12
- import { translate } from "@docusaurus/Translate";
12
+ import Translate, { translate } from "@docusaurus/Translate";
13
13
  import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
14
14
  import ApiCodeBlock from "@theme/ApiExplorer/ApiCodeBlock";
15
15
  import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
16
16
  import SchemaTabs from "@theme/SchemaTabs";
17
17
  import TabItem from "@theme/TabItem";
18
- import { OPENAPI_RESPONSE } from "@theme/translationIds";
18
+ import { OPENAPI_REQUEST, OPENAPI_RESPONSE } from "@theme/translationIds";
19
19
  import clsx from "clsx";
20
20
  import type { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
21
21
  import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
@@ -84,7 +84,8 @@ function Response({ item }: { item: ApiItem }) {
84
84
  <span className="openapi-explorer__response-title">
85
85
  {translate({ id: OPENAPI_RESPONSE.TITLE, message: "Response" })}
86
86
  </span>
87
- <span
87
+ <button
88
+ type="button"
88
89
  className="openapi-explorer__response-clear-btn"
89
90
  onClick={() => {
90
91
  dispatch(clearResponse());
@@ -93,7 +94,7 @@ function Response({ item }: { item: ApiItem }) {
93
94
  }}
94
95
  >
95
96
  {translate({ id: OPENAPI_RESPONSE.CLEAR, message: "Clear" })}
96
- </span>
97
+ </button>
97
98
  </div>
98
99
  <div
99
100
  style={{
@@ -126,11 +127,22 @@ function Response({ item }: { item: ApiItem }) {
126
127
  >
127
128
  {prettyResponse || (
128
129
  <p className="openapi-explorer__response-placeholder-message">
129
- {translate({
130
- id: OPENAPI_RESPONSE.PLACEHOLDER,
131
- message:
132
- "Click the <code>Send API Request</code> button above and see the response here!",
133
- })}
130
+ <Translate
131
+ id={OPENAPI_RESPONSE.PLACEHOLDER}
132
+ values={{
133
+ code: (
134
+ <code>
135
+ <Translate id={OPENAPI_REQUEST.SEND_BUTTON}>
136
+ Send API Request
137
+ </Translate>
138
+ </code>
139
+ ),
140
+ }}
141
+ >
142
+ {
143
+ "Click the {code} button above and see the response here!"
144
+ }
145
+ </Translate>
134
146
  </p>
135
147
  )}
136
148
  </ApiCodeBlock>
@@ -163,11 +175,20 @@ function Response({ item }: { item: ApiItem }) {
163
175
  </div>
164
176
  ) : (
165
177
  <p className="openapi-explorer__response-placeholder-message">
166
- {translate({
167
- id: OPENAPI_RESPONSE.PLACEHOLDER,
168
- message:
169
- "Click the <code>Send API Request</code> button above and see the response here!",
170
- })}
178
+ <Translate
179
+ id={OPENAPI_RESPONSE.PLACEHOLDER}
180
+ values={{
181
+ code: (
182
+ <code>
183
+ <Translate id={OPENAPI_REQUEST.SEND_BUTTON}>
184
+ Send API Request
185
+ </Translate>
186
+ </code>
187
+ ),
188
+ }}
189
+ >
190
+ {"Click the {code} button above and see the response here!"}
191
+ </Translate>
171
192
  </p>
172
193
  )}
173
194
  </div>
@@ -17,7 +17,11 @@ import { OPENAPI_SERVER } from "@theme/translationIds";
17
17
 
18
18
  import { setServer, setServerVariable } from "./slice";
19
19
 
20
- function Server() {
20
+ interface ServerProps {
21
+ labelId?: string;
22
+ }
23
+
24
+ function Server({ labelId }: ServerProps) {
21
25
  const [isEditing, setIsEditing] = useState(false);
22
26
  const value = useTypedSelector((state: any) => state.server.value);
23
27
  const options = useTypedSelector((state: any) => state.server.options);
@@ -79,6 +83,7 @@ function Server() {
79
83
  >
80
84
  <FormItem>
81
85
  <FormSelect
86
+ ariaLabelledBy={labelId}
82
87
  options={options.map((s: any) => s.url)}
83
88
  onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
84
89
  dispatch(
@@ -99,8 +104,9 @@ function Server() {
99
104
  Object.keys(value.variables).map((key) => {
100
105
  if (value.variables?.[key].enum !== undefined) {
101
106
  return (
102
- <FormItem label={key}>
107
+ <FormItem>
103
108
  <FormSelect
109
+ label={key}
104
110
  options={value.variables[key].enum}
105
111
  onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
106
112
  dispatch(
@@ -115,8 +121,9 @@ function Server() {
115
121
  );
116
122
  }
117
123
  return (
118
- <FormItem label={key}>
124
+ <FormItem>
119
125
  <FormTextInput
126
+ label={key}
120
127
  placeholder={value.variables?.[key].default}
121
128
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
122
129
  dispatch(
@@ -293,12 +293,36 @@ function tryDecodeJsonParam(value: string): any {
293
293
  }
294
294
 
295
295
  // TODO: this is all a bit hacky
296
- function setBody(clonedPostman: sdk.Request, body: Body) {
296
+ function setBody(
297
+ clonedPostman: sdk.Request,
298
+ body: Body,
299
+ encoding?: Record<string, { contentType?: string }>
300
+ ) {
297
301
  if (clonedPostman.body === undefined) {
298
302
  return;
299
303
  }
300
304
 
301
305
  if (body.type === "empty") {
306
+ // When the original request has formdata and encoding is declared, keep the
307
+ // placeholder params so the code snippet reflects the selected encoding even
308
+ // before the user has uploaded a file.
309
+ if (
310
+ clonedPostman.body?.mode === "formdata" &&
311
+ encoding &&
312
+ Object.keys(encoding).length > 0
313
+ ) {
314
+ const members: any[] =
315
+ (clonedPostman.body.formdata as any)?.members ?? [];
316
+ members.forEach((param: any) => {
317
+ const partContentType = encoding[param.key]?.contentType
318
+ ?.split(",")[0]
319
+ .trim();
320
+ if (partContentType) {
321
+ param.contentType = partContentType;
322
+ }
323
+ });
324
+ return;
325
+ }
302
326
  clonedPostman.body = undefined;
303
327
  return;
304
328
  }
@@ -336,14 +360,35 @@ function setBody(clonedPostman: sdk.Request, body: Body) {
336
360
  Object.entries(body.content)
337
361
  .filter((entry): entry is [string, NonNullable<Content>] => !!entry[1])
338
362
  .forEach(([key, content]) => {
363
+ const partContentType = encoding?.[key]?.contentType
364
+ ?.split(",")[0]
365
+ .trim();
339
366
  if (content.type === "file") {
340
- params.push(new sdk.FormParam({ key: key, ...content }));
367
+ params.push(
368
+ new sdk.FormParam({
369
+ key: key,
370
+ ...content,
371
+ ...(partContentType && { contentType: partContentType }),
372
+ })
373
+ );
341
374
  } else if (content.type === "file[]") {
342
375
  content.value.forEach((file) =>
343
- params.push(new sdk.FormParam({ key, value: file }))
376
+ params.push(
377
+ new sdk.FormParam({
378
+ key,
379
+ value: file,
380
+ ...(partContentType && { contentType: partContentType }),
381
+ })
382
+ )
344
383
  );
345
384
  } else {
346
- params.push(new sdk.FormParam({ key: key, value: content.value }));
385
+ params.push(
386
+ new sdk.FormParam({
387
+ key: key,
388
+ value: content.value,
389
+ ...(partContentType && { contentType: partContentType }),
390
+ })
391
+ );
347
392
  }
348
393
  });
349
394
  params.forEach((param) => {
@@ -391,6 +436,7 @@ interface Options {
391
436
  accept: string;
392
437
  body: Body;
393
438
  auth: AuthState;
439
+ encoding?: Record<string, { contentType?: string }>;
394
440
  }
395
441
 
396
442
  function buildPostmanRequest(
@@ -405,6 +451,7 @@ function buildPostmanRequest(
405
451
  body,
406
452
  server,
407
453
  auth,
454
+ encoding,
408
455
  }: Options
409
456
  ) {
410
457
  const clonedPostman = cloneDeep(postman);
@@ -532,7 +579,7 @@ function buildPostmanRequest(
532
579
  otherHeaders
533
580
  );
534
581
 
535
- setBody(clonedPostman, body);
582
+ setBody(clonedPostman, body, encoding);
536
583
 
537
584
  return clonedPostman;
538
585
  }
@@ -41,6 +41,7 @@ function ApiExplorer({
41
41
  postman={postman}
42
42
  codeSamples={(item as any)["x-codeSamples"] ?? []}
43
43
  maskCredentials={mask_credentials}
44
+ requestBody={item.requestBody}
44
45
  />
45
46
  )}
46
47
  <Request item={item} />
@@ -122,7 +122,7 @@ export default function ApiItem(props: Props): JSX.Element {
122
122
  (param: { in: "path" | "query" | "header" | "cookie" }) => {
123
123
  const paramType = param.in;
124
124
  const paramsArray: ParameterObject[] = params[paramType];
125
- paramsArray.push(param as ParameterObject);
125
+ paramsArray?.push(param as ParameterObject);
126
126
  }
127
127
  );
128
128
  const auth = createAuth({
@@ -159,6 +159,7 @@ export default function ApiItem(props: Props): JSX.Element {
159
159
  params,
160
160
  auth,
161
161
  schemaSelection: { selections: {} },
162
+ encodingSelection: {},
162
163
  },
163
164
  [persistenceMiddleware]
164
165
  );
@@ -10,6 +10,7 @@ import accept from "@theme/ApiExplorer/Accept/slice";
10
10
  import auth from "@theme/ApiExplorer/Authorization/slice";
11
11
  import body from "@theme/ApiExplorer/Body/slice";
12
12
  import contentType from "@theme/ApiExplorer/ContentType/slice";
13
+ import encodingSelection from "@theme/ApiExplorer/EncodingSelection/slice";
13
14
  import params from "@theme/ApiExplorer/ParamOptions/slice";
14
15
  import response from "@theme/ApiExplorer/Response/slice";
15
16
  import schemaSelection from "@theme/ApiExplorer/SchemaSelection/slice";
@@ -18,6 +19,7 @@ import server from "@theme/ApiExplorer/Server/slice";
18
19
  const rootReducer = combineReducers({
19
20
  accept,
20
21
  contentType,
22
+ encodingSelection,
21
23
  response,
22
24
  server,
23
25
  body,
@@ -15,11 +15,12 @@ import React, {
15
15
 
16
16
  import {
17
17
  sanitizeTabsChildren,
18
+ type TabItemProps,
18
19
  TabProps,
20
+ TabsProvider,
19
21
  useScrollPositionBlocker,
20
- useTabs,
22
+ useTabsContextValue,
21
23
  } from "@docusaurus/theme-common/internal";
22
- import { TabItemProps } from "@docusaurus/theme-common/lib/utils/tabsUtils";
23
24
  import { translate } from "@docusaurus/Translate";
24
25
  import useIsBrowser from "@docusaurus/useIsBrowser";
25
26
  import Heading from "@theme/Heading";
@@ -42,7 +43,7 @@ function TabList({
42
43
  message: "Responses",
43
44
  }),
44
45
  id = "responses",
45
- }: TabListProps & ReturnType<typeof useTabs>) {
46
+ }: TabListProps & ReturnType<typeof useTabsContextValue>) {
46
47
  const tabRefs: (HTMLLIElement | null)[] = [];
47
48
  const { blockElementScrollPositionUntilNextRender } =
48
49
  useScrollPositionBlocker();
@@ -194,7 +195,8 @@ function TabContent({
194
195
  lazy,
195
196
  children,
196
197
  selectedValue,
197
- }: TabProps & ReturnType<typeof useTabs>): React.JSX.Element | null {
198
+ }: TabProps &
199
+ ReturnType<typeof useTabsContextValue>): React.JSX.Element | null {
198
200
  const childTabs = (Array.isArray(children) ? children : [children]).filter(
199
201
  Boolean
200
202
  ) as ReactElement<TabItemProps>[];
@@ -208,24 +210,17 @@ function TabContent({
208
210
  }
209
211
  return cloneElement(selectedTabItem, { className: "margin-top--md" });
210
212
  }
211
- return (
212
- <div className="margin-top--md">
213
- {childTabs.map((tabItem, i) =>
214
- cloneElement(tabItem, {
215
- key: i,
216
- hidden: tabItem.props.value !== selectedValue,
217
- })
218
- )}
219
- </div>
220
- );
213
+ return <div className="margin-top--md">{childTabs}</div>;
221
214
  }
222
215
  function TabsComponent(props: TabListProps): React.JSX.Element {
223
- const tabs = useTabs(props);
216
+ const tabs = useTabsContextValue(props);
224
217
  return (
225
- <div className="openapi-tabs__container">
226
- <TabList {...props} {...tabs} />
227
- <TabContent {...props} {...tabs} />
228
- </div>
218
+ <TabsProvider value={tabs}>
219
+ <div className="openapi-tabs__container">
220
+ <TabList {...props} {...tabs} />
221
+ <TabContent {...props} {...tabs} />
222
+ </div>
223
+ </TabsProvider>
229
224
  );
230
225
  }
231
226
  export default function ApiTabs(props: TabListProps): React.JSX.Element {
@@ -15,11 +15,12 @@ import React, {
15
15
 
16
16
  import {
17
17
  sanitizeTabsChildren,
18
+ type TabItemProps,
18
19
  TabProps,
20
+ TabsProvider,
19
21
  useScrollPositionBlocker,
20
- useTabs,
22
+ useTabsContextValue,
21
23
  } from "@docusaurus/theme-common/internal";
22
- import { TabItemProps } from "@docusaurus/theme-common/lib/utils/tabsUtils";
23
24
  import useIsBrowser from "@docusaurus/useIsBrowser";
24
25
  import clsx from "clsx";
25
26
  import flatten from "lodash/flatten";
@@ -30,7 +31,7 @@ function TabList({
30
31
  selectedValue,
31
32
  selectValue,
32
33
  tabValues,
33
- }: TabProps & ReturnType<typeof useTabs>) {
34
+ }: TabProps & ReturnType<typeof useTabsContextValue>) {
34
35
  const tabRefs: (HTMLLIElement | null)[] = [];
35
36
  const { blockElementScrollPositionUntilNextRender } =
36
37
  useScrollPositionBlocker();
@@ -172,7 +173,8 @@ function TabContent({
172
173
  lazy,
173
174
  children,
174
175
  selectedValue,
175
- }: TabProps & ReturnType<typeof useTabs>): React.JSX.Element | null {
176
+ }: TabProps &
177
+ ReturnType<typeof useTabsContextValue>): React.JSX.Element | null {
176
178
  const childTabs = (Array.isArray(children) ? children : [children]).filter(
177
179
  Boolean
178
180
  ) as ReactElement<TabItemProps>[];
@@ -187,24 +189,17 @@ function TabContent({
187
189
  }
188
190
  return cloneElement(selectedTabItem, { className: "margin-top--md" });
189
191
  }
190
- return (
191
- <div className="margin-top--md">
192
- {childTabs.map((tabItem, i) =>
193
- cloneElement(tabItem, {
194
- key: i,
195
- hidden: tabItem.props.value !== selectedValue,
196
- })
197
- )}
198
- </div>
199
- );
192
+ return <div className="margin-top--md">{childTabs}</div>;
200
193
  }
201
194
  function TabsComponent(props: TabProps): React.JSX.Element {
202
- const tabs = useTabs(props);
195
+ const tabs = useTabsContextValue(props);
203
196
  return (
204
- <div className="openapi-tabs__container">
205
- <TabList {...props} {...tabs} />
206
- <TabContent {...props} {...tabs} />
207
- </div>
197
+ <TabsProvider value={tabs}>
198
+ <div className="openapi-tabs__container">
199
+ <TabList {...props} {...tabs} />
200
+ <TabContent {...props} {...tabs} />
201
+ </div>
202
+ </TabsProvider>
208
203
  );
209
204
  }
210
205
  export default function DiscriminatorTabs(props: TabProps): React.JSX.Element {
@@ -15,11 +15,12 @@ import React, {
15
15
 
16
16
  import {
17
17
  sanitizeTabsChildren,
18
+ type TabItemProps,
18
19
  TabProps,
20
+ TabsProvider,
19
21
  useScrollPositionBlocker,
20
- useTabs,
22
+ useTabsContextValue,
21
23
  } from "@docusaurus/theme-common/internal";
22
- import { TabItemProps } from "@docusaurus/theme-common/lib/utils/tabsUtils";
23
24
  import useIsBrowser from "@docusaurus/useIsBrowser";
24
25
  import { setAccept } from "@theme/ApiExplorer/Accept/slice";
25
26
  import { setContentType } from "@theme/ApiExplorer/ContentType/slice";
@@ -38,7 +39,9 @@ function TabList({
38
39
  selectValue,
39
40
  tabValues,
40
41
  schemaType,
41
- }: Props & TabProps & ReturnType<typeof useTabs>): React.JSX.Element {
42
+ }: Props &
43
+ TabProps &
44
+ ReturnType<typeof useTabsContextValue>): React.JSX.Element {
42
45
  const tabRefs: (HTMLLIElement | null)[] = [];
43
46
  const { blockElementScrollPositionUntilNextRender } =
44
47
  useScrollPositionBlocker();
@@ -208,7 +211,7 @@ function TabContent({
208
211
  lazy,
209
212
  children,
210
213
  selectedValue,
211
- }: Props & TabProps & ReturnType<typeof useTabs>) {
214
+ }: Props & TabProps & ReturnType<typeof useTabsContextValue>) {
212
215
  const childTabs = (Array.isArray(children) ? children : [children]).filter(
213
216
  Boolean
214
217
  ) as ReactElement<TabItemProps>[];
@@ -222,24 +225,17 @@ function TabContent({
222
225
  }
223
226
  return cloneElement(selectedTabItem, { className: "margin-top--md" });
224
227
  }
225
- return (
226
- <div className="margin-top--md">
227
- {childTabs.map((tabItem, i) =>
228
- cloneElement(tabItem, {
229
- key: i,
230
- hidden: tabItem.props.value !== selectedValue,
231
- })
232
- )}
233
- </div>
234
- );
228
+ return <div className="margin-top--md">{childTabs}</div>;
235
229
  }
236
230
  function TabsComponent(props: Props & TabProps): React.JSX.Element {
237
- const tabs = useTabs(props);
231
+ const tabs = useTabsContextValue(props);
238
232
  return (
239
- <div className="tabs-container">
240
- <TabList {...props} {...tabs} />
241
- <TabContent {...props} {...tabs} />
242
- </div>
233
+ <TabsProvider value={tabs}>
234
+ <div className="tabs-container">
235
+ <TabList {...props} {...tabs} />
236
+ <TabContent {...props} {...tabs} />
237
+ </div>
238
+ </TabsProvider>
243
239
  );
244
240
  }
245
241
  export default function MimeTabs(props: Props & TabProps) {