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
@@ -5,9 +5,10 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  * ========================================================================== */
7
7
 
8
- import React from "react";
8
+ import React, { useEffect, useState } from "react";
9
9
 
10
10
  import FormFileUpload from "@theme/ApiExplorer/FormFileUpload";
11
+ import FormLabel from "@theme/ApiExplorer/FormLabel";
11
12
  import FormSelect from "@theme/ApiExplorer/FormSelect";
12
13
  import FormTextInput from "@theme/ApiExplorer/FormTextInput";
13
14
  import LiveApp from "@theme/ApiExplorer/LiveEditor";
@@ -16,70 +17,139 @@ import type { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/ty
16
17
 
17
18
  import FileArrayFormBodyItem from "../FileArrayFormBodyItem";
18
19
  import { clearFormBodyKey, setFileFormBody, setStringFormBody } from "../slice";
20
+ import { setFieldEncoding } from "../../EncodingSelection/slice";
19
21
 
20
22
  interface FormBodyItemProps {
21
23
  schemaObject: SchemaObject;
22
24
  id: string;
23
25
  schema: SchemaObject;
26
+ label?: string;
27
+ required?: boolean;
28
+ exampleValue?: SchemaObject["example"];
29
+ fieldEncoding?: string;
24
30
  }
25
31
 
26
32
  export default function FormBodyItem({
27
33
  schemaObject,
28
34
  id,
29
35
  schema,
36
+ label,
37
+ required,
38
+ exampleValue,
39
+ fieldEncoding,
30
40
  }: FormBodyItemProps): React.JSX.Element {
31
41
  const dispatch = useTypedDispatch();
32
42
 
43
+ // Parse comma-separated encoding contentType into selectable options
44
+ const encodingOptions = fieldEncoding
45
+ ? fieldEncoding
46
+ .split(",")
47
+ .map((t) => t.trim())
48
+ .filter(Boolean)
49
+ : [];
50
+ const hasMultipleEncodings = encodingOptions.length > 1;
51
+
52
+ // Initialize with the first declared content type
53
+ const [selectedEncoding, setSelectedEncoding] = useState<string>(
54
+ encodingOptions[0] ?? ""
55
+ );
56
+
57
+ // Seed Redux with the first declared encoding on mount so the code snippet
58
+ // reflects a content type immediately, even before the user interacts.
59
+ // The empty dep array is intentional: `fieldEncoding` comes from a static
60
+ // spec value that never changes for the lifetime of this component instance,
61
+ // and re-seeding on every render would fight user selections.
62
+ useEffect(() => {
63
+ if (encodingOptions[0]) {
64
+ dispatch(
65
+ setFieldEncoding({ field: id, contentType: encodingOptions[0] })
66
+ );
67
+ }
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ }, []);
70
+ const [value, setValue] = useState(() => {
71
+ let initialValue = exampleValue ?? "";
72
+
73
+ if (schemaObject.type === "object" && exampleValue) {
74
+ initialValue = JSON.stringify(exampleValue, null, 2);
75
+ }
76
+
77
+ return initialValue;
78
+ });
79
+
80
+ useEffect(() => {
81
+ if (value) {
82
+ dispatch(setStringFormBody({ key: id, value }));
83
+ } else {
84
+ dispatch(clearFormBodyKey(id));
85
+ }
86
+ // eslint-disable-next-line react-hooks/exhaustive-deps
87
+ }, []);
88
+
33
89
  if (
34
90
  schemaObject.type === "array" &&
35
91
  schemaObject.items?.format === "binary"
36
92
  ) {
37
93
  return (
38
- <FileArrayFormBodyItem id={id} description={schemaObject.description} />
94
+ <>
95
+ {label && <FormLabel label={label} required={required} />}
96
+ <FileArrayFormBodyItem id={id} description={schemaObject.description} />
97
+ </>
39
98
  );
40
99
  }
41
100
 
42
101
  if (schemaObject.format === "binary") {
43
102
  return (
44
- <FormFileUpload
45
- placeholder={schemaObject.description || id}
46
- onChange={(file: any) => {
47
- if (file === undefined) {
48
- dispatch(clearFormBodyKey(id));
49
- return;
50
- }
51
- dispatch(
52
- setFileFormBody({
53
- key: id,
54
- value: {
55
- src: `/path/to/${file.name}`,
56
- content: file,
57
- },
58
- })
59
- );
60
- }}
61
- />
103
+ <>
104
+ {label && <FormLabel label={label} required={required} />}
105
+ {hasMultipleEncodings && (
106
+ <div style={{ marginTop: "0.5rem" }}>
107
+ <FormSelect
108
+ label="Content-Type"
109
+ options={encodingOptions}
110
+ value={selectedEncoding}
111
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
112
+ const ct = e.target.value;
113
+ setSelectedEncoding(ct);
114
+ dispatch(setFieldEncoding({ field: id, contentType: ct }));
115
+ }}
116
+ />
117
+ </div>
118
+ )}
119
+ <FormFileUpload
120
+ placeholder={schemaObject.description || id}
121
+ onChange={(file: any) => {
122
+ if (file === undefined) {
123
+ dispatch(clearFormBodyKey(id));
124
+ return;
125
+ }
126
+ dispatch(
127
+ setFileFormBody({
128
+ key: id,
129
+ value: {
130
+ src: `/path/to/${file.name}`,
131
+ content: file,
132
+ },
133
+ })
134
+ );
135
+ }}
136
+ />
137
+ </>
62
138
  );
63
139
  }
64
140
 
65
- if (
66
- schemaObject.type === "object" &&
67
- (schemaObject.example || schemaObject.examples)
68
- ) {
69
- const objectExample = JSON.stringify(
70
- schemaObject.example ?? schemaObject.examples[0],
71
- null,
72
- 2
73
- );
74
-
141
+ if (schemaObject.type === "object") {
75
142
  return (
76
- <LiveApp
77
- action={(code: string) =>
78
- dispatch(setStringFormBody({ key: id, value: code }))
79
- }
80
- >
81
- {objectExample}
82
- </LiveApp>
143
+ <>
144
+ {label && <FormLabel label={label} required={required} />}
145
+ <LiveApp
146
+ action={(code: string) =>
147
+ dispatch(setStringFormBody({ key: id, value: code }))
148
+ }
149
+ >
150
+ {value}
151
+ </LiveApp>
152
+ </>
83
153
  );
84
154
  }
85
155
 
@@ -89,9 +159,13 @@ export default function FormBodyItem({
89
159
  ) {
90
160
  return (
91
161
  <FormSelect
162
+ label={label}
163
+ required={required}
164
+ value={value}
92
165
  options={["---", ...schemaObject.enum]}
93
166
  onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
94
167
  const val = e.target.value;
168
+ setValue(val);
95
169
  if (val === "---") {
96
170
  dispatch(clearFormBodyKey(id));
97
171
  } else {
@@ -109,12 +183,16 @@ export default function FormBodyItem({
109
183
  // TODO: support all the other types.
110
184
  return (
111
185
  <FormTextInput
186
+ label={label}
187
+ required={required}
188
+ value={value}
112
189
  paramName={id}
113
190
  isRequired={
114
191
  Array.isArray(schema.required) && schema.required.includes(id)
115
192
  }
116
193
  placeholder={schemaObject.description || id}
117
194
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
195
+ setValue(e.target.value);
118
196
  dispatch(setStringFormBody({ key: id, value: e.target.value }));
119
197
  }}
120
198
  />
@@ -93,6 +93,8 @@ function Body({
93
93
  const rawSchema = requestBodyMetadata?.content?.[contentType]?.schema;
94
94
  const example = requestBodyMetadata?.content?.[contentType]?.example;
95
95
  const examples = requestBodyMetadata?.content?.[contentType]?.examples;
96
+ const encoding: Record<string, { contentType?: string }> | undefined =
97
+ requestBodyMetadata?.content?.[contentType]?.encoding;
96
98
 
97
99
  // Resolve the schema based on user's anyOf/oneOf tab selections
98
100
  const schema = useMemo(() => {
@@ -316,23 +318,89 @@ function Body({
316
318
  ) {
317
319
  return (
318
320
  <FormItem className="openapi-explorer__form-item-body-container">
319
- {Object.entries(schema.properties ?? {}).map(([key, val]: any) => {
320
- return (
321
- <FormItem
322
- key={key}
323
- label={key}
324
- required={
325
- Array.isArray(schema.required) && schema.required.includes(key)
326
- }
327
- >
328
- <FormBodyItem
329
- schemaObject={val}
330
- id={key}
331
- schema={schema}
332
- ></FormBodyItem>
333
- </FormItem>
334
- );
335
- })}
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>
336
404
  </FormItem>
337
405
  );
338
406
  }
@@ -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
  );
@@ -0,0 +1,31 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
9
+
10
+ // Maps form field name → user-selected content type
11
+ export type State = Record<string, string>;
12
+
13
+ const initialState: State = {};
14
+
15
+ export const slice = createSlice({
16
+ name: "encodingSelection",
17
+ initialState,
18
+ reducers: {
19
+ setFieldEncoding: (
20
+ state,
21
+ action: PayloadAction<{ field: string; contentType: string }>
22
+ ) => {
23
+ state[action.payload.field] = action.payload.contentType;
24
+ },
25
+ clearEncodingSelection: () => initialState,
26
+ },
27
+ });
28
+
29
+ export const { setFieldEncoding, clearEncodingSelection } = slice.actions;
30
+
31
+ export default slice.reducer;
@@ -0,0 +1,43 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import { useTypedSelector } from "@theme/ApiItem/hooks";
9
+ import type { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
10
+
11
+ /**
12
+ * Merges the spec-declared `encoding` for the active content type with any
13
+ * per-field content-type selections the user has made in the UI. User picks
14
+ * take precedence over the spec default.
15
+ *
16
+ * Returns `undefined` when no encoding is declared for the current content
17
+ * type so callers can skip the encoding path entirely.
18
+ */
19
+ export function useResolvedEncoding(
20
+ requestBody: RequestBodyObject | undefined
21
+ ): Record<string, { contentType?: string }> | undefined {
22
+ const contentType = useTypedSelector((state: any) => state.contentType.value);
23
+ const encodingSelection = useTypedSelector(
24
+ (state: any) => state.encodingSelection
25
+ );
26
+
27
+ const specEncoding: Record<string, { contentType?: string }> =
28
+ requestBody?.content?.[contentType]?.encoding ?? {};
29
+
30
+ if (!Object.keys(specEncoding).length) {
31
+ return undefined;
32
+ }
33
+
34
+ return Object.fromEntries(
35
+ Object.entries(specEncoding).map(([field, enc]) => [
36
+ field,
37
+ {
38
+ ...enc,
39
+ contentType: encodingSelection[field] ?? enc.contentType,
40
+ },
41
+ ])
42
+ );
43
+ }
@@ -14,8 +14,3 @@
14
14
  .openapi-explorer__form-item-body-container {
15
15
  padding: 0;
16
16
  }
17
-
18
- .openapi-explorer__form-item-label {
19
- font-family: var(--ifm-font-family-monospace);
20
- font-weight: bold;
21
- }
@@ -7,31 +7,17 @@
7
7
 
8
8
  import React from "react";
9
9
 
10
- import { translate } from "@docusaurus/Translate";
11
- import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
12
10
  import clsx from "clsx";
13
11
 
14
12
  export interface Props {
15
- label?: string;
16
- type?: string;
17
- required?: boolean | undefined;
18
13
  children?: React.ReactNode;
19
14
  className?: string;
20
15
  }
21
16
 
22
- function FormItem({ label, type, required, children, className }: Props) {
17
+ function FormItem({ children, className }: Props) {
23
18
  return (
24
19
  <div className={clsx("openapi-explorer__form-item", className)}>
25
- {label && (
26
- <label className="openapi-explorer__form-item-label">{label}</label>
27
- )}
28
- {type && <span style={{ opacity: 0.6 }}> — {type}</span>}
29
- {required && (
30
- <span className="openapi-schema__required">
31
- {translate({ id: OPENAPI_SCHEMA_ITEM.REQUIRED, message: "required" })}
32
- </span>
33
- )}
34
- <div>{children}</div>
20
+ {children}
35
21
  </div>
36
22
  );
37
23
  }
@@ -0,0 +1,4 @@
1
+ .openapi-explorer__form-item-label {
2
+ font-family: var(--ifm-font-family-monospace);
3
+ font-weight: bold;
4
+ }
@@ -0,0 +1,43 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import React from "react";
9
+
10
+ import { translate } from "@docusaurus/Translate";
11
+ import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
12
+
13
+ export interface Props {
14
+ htmlFor?: string;
15
+ label: string;
16
+ type?: string;
17
+ required?: boolean;
18
+ }
19
+
20
+ function FormLabel({ htmlFor, label, type, required }: Props) {
21
+ return (
22
+ <>
23
+ {htmlFor ? (
24
+ <label className="openapi-explorer__form-item-label" htmlFor={htmlFor}>
25
+ {label}
26
+ </label>
27
+ ) : (
28
+ <span className="openapi-explorer__form-item-label">{label}</span>
29
+ )}
30
+ {type && <span style={{ opacity: 0.6 }}> — {type}</span>}
31
+ {required && (
32
+ <span className="openapi-schema__required">
33
+ {translate({
34
+ id: OPENAPI_SCHEMA_ITEM.REQUIRED,
35
+ message: "required",
36
+ })}
37
+ </span>
38
+ )}
39
+ </>
40
+ );
41
+ }
42
+
43
+ export default FormLabel;