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
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  * ========================================================================== */
7
7
 
8
- import React, { useState } from "react";
8
+ import React, { useState, useId } from "react";
9
9
 
10
10
  import { translate } from "@docusaurus/Translate";
11
11
  import FormItem from "@theme/ApiExplorer/FormItem";
@@ -19,36 +19,117 @@ import { OPENAPI_PARAM_OPTIONS } from "@theme/translationIds";
19
19
 
20
20
  import { Param } from "./slice";
21
21
 
22
+ export interface LabelProps {
23
+ label?: string;
24
+ type?: string;
25
+ required?: boolean;
26
+ }
27
+
22
28
  export interface ParamProps {
23
29
  param: Param;
24
30
  }
25
31
 
26
- function ParamOption({ param }: ParamProps) {
27
- if (param.schema?.type === "array" && param.schema.items?.enum) {
28
- return <ParamMultiSelectFormItem param={param} />;
32
+ /**
33
+ * Extracts enum values from a schema, including when wrapped in allOf.
34
+ * This handles cases where an enum is referenced via allOf for composition.
35
+ */
36
+ export function getSchemaEnum(schema: any): any[] | undefined {
37
+ // Direct enum on schema
38
+ if (schema?.enum) {
39
+ return schema.enum;
40
+ }
41
+
42
+ // Enum inside allOf - check each item
43
+ if (schema?.allOf && Array.isArray(schema.allOf)) {
44
+ for (const item of schema.allOf) {
45
+ if (item.enum) {
46
+ return item.enum;
47
+ }
48
+ }
49
+ }
50
+
51
+ // const is semantically a single-value enum
52
+ if (schema?.const !== undefined) {
53
+ return [schema.const];
54
+ }
55
+
56
+ return undefined;
57
+ }
58
+
59
+ function ParamOption({
60
+ param,
61
+ label,
62
+ type,
63
+ required,
64
+ }: ParamProps & LabelProps) {
65
+ const schemaEnum = getSchemaEnum(param.schema);
66
+ const itemsEnum = getSchemaEnum(param.schema?.items);
67
+
68
+ if (param.schema?.type === "array" && itemsEnum) {
69
+ return (
70
+ <ParamMultiSelectFormItem
71
+ param={param}
72
+ label={label}
73
+ type={type}
74
+ required={required}
75
+ />
76
+ );
29
77
  }
30
78
 
31
79
  if (param.schema?.type === "array") {
32
- return <ParamArrayFormItem param={param} />;
80
+ return (
81
+ <ParamArrayFormItem
82
+ param={param}
83
+ label={label}
84
+ type={type}
85
+ required={required}
86
+ />
87
+ );
33
88
  }
34
89
 
35
- if (param.schema?.enum) {
36
- return <ParamSelectFormItem param={param} />;
90
+ if (schemaEnum) {
91
+ return (
92
+ <ParamSelectFormItem
93
+ param={param}
94
+ label={label}
95
+ type={type}
96
+ required={required}
97
+ />
98
+ );
37
99
  }
38
100
 
39
101
  if (param.schema?.type === "boolean") {
40
- return <ParamBooleanFormItem param={param} />;
102
+ return (
103
+ <ParamBooleanFormItem
104
+ param={param}
105
+ label={label}
106
+ type={type}
107
+ required={required}
108
+ />
109
+ );
41
110
  }
42
111
 
43
112
  // integer, number, string, int32, int64, float, double, object, byte, binary,
44
113
  // date-time, date, password
45
- return <ParamTextFormItem param={param} />;
114
+ return (
115
+ <ParamTextFormItem
116
+ param={param}
117
+ label={label}
118
+ type={type}
119
+ required={required}
120
+ />
121
+ );
46
122
  }
47
123
 
48
124
  function ParamOptionWrapper({ param }: ParamProps) {
49
125
  return (
50
- <FormItem label={param.name} type={param.in} required={param.required}>
51
- <ParamOption param={param} />
126
+ <FormItem>
127
+ <ParamOption
128
+ param={param}
129
+ label={param.name}
130
+ type={param.in}
131
+ required={param.required}
132
+ />
52
133
  </FormItem>
53
134
  );
54
135
  }
@@ -56,6 +137,8 @@ function ParamOptionWrapper({ param }: ParamProps) {
56
137
  function ParamOptions() {
57
138
  const [showOptional, setShowOptional] = useState(false);
58
139
 
140
+ const optionalId = useId();
141
+
59
142
  const pathParams = useTypedSelector((state: any) => state.params.path);
60
143
  const queryParams = useTypedSelector((state: any) => state.params.query);
61
144
  const cookieParams = useTypedSelector((state: any) => state.params.cookie);
@@ -84,6 +167,8 @@ function ParamOptions() {
84
167
  <button
85
168
  type="button"
86
169
  className="openapi-explorer__show-more-btn"
170
+ aria-expanded={showOptional}
171
+ aria-controls={optionalId}
87
172
  onClick={() => setShowOptional((prev) => !prev)}
88
173
  >
89
174
  <span
@@ -137,6 +222,7 @@ function ParamOptions() {
137
222
  ? "openapi-explorer__show-options"
138
223
  : "openapi-explorer__hide-options"
139
224
  }
225
+ id={optionalId}
140
226
  >
141
227
  {optionalParams.map((param) => (
142
228
  <ParamOptionWrapper
@@ -6,7 +6,7 @@
6
6
  * ========================================================================== */
7
7
 
8
8
  import { createSlice, PayloadAction } from "@reduxjs/toolkit";
9
- import { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
9
+ import type { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
10
10
 
11
11
  export type Param = ParameterObject & { value?: string[] | string };
12
12
 
@@ -48,6 +48,17 @@
48
48
  }
49
49
 
50
50
  .openapi-explorer__expand-details-btn {
51
+ -webkit-appearance: none;
52
+ -moz-appearance: none;
53
+ appearance: none;
54
+ padding: 0;
55
+ cursor: pointer;
56
+ border: 0px solid transparent;
57
+ background-color: transparent;
58
+ text-transform: inherit;
59
+ font-weight: inherit;
60
+ font-size: inherit;
61
+
51
62
  &:hover {
52
63
  cursor: pointer;
53
64
  }
@@ -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,10 +25,11 @@ 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
- import { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
31
- import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
31
+ import type { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
32
+ import type { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
32
33
  import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
33
34
  import * as sdk from "postman-collection";
34
35
  import { FormProvider, useForm } from "react-hook-form";
@@ -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,15 +9,15 @@ 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
- import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
20
+ import type { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
21
21
  import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
22
22
 
23
23
  import { clearResponse, clearCode, clearHeaders } from "./slice";
@@ -47,8 +47,7 @@ function Response({ item }: { item: ApiItem }) {
47
47
  const { siteConfig } = useDocusaurusContext();
48
48
  const themeConfig = siteConfig.themeConfig as ThemeConfig;
49
49
  const hideSendButton = metadata.frontMatter.hide_send_button;
50
- const proxy =
51
- metadata.frontMatter.proxy ?? themeConfig.api?.proxy;
50
+ const proxy = metadata.frontMatter.proxy ?? themeConfig.api?.proxy;
52
51
  const prismTheme = usePrismTheme();
53
52
  const code = useTypedSelector((state: any) => state.response.code);
54
53
  const headers = useTypedSelector((state: any) => state.response.headers);
@@ -85,7 +84,8 @@ function Response({ item }: { item: ApiItem }) {
85
84
  <span className="openapi-explorer__response-title">
86
85
  {translate({ id: OPENAPI_RESPONSE.TITLE, message: "Response" })}
87
86
  </span>
88
- <span
87
+ <button
88
+ type="button"
89
89
  className="openapi-explorer__response-clear-btn"
90
90
  onClick={() => {
91
91
  dispatch(clearResponse());
@@ -94,7 +94,7 @@ function Response({ item }: { item: ApiItem }) {
94
94
  }}
95
95
  >
96
96
  {translate({ id: OPENAPI_RESPONSE.CLEAR, message: "Clear" })}
97
- </span>
97
+ </button>
98
98
  </div>
99
99
  <div
100
100
  style={{
@@ -127,11 +127,22 @@ function Response({ item }: { item: ApiItem }) {
127
127
  >
128
128
  {prettyResponse || (
129
129
  <p className="openapi-explorer__response-placeholder-message">
130
- {translate({
131
- id: OPENAPI_RESPONSE.PLACEHOLDER,
132
- message:
133
- "Click the <code>Send API Request</code> button above and see the response here!",
134
- })}
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>
135
146
  </p>
136
147
  )}
137
148
  </ApiCodeBlock>
@@ -164,11 +175,20 @@ function Response({ item }: { item: ApiItem }) {
164
175
  </div>
165
176
  ) : (
166
177
  <p className="openapi-explorer__response-placeholder-message">
167
- {translate({
168
- id: OPENAPI_RESPONSE.PLACEHOLDER,
169
- message:
170
- "Click the <code>Send API Request</code> button above and see the response here!",
171
- })}
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>
172
192
  </p>
173
193
  )}
174
194
  </div>
@@ -7,11 +7,10 @@
7
7
 
8
8
  import React from "react";
9
9
 
10
- import { translate } from "@docusaurus/Translate";
11
- import { OPENAPI_SECURITY_SCHEMES } from "@theme/translationIds";
12
-
13
10
  import Link from "@docusaurus/Link";
11
+ import { translate } from "@docusaurus/Translate";
14
12
  import { useTypedSelector } from "@theme/ApiItem/hooks";
13
+ import { OPENAPI_SECURITY_SCHEMES } from "@theme/translationIds";
15
14
 
16
15
  function SecuritySchemes(props: any) {
17
16
  const options = useTypedSelector((state: any) => state.auth.options);
@@ -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(
@@ -6,7 +6,7 @@
6
6
  * ========================================================================== */
7
7
 
8
8
  import { createSlice, PayloadAction } from "@reduxjs/toolkit";
9
- import { ServerObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
9
+ import type { ServerObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
10
10
  // TODO: we might want to export this
11
11
 
12
12
  export interface State {
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { AuthState, Scheme } from "@theme/ApiExplorer/Authorization/slice";
9
9
  import { Body, Content } from "@theme/ApiExplorer/Body/slice";
10
- import {
10
+ import type {
11
11
  ParameterObject,
12
12
  ServerObject,
13
13
  } from "docusaurus-plugin-openapi-docs/src/openapi/types";
@@ -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
  }
@@ -12,7 +12,7 @@ import CodeSnippets from "@theme/ApiExplorer/CodeSnippets";
12
12
  import Request from "@theme/ApiExplorer/Request";
13
13
  import Response from "@theme/ApiExplorer/Response";
14
14
  import SecuritySchemes from "@theme/ApiExplorer/SecuritySchemes";
15
- import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
15
+ import type { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
16
16
  import * as sdk from "postman-collection";
17
17
 
18
18
  function ApiExplorer({
@@ -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} />