docusaurus-theme-openapi-docs 4.6.0 → 4.7.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 (36) hide show
  1. package/lib/markdown/schema.js +5 -0
  2. package/lib/theme/ApiExplorer/Body/index.js +263 -128
  3. package/lib/theme/ApiExplorer/Body/resolveSchemaWithSelections.d.ts +13 -0
  4. package/lib/theme/ApiExplorer/Body/resolveSchemaWithSelections.js +133 -0
  5. package/lib/theme/ApiExplorer/SchemaSelection/index.d.ts +2 -0
  6. package/lib/theme/ApiExplorer/SchemaSelection/index.js +36 -0
  7. package/lib/theme/ApiExplorer/SchemaSelection/slice.d.ts +37 -0
  8. package/lib/theme/ApiExplorer/SchemaSelection/slice.js +39 -0
  9. package/lib/theme/ApiExplorer/persistenceMiddleware.d.ts +2 -0
  10. package/lib/theme/ApiItem/hooks.d.ts +1 -0
  11. package/lib/theme/ApiItem/index.js +1 -0
  12. package/lib/theme/ApiItem/store.d.ts +6 -0
  13. package/lib/theme/ApiItem/store.js +6 -2
  14. package/lib/theme/RequestSchema/index.js +58 -52
  15. package/lib/theme/Schema/index.d.ts +6 -0
  16. package/lib/theme/Schema/index.js +135 -9
  17. package/lib/theme/SchemaTabs/index.d.ts +8 -1
  18. package/lib/theme/SchemaTabs/index.js +10 -1
  19. package/lib/theme/StatusCodes/index.d.ts +1 -1
  20. package/lib/theme/styles.scss +10 -0
  21. package/package.json +3 -3
  22. package/src/markdown/schema.ts +6 -0
  23. package/src/theme/ApiExplorer/Body/index.tsx +206 -122
  24. package/src/theme/ApiExplorer/Body/resolveSchemaWithSelections.ts +155 -0
  25. package/{lib/types.js → src/theme/ApiExplorer/SchemaSelection/index.ts} +7 -2
  26. package/src/theme/ApiExplorer/SchemaSelection/slice.ts +46 -0
  27. package/src/theme/ApiItem/index.tsx +1 -0
  28. package/src/theme/ApiItem/store.ts +2 -0
  29. package/src/theme/RequestSchema/index.tsx +49 -39
  30. package/src/theme/Schema/index.tsx +184 -27
  31. package/src/theme/SchemaTabs/index.tsx +15 -4
  32. package/src/theme/StatusCodes/index.tsx +1 -2
  33. package/src/theme/styles.scss +10 -0
  34. package/tsconfig.tsbuildinfo +1 -1
  35. package/lib/types.d.ts +0 -54
  36. /package/src/{types.ts → types.d.ts} +0 -0
@@ -5,7 +5,7 @@
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, useMemo } from "react";
9
9
 
10
10
  import { translate } from "@docusaurus/Translate";
11
11
 
@@ -19,10 +19,12 @@ import SchemaTabs from "@theme/SchemaTabs";
19
19
  import TabItem from "@theme/TabItem";
20
20
  import { OPENAPI_BODY, OPENAPI_REQUEST } from "@theme/translationIds";
21
21
  import { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
22
+ import { sampleFromSchema } from "docusaurus-plugin-openapi-docs/src/openapi/createSchemaExample";
22
23
  import format from "xml-formatter";
23
24
 
24
25
  import { clearRawBody, setFileRawBody, setStringRawBody } from "./slice";
25
26
  import FormBodyItem from "./FormBodyItem";
27
+ import { resolveSchemaWithSelections } from "./resolveSchemaWithSelections";
26
28
 
27
29
  export interface Props {
28
30
  jsonRequestBodyExample: string;
@@ -63,6 +65,9 @@ function Body({
63
65
  required,
64
66
  }: Props) {
65
67
  const contentType = useTypedSelector((state: any) => state.contentType.value);
68
+ const schemaSelections = useTypedSelector(
69
+ (state: any) => state.schemaSelection?.selections ?? {}
70
+ );
66
71
  const dispatch = useTypedDispatch();
67
72
 
68
73
  // Lot's of possible content-types:
@@ -86,12 +91,200 @@ function Body({
86
91
  // - multipart/form-data
87
92
  // - application/x-www-form-urlencoded
88
93
 
89
- const schema = requestBodyMetadata?.content?.[contentType]?.schema;
94
+ const rawSchema = requestBodyMetadata?.content?.[contentType]?.schema;
90
95
  const example = requestBodyMetadata?.content?.[contentType]?.example;
91
96
  const examples = requestBodyMetadata?.content?.[contentType]?.examples;
97
+
98
+ // Resolve the schema based on user's anyOf/oneOf tab selections
99
+ const schema = useMemo(() => {
100
+ if (!rawSchema) return rawSchema;
101
+ return resolveSchemaWithSelections(
102
+ rawSchema,
103
+ schemaSelections,
104
+ "requestBody"
105
+ );
106
+ }, [rawSchema, schemaSelections]);
107
+
92
108
  // OpenAPI 3.1 / JSON Schema: schema.examples is an array of example values
93
109
  const schemaExamples = schema?.examples as any[] | undefined;
94
110
 
111
+ // Compute the default body based on content type and schema
112
+ // This needs to be computed before early returns so the useEffect can use it
113
+ const { defaultBody, exampleBody, examplesBodies, language } = useMemo(() => {
114
+ let lang = "plaintext";
115
+ let defBody = "";
116
+ let exBody;
117
+ let exBodies = [] as any;
118
+
119
+ // Skip body generation for binary and form content types
120
+ if (schema?.format === "binary") {
121
+ return {
122
+ defaultBody: defBody,
123
+ exampleBody: exBody,
124
+ examplesBodies: exBodies,
125
+ language: lang,
126
+ };
127
+ }
128
+ if (
129
+ (contentType === "multipart/form-data" ||
130
+ contentType === "application/x-www-form-urlencoded") &&
131
+ schema?.type === "object"
132
+ ) {
133
+ return {
134
+ defaultBody: defBody,
135
+ exampleBody: exBody,
136
+ examplesBodies: exBodies,
137
+ language: lang,
138
+ };
139
+ }
140
+
141
+ // Generate example from the schema for the current content type
142
+ let contentTypeExample;
143
+ if (schema) {
144
+ contentTypeExample = sampleFromSchema(schema, { type: "request" });
145
+ } else if (jsonRequestBodyExample) {
146
+ // Fallback to the build-time generated example if no schema is available
147
+ contentTypeExample = jsonRequestBodyExample;
148
+ }
149
+
150
+ if (
151
+ contentType?.includes("application/json") ||
152
+ contentType?.endsWith("+json")
153
+ ) {
154
+ if (contentTypeExample) {
155
+ defBody = JSON.stringify(contentTypeExample, null, 2);
156
+ }
157
+ if (example) {
158
+ exBody = JSON.stringify(example, null, 2);
159
+ }
160
+ if (examples) {
161
+ for (const [key, ex] of Object.entries(examples)) {
162
+ let body = ex.value;
163
+ try {
164
+ // If the value is already valid JSON we shouldn't double encode the value
165
+ JSON.parse(ex.value);
166
+ } catch (e) {
167
+ body = JSON.stringify(ex.value, null, 2);
168
+ }
169
+
170
+ exBodies.push({
171
+ label: key,
172
+ body,
173
+ summary: ex.summary,
174
+ });
175
+ }
176
+ }
177
+ // OpenAPI 3.1: schema.examples is an array of example values
178
+ if (schemaExamples && Array.isArray(schemaExamples)) {
179
+ schemaExamples.forEach((schemaExample, index) => {
180
+ const body = JSON.stringify(schemaExample, null, 2);
181
+ exBodies.push({
182
+ label: `Example ${index + 1}`,
183
+ body,
184
+ summary: undefined,
185
+ });
186
+ });
187
+ }
188
+ lang = "json";
189
+ }
190
+
191
+ if (contentType === "application/xml" || contentType?.endsWith("+xml")) {
192
+ if (contentTypeExample) {
193
+ try {
194
+ defBody = format(json2xml(contentTypeExample, ""), {
195
+ indentation: " ",
196
+ lineSeparator: "\n",
197
+ collapseContent: true,
198
+ });
199
+ } catch {
200
+ defBody = json2xml(contentTypeExample);
201
+ }
202
+ }
203
+ if (example) {
204
+ try {
205
+ exBody = format(json2xml(example, ""), {
206
+ indentation: " ",
207
+ lineSeparator: "\n",
208
+ collapseContent: true,
209
+ });
210
+ } catch {
211
+ exBody = json2xml(example);
212
+ }
213
+ }
214
+ if (examples) {
215
+ for (const [key, ex] of Object.entries(examples)) {
216
+ let formattedXmlBody;
217
+ try {
218
+ formattedXmlBody = format(ex.value, {
219
+ indentation: " ",
220
+ lineSeparator: "\n",
221
+ collapseContent: true,
222
+ });
223
+ } catch {
224
+ formattedXmlBody = ex.value;
225
+ }
226
+ exBodies.push({
227
+ label: key,
228
+ body: formattedXmlBody,
229
+ summary: ex.summary,
230
+ });
231
+ }
232
+ }
233
+ // OpenAPI 3.1: schema.examples is an array of example values
234
+ if (schemaExamples && Array.isArray(schemaExamples)) {
235
+ schemaExamples.forEach((schemaExample, index) => {
236
+ let formattedXmlBody;
237
+ try {
238
+ formattedXmlBody = format(json2xml(schemaExample, ""), {
239
+ indentation: " ",
240
+ lineSeparator: "\n",
241
+ collapseContent: true,
242
+ });
243
+ } catch {
244
+ formattedXmlBody = json2xml(schemaExample);
245
+ }
246
+ exBodies.push({
247
+ label: `Example ${index + 1}`,
248
+ body: formattedXmlBody,
249
+ summary: undefined,
250
+ });
251
+ });
252
+ }
253
+ lang = "xml";
254
+ }
255
+
256
+ return {
257
+ defaultBody: defBody,
258
+ exampleBody: exBody,
259
+ examplesBodies: exBodies,
260
+ language: lang,
261
+ };
262
+ }, [
263
+ schema,
264
+ contentType,
265
+ example,
266
+ examples,
267
+ schemaExamples,
268
+ jsonRequestBodyExample,
269
+ ]);
270
+
271
+ // Create a stable key for the LiveApp component that changes when schema selection changes
272
+ // This forces the editor to remount and pick up the new defaultBody
273
+ const schemaSelectionKey = useMemo(
274
+ () => JSON.stringify(schemaSelections),
275
+ [schemaSelections]
276
+ );
277
+
278
+ // Update body in Redux when content type or schema selection changes
279
+ useEffect(() => {
280
+ if (defaultBody) {
281
+ dispatch(setStringRawBody(defaultBody));
282
+ }
283
+ // Re-run when contentType, schemaSelections, or defaultBody change
284
+ // eslint-disable-next-line react-hooks/exhaustive-deps
285
+ }, [contentType, schemaSelections, defaultBody]);
286
+
287
+ // Now handle early returns after all hooks have been called
95
288
  if (schema?.format === "binary") {
96
289
  return (
97
290
  <FormItem>
@@ -116,6 +309,7 @@ function Body({
116
309
  </FormItem>
117
310
  );
118
311
  }
312
+
119
313
  if (
120
314
  (contentType === "multipart/form-data" ||
121
315
  contentType === "application/x-www-form-urlencoded") &&
@@ -144,117 +338,6 @@ function Body({
144
338
  );
145
339
  }
146
340
 
147
- let language = "plaintext";
148
- let defaultBody = ""; //"body content";
149
- let exampleBody;
150
- let examplesBodies = [] as any;
151
-
152
- if (
153
- contentType.includes("application/json") ||
154
- contentType.endsWith("+json")
155
- ) {
156
- if (jsonRequestBodyExample) {
157
- defaultBody = JSON.stringify(jsonRequestBodyExample, null, 2);
158
- }
159
- if (example) {
160
- exampleBody = JSON.stringify(example, null, 2);
161
- }
162
- if (examples) {
163
- for (const [key, example] of Object.entries(examples)) {
164
- let body = example.value;
165
- try {
166
- // If the value is already valid JSON we shouldn't double encode the value
167
- JSON.parse(example.value);
168
- } catch (e) {
169
- body = JSON.stringify(example.value, null, 2);
170
- }
171
-
172
- examplesBodies.push({
173
- label: key,
174
- body,
175
- summary: example.summary,
176
- });
177
- }
178
- }
179
- // OpenAPI 3.1: schema.examples is an array of example values
180
- if (schemaExamples && Array.isArray(schemaExamples)) {
181
- schemaExamples.forEach((schemaExample, index) => {
182
- const body = JSON.stringify(schemaExample, null, 2);
183
- examplesBodies.push({
184
- label: `Example ${index + 1}`,
185
- body,
186
- summary: undefined,
187
- });
188
- });
189
- }
190
- language = "json";
191
- }
192
-
193
- if (contentType === "application/xml" || contentType.endsWith("+xml")) {
194
- if (jsonRequestBodyExample) {
195
- try {
196
- defaultBody = format(json2xml(jsonRequestBodyExample, ""), {
197
- indentation: " ",
198
- lineSeparator: "\n",
199
- collapseContent: true,
200
- });
201
- } catch {
202
- defaultBody = json2xml(jsonRequestBodyExample);
203
- }
204
- }
205
- if (example) {
206
- try {
207
- exampleBody = format(json2xml(example, ""), {
208
- indentation: " ",
209
- lineSeparator: "\n",
210
- collapseContent: true,
211
- });
212
- } catch {
213
- exampleBody = json2xml(example);
214
- }
215
- }
216
- if (examples) {
217
- for (const [key, example] of Object.entries(examples)) {
218
- let formattedXmlBody;
219
- try {
220
- formattedXmlBody = format(example.value, {
221
- indentation: " ",
222
- lineSeparator: "\n",
223
- collapseContent: true,
224
- });
225
- } catch {
226
- formattedXmlBody = example.value;
227
- }
228
- examplesBodies.push({
229
- label: key,
230
- body: formattedXmlBody,
231
- summary: example.summary,
232
- });
233
- }
234
- }
235
- // OpenAPI 3.1: schema.examples is an array of example values
236
- if (schemaExamples && Array.isArray(schemaExamples)) {
237
- schemaExamples.forEach((schemaExample, index) => {
238
- let formattedXmlBody;
239
- try {
240
- formattedXmlBody = format(json2xml(schemaExample, ""), {
241
- indentation: " ",
242
- lineSeparator: "\n",
243
- collapseContent: true,
244
- });
245
- } catch {
246
- formattedXmlBody = json2xml(schemaExample);
247
- }
248
- examplesBodies.push({
249
- label: `Example ${index + 1}`,
250
- body: formattedXmlBody,
251
- summary: undefined,
252
- });
253
- });
254
- }
255
- language = "xml";
256
- }
257
-
258
341
  if (exampleBody) {
259
342
  return (
260
343
  <FormItem>
@@ -269,6 +352,7 @@ function Body({
269
352
  default
270
353
  >
271
354
  <LiveApp
355
+ key={`${contentType}-${schemaSelectionKey}`}
272
356
  action={(code: string) => dispatch(setStringRawBody(code))}
273
357
  language={language}
274
358
  required={required}
@@ -281,6 +365,7 @@ function Body({
281
365
  {example.summary && <Markdown>{example.summary}</Markdown>}
282
366
  {exampleBody && (
283
367
  <LiveApp
368
+ key={`${contentType}-example`}
284
369
  action={(code: string) => dispatch(setStringRawBody(code))}
285
370
  language={language}
286
371
  required={required}
@@ -308,6 +393,7 @@ function Body({
308
393
  default
309
394
  >
310
395
  <LiveApp
396
+ key={`${contentType}-${schemaSelectionKey}`}
311
397
  action={(code: string) => dispatch(setStringRawBody(code))}
312
398
  language={language}
313
399
  required={required}
@@ -315,21 +401,18 @@ function Body({
315
401
  {defaultBody}
316
402
  </LiveApp>
317
403
  </TabItem>
318
- {examplesBodies.map((example: any) => {
404
+ {examplesBodies.map((ex: any) => {
319
405
  return (
320
406
  // @ts-ignore
321
- <TabItem
322
- label={example.label}
323
- value={example.label}
324
- key={example.label}
325
- >
326
- {example.summary && <Markdown>{example.summary}</Markdown>}
327
- {example.body && (
407
+ <TabItem label={ex.label} value={ex.label} key={ex.label}>
408
+ {ex.summary && <Markdown>{ex.summary}</Markdown>}
409
+ {ex.body && (
328
410
  <LiveApp
411
+ key={`${contentType}-${ex.label}`}
329
412
  action={(code: string) => dispatch(setStringRawBody(code))}
330
413
  language={language}
331
414
  >
332
- {example.body}
415
+ {ex.body}
333
416
  </LiveApp>
334
417
  )}
335
418
  </TabItem>
@@ -343,6 +426,7 @@ function Body({
343
426
  return (
344
427
  <FormItem>
345
428
  <LiveApp
429
+ key={`${contentType}-${schemaSelectionKey}`}
346
430
  action={(code: string) => dispatch(setStringRawBody(code))}
347
431
  language={language}
348
432
  required={required}
@@ -0,0 +1,155 @@
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 { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
9
+ import merge from "lodash/merge";
10
+
11
+ export interface SchemaSelections {
12
+ [schemaPath: string]: number;
13
+ }
14
+
15
+ /**
16
+ * Resolves a schema by replacing anyOf/oneOf with the selected option based on user selections.
17
+ *
18
+ * @param schema - The original schema object
19
+ * @param selections - Map of schema paths to selected indices
20
+ * @param basePath - The base path for this schema (used for looking up selections)
21
+ * @returns A new schema with anyOf/oneOf resolved to selected options
22
+ */
23
+ export function resolveSchemaWithSelections(
24
+ schema: SchemaObject | undefined,
25
+ selections: SchemaSelections,
26
+ basePath: string = "requestBody"
27
+ ): SchemaObject | undefined {
28
+ if (!schema) {
29
+ return schema;
30
+ }
31
+
32
+ // Deep clone to avoid mutating the original schema
33
+ const schemaCopy = JSON.parse(JSON.stringify(schema)) as SchemaObject;
34
+
35
+ return resolveSchemaRecursive(schemaCopy, selections, basePath);
36
+ }
37
+
38
+ function resolveSchemaRecursive(
39
+ schema: SchemaObject,
40
+ selections: SchemaSelections,
41
+ currentPath: string
42
+ ): SchemaObject {
43
+ // Handle oneOf
44
+ if (schema.oneOf && Array.isArray(schema.oneOf)) {
45
+ const selectedIndex = selections[currentPath] ?? 0;
46
+ const selectedSchema = schema.oneOf[selectedIndex] as SchemaObject;
47
+
48
+ if (selectedSchema) {
49
+ // If there are shared properties, merge them with the selected schema
50
+ if (schema.properties) {
51
+ const mergedSchema = merge({}, schema, selectedSchema);
52
+ delete mergedSchema.oneOf;
53
+
54
+ // Continue resolving nested schemas in the merged result
55
+ return resolveSchemaRecursive(
56
+ mergedSchema,
57
+ selections,
58
+ `${currentPath}.${selectedIndex}`
59
+ );
60
+ }
61
+
62
+ // No shared properties, just use the selected schema
63
+ // Continue resolving in case there are nested anyOf/oneOf
64
+ return resolveSchemaRecursive(
65
+ selectedSchema,
66
+ selections,
67
+ `${currentPath}.${selectedIndex}`
68
+ );
69
+ }
70
+ }
71
+
72
+ // Handle anyOf
73
+ if (schema.anyOf && Array.isArray(schema.anyOf)) {
74
+ const selectedIndex = selections[currentPath] ?? 0;
75
+ const selectedSchema = schema.anyOf[selectedIndex] as SchemaObject;
76
+
77
+ if (selectedSchema) {
78
+ // If there are shared properties, merge them with the selected schema
79
+ if (schema.properties) {
80
+ const mergedSchema = merge({}, schema, selectedSchema);
81
+ delete mergedSchema.anyOf;
82
+
83
+ // Continue resolving nested schemas in the merged result
84
+ return resolveSchemaRecursive(
85
+ mergedSchema,
86
+ selections,
87
+ `${currentPath}.${selectedIndex}`
88
+ );
89
+ }
90
+
91
+ // No shared properties, just use the selected schema
92
+ // Continue resolving in case there are nested anyOf/oneOf
93
+ return resolveSchemaRecursive(
94
+ selectedSchema,
95
+ selections,
96
+ `${currentPath}.${selectedIndex}`
97
+ );
98
+ }
99
+ }
100
+
101
+ // Handle allOf - merge all schemas and continue resolving
102
+ if (schema.allOf && Array.isArray(schema.allOf)) {
103
+ // Process each allOf item, resolving any anyOf/oneOf within them
104
+ const resolvedItems = schema.allOf.map((item, index) => {
105
+ return resolveSchemaRecursive(
106
+ item as SchemaObject,
107
+ selections,
108
+ `${currentPath}.allOf.${index}`
109
+ );
110
+ });
111
+
112
+ // Merge all resolved items
113
+ const mergedSchema = resolvedItems.reduce(
114
+ (acc, item) => merge(acc, item),
115
+ {} as SchemaObject
116
+ );
117
+
118
+ // Preserve any top-level properties from the original schema
119
+ if (schema.properties) {
120
+ mergedSchema.properties = merge(
121
+ {},
122
+ mergedSchema.properties,
123
+ schema.properties
124
+ );
125
+ }
126
+
127
+ return mergedSchema;
128
+ }
129
+
130
+ // Handle object properties recursively
131
+ if (schema.properties) {
132
+ const resolvedProperties: { [key: string]: SchemaObject } = {};
133
+
134
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
135
+ resolvedProperties[propName] = resolveSchemaRecursive(
136
+ propSchema as SchemaObject,
137
+ selections,
138
+ `${currentPath}.${propName}`
139
+ );
140
+ }
141
+
142
+ schema.properties = resolvedProperties;
143
+ }
144
+
145
+ // Handle array items recursively
146
+ if (schema.items) {
147
+ schema.items = resolveSchemaRecursive(
148
+ schema.items as SchemaObject,
149
+ selections,
150
+ `${currentPath}.items`
151
+ );
152
+ }
153
+
154
+ return schema;
155
+ }
@@ -1,8 +1,13 @@
1
- "use strict";
2
1
  /* ============================================================================
3
2
  * Copyright (c) Palo Alto Networks
4
3
  *
5
4
  * This source code is licensed under the MIT license found in the
6
5
  * LICENSE file in the root directory of this source tree.
7
6
  * ========================================================================== */
8
- Object.defineProperty(exports, "__esModule", { value: true });
7
+
8
+ export {
9
+ default as schemaSelectionReducer,
10
+ setSchemaSelection,
11
+ clearSchemaSelections,
12
+ } from "./slice";
13
+ export type { SchemaSelectionState } from "./slice";
@@ -0,0 +1,46 @@
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
+ export interface SchemaSelectionState {
11
+ /**
12
+ * Maps schema path (e.g., "requestBody", "requestBody.anyOf.0.layer3")
13
+ * to the selected anyOf/oneOf option index
14
+ */
15
+ selections: { [schemaPath: string]: number };
16
+ }
17
+
18
+ const initialState: SchemaSelectionState = {
19
+ selections: {},
20
+ };
21
+
22
+ export const slice = createSlice({
23
+ name: "schemaSelection",
24
+ initialState,
25
+ reducers: {
26
+ /**
27
+ * Set the selected index for a specific schema path
28
+ */
29
+ setSchemaSelection: (
30
+ state,
31
+ action: PayloadAction<{ path: string; index: number }>
32
+ ) => {
33
+ state.selections[action.payload.path] = action.payload.index;
34
+ },
35
+ /**
36
+ * Clear all schema selections (useful when navigating to a new API endpoint)
37
+ */
38
+ clearSchemaSelections: (state) => {
39
+ state.selections = {};
40
+ },
41
+ },
42
+ });
43
+
44
+ export const { setSchemaSelection, clearSchemaSelections } = slice.actions;
45
+
46
+ export default slice.reducer;
@@ -158,6 +158,7 @@ export default function ApiItem(props: Props): JSX.Element {
158
158
  body: { type: "empty" },
159
159
  params,
160
160
  auth,
161
+ schemaSelection: { selections: {} },
161
162
  },
162
163
  [persistenceMiddleware]
163
164
  );
@@ -12,6 +12,7 @@ import body from "@theme/ApiExplorer/Body/slice";
12
12
  import contentType from "@theme/ApiExplorer/ContentType/slice";
13
13
  import params from "@theme/ApiExplorer/ParamOptions/slice";
14
14
  import response from "@theme/ApiExplorer/Response/slice";
15
+ import schemaSelection from "@theme/ApiExplorer/SchemaSelection/slice";
15
16
  import server from "@theme/ApiExplorer/Server/slice";
16
17
 
17
18
  const rootReducer = combineReducers({
@@ -22,6 +23,7 @@ const rootReducer = combineReducers({
22
23
  body,
23
24
  params,
24
25
  auth,
26
+ schemaSelection,
25
27
  });
26
28
 
27
29
  export type RootState = ReturnType<typeof rootReducer>;