@gitbook/react-openapi 1.1.9 → 1.2.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 (177) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/InteractiveSection.d.ts +4 -0
  3. package/dist/InteractiveSection.jsx +14 -13
  4. package/dist/OpenAPICodeSample.d.ts +3 -2
  5. package/dist/OpenAPICodeSample.jsx +8 -12
  6. package/dist/OpenAPICodeSampleInteractive.d.ts +3 -0
  7. package/dist/OpenAPICodeSampleInteractive.jsx +37 -49
  8. package/dist/OpenAPICodeSampleSelector.d.ts +14 -0
  9. package/dist/OpenAPICodeSampleSelector.jsx +44 -0
  10. package/dist/OpenAPICopyButton.d.ts +2 -0
  11. package/dist/OpenAPICopyButton.jsx +5 -2
  12. package/dist/OpenAPIDisclosure.d.ts +4 -3
  13. package/dist/OpenAPIDisclosure.jsx +8 -11
  14. package/dist/OpenAPIDisclosureGroup.d.ts +7 -3
  15. package/dist/OpenAPIDisclosureGroup.jsx +18 -18
  16. package/dist/OpenAPIExample.d.ts +16 -0
  17. package/dist/OpenAPIExample.jsx +36 -0
  18. package/dist/OpenAPIMediaType.d.ts +21 -0
  19. package/dist/OpenAPIMediaType.jsx +61 -0
  20. package/dist/OpenAPIOperation.d.ts +3 -2
  21. package/dist/OpenAPIOperation.jsx +9 -72
  22. package/dist/OpenAPIOperationDescription.d.ts +9 -0
  23. package/dist/OpenAPIOperationDescription.jsx +22 -0
  24. package/dist/OpenAPIOperationStability.d.ts +9 -0
  25. package/dist/OpenAPIOperationStability.jsx +27 -0
  26. package/dist/OpenAPIPath.d.ts +12 -2
  27. package/dist/OpenAPIPath.jsx +10 -4
  28. package/dist/OpenAPIRequestBody.d.ts +3 -1
  29. package/dist/OpenAPIRequestBody.jsx +4 -3
  30. package/dist/OpenAPIResponse.d.ts +1 -1
  31. package/dist/OpenAPIResponse.jsx +1 -1
  32. package/dist/OpenAPIResponseExample.d.ts +4 -3
  33. package/dist/OpenAPIResponseExample.jsx +24 -154
  34. package/dist/OpenAPIResponseExampleContent.d.ts +19 -0
  35. package/dist/OpenAPIResponseExampleContent.jsx +57 -0
  36. package/dist/OpenAPIResponses.d.ts +1 -1
  37. package/dist/OpenAPIResponses.jsx +49 -36
  38. package/dist/OpenAPISchema.d.ts +1 -1
  39. package/dist/OpenAPISchema.jsx +121 -20
  40. package/dist/OpenAPISchemaName.d.ts +2 -0
  41. package/dist/OpenAPISchemaName.jsx +21 -17
  42. package/dist/OpenAPISchemaServer.d.ts +1 -1
  43. package/dist/OpenAPISecurities.d.ts +2 -1
  44. package/dist/OpenAPISecurities.jsx +11 -10
  45. package/dist/OpenAPISelect.d.ts +22 -0
  46. package/dist/OpenAPISelect.jsx +43 -0
  47. package/dist/OpenAPISpec.d.ts +3 -2
  48. package/dist/OpenAPISpec.jsx +11 -9
  49. package/dist/OpenAPITabs.jsx +9 -9
  50. package/dist/OpenAPIWebhook.d.ts +10 -0
  51. package/dist/OpenAPIWebhook.jsx +23 -0
  52. package/dist/OpenAPIWebhookExample.d.ts +6 -0
  53. package/dist/OpenAPIWebhookExample.jsx +41 -0
  54. package/dist/ScalarApiButton.d.ts +2 -0
  55. package/dist/ScalarApiButton.jsx +4 -3
  56. package/dist/StaticSection.d.ts +4 -1
  57. package/dist/StaticSection.jsx +13 -4
  58. package/dist/code-samples.js +57 -39
  59. package/dist/common/OpenAPIColumnSpec.d.ts +6 -0
  60. package/dist/common/OpenAPIColumnSpec.jsx +20 -0
  61. package/dist/common/OpenAPIOperationDescription.d.ts +6 -0
  62. package/dist/common/OpenAPIOperationDescription.jsx +19 -0
  63. package/dist/common/OpenAPIStability.d.ts +4 -0
  64. package/dist/common/OpenAPIStability.jsx +15 -0
  65. package/dist/common/OpenAPISummary.d.ts +6 -0
  66. package/dist/common/OpenAPISummary.jsx +30 -0
  67. package/dist/context.d.ts +75 -0
  68. package/dist/context.js +43 -0
  69. package/dist/generateSchemaExample.js +4 -0
  70. package/dist/getOrCreateStoreByKey.d.ts +10 -0
  71. package/dist/getOrCreateStoreByKey.js +19 -0
  72. package/dist/index.d.ts +5 -1
  73. package/dist/index.js +3 -0
  74. package/dist/resolveOpenAPIOperation.js +10 -5
  75. package/dist/resolveOpenAPIWebhook.d.ts +11 -0
  76. package/dist/resolveOpenAPIWebhook.js +127 -0
  77. package/dist/schemas/OpenAPISchemas.d.ts +5 -6
  78. package/dist/schemas/OpenAPISchemas.jsx +52 -49
  79. package/dist/schemas/resolveOpenAPISchemas.d.ts +4 -3
  80. package/dist/schemas/resolveOpenAPISchemas.js +0 -1
  81. package/dist/stringifyOpenAPI.d.ts +1 -1
  82. package/dist/stringifyOpenAPI.js +6 -3
  83. package/dist/translate.d.ts +10 -0
  84. package/dist/translate.jsx +75 -0
  85. package/dist/translations/de.d.ts +37 -0
  86. package/dist/translations/de.js +37 -0
  87. package/dist/translations/en.d.ts +37 -0
  88. package/dist/translations/en.js +37 -0
  89. package/dist/translations/es.d.ts +37 -0
  90. package/dist/translations/es.js +37 -0
  91. package/dist/translations/fr.d.ts +37 -0
  92. package/dist/translations/fr.js +37 -0
  93. package/dist/translations/index.d.ts +341 -0
  94. package/dist/translations/index.js +27 -0
  95. package/dist/translations/ja.d.ts +37 -0
  96. package/dist/translations/ja.js +37 -0
  97. package/dist/translations/nl.d.ts +37 -0
  98. package/dist/translations/nl.js +37 -0
  99. package/dist/translations/no.d.ts +37 -0
  100. package/dist/translations/no.js +37 -0
  101. package/dist/translations/pt-br.d.ts +37 -0
  102. package/dist/translations/pt-br.js +37 -0
  103. package/dist/translations/types.d.ts +5 -0
  104. package/dist/translations/types.js +1 -0
  105. package/dist/translations/zh.d.ts +37 -0
  106. package/dist/translations/zh.js +37 -0
  107. package/dist/tsconfig.build.tsbuildinfo +1 -1
  108. package/dist/types.d.ts +12 -48
  109. package/dist/util/example.d.ts +35 -0
  110. package/dist/util/example.jsx +103 -0
  111. package/dist/utils.d.ts +18 -0
  112. package/dist/utils.js +57 -0
  113. package/package.json +3 -3
  114. package/src/InteractiveSection.tsx +22 -18
  115. package/src/OpenAPICodeSample.tsx +26 -15
  116. package/src/OpenAPICodeSampleInteractive.tsx +67 -70
  117. package/src/OpenAPICodeSampleSelector.tsx +94 -0
  118. package/src/OpenAPICopyButton.tsx +7 -2
  119. package/src/OpenAPIDisclosure.tsx +20 -22
  120. package/src/OpenAPIDisclosureGroup.tsx +40 -22
  121. package/src/OpenAPIExample.tsx +55 -0
  122. package/src/OpenAPIMediaType.tsx +139 -0
  123. package/src/OpenAPIOperation.tsx +11 -104
  124. package/src/OpenAPIOperationDescription.tsx +34 -0
  125. package/src/OpenAPIOperationStability.tsx +39 -0
  126. package/src/OpenAPIPath.tsx +26 -6
  127. package/src/OpenAPIRequestBody.tsx +9 -4
  128. package/src/OpenAPIResponse.tsx +2 -2
  129. package/src/OpenAPIResponseExample.tsx +41 -215
  130. package/src/OpenAPIResponseExampleContent.tsx +123 -0
  131. package/src/OpenAPIResponses.tsx +83 -62
  132. package/src/OpenAPISchema.test.ts +80 -0
  133. package/src/OpenAPISchema.tsx +149 -25
  134. package/src/OpenAPISchemaName.tsx +28 -19
  135. package/src/OpenAPISchemaServer.tsx +1 -1
  136. package/src/OpenAPISecurities.tsx +46 -12
  137. package/src/OpenAPISelect.tsx +96 -0
  138. package/src/OpenAPISpec.tsx +21 -10
  139. package/src/OpenAPITabs.tsx +9 -9
  140. package/src/OpenAPIWebhook.tsx +33 -0
  141. package/src/OpenAPIWebhookExample.tsx +60 -0
  142. package/src/ScalarApiButton.tsx +6 -6
  143. package/src/StaticSection.tsx +37 -5
  144. package/src/code-samples.test.ts +3 -1
  145. package/src/code-samples.ts +67 -54
  146. package/src/common/OpenAPIColumnSpec.tsx +31 -0
  147. package/src/common/OpenAPIOperationDescription.tsx +31 -0
  148. package/src/common/OpenAPIStability.tsx +23 -0
  149. package/src/common/OpenAPISummary.tsx +45 -0
  150. package/src/context.ts +99 -0
  151. package/src/generateSchemaExample.test.ts +1020 -0
  152. package/src/generateSchemaExample.ts +5 -0
  153. package/src/getOrCreateStoreByKey.ts +33 -0
  154. package/src/index.ts +5 -1
  155. package/src/resolveOpenAPIOperation.ts +14 -3
  156. package/src/resolveOpenAPIWebhook.ts +99 -0
  157. package/src/schemas/OpenAPISchemas.tsx +76 -71
  158. package/src/schemas/resolveOpenAPISchemas.ts +4 -5
  159. package/src/stringifyOpenAPI.ts +11 -3
  160. package/src/translate.tsx +80 -0
  161. package/src/translations/de.ts +37 -0
  162. package/src/translations/en.ts +37 -0
  163. package/src/translations/es.ts +37 -0
  164. package/src/translations/fr.ts +37 -0
  165. package/src/translations/index.ts +33 -0
  166. package/src/translations/ja.ts +37 -0
  167. package/src/translations/nl.ts +37 -0
  168. package/src/translations/no.ts +37 -0
  169. package/src/translations/pt-br.ts +37 -0
  170. package/src/translations/types.ts +7 -0
  171. package/src/translations/zh.ts +37 -0
  172. package/src/types.ts +11 -46
  173. package/src/util/example.tsx +129 -0
  174. package/src/utils.ts +67 -0
  175. package/dist/useSyncedTabsGlobalState.d.ts +0 -10
  176. package/dist/useSyncedTabsGlobalState.js +0 -20
  177. package/src/useSyncedTabsGlobalState.ts +0 -35
@@ -1,19 +1,20 @@
1
1
  import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
2
  import { Markdown } from './Markdown';
3
- import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
4
- import { StaticSection } from './StaticSection';
5
- import { generateSchemaExample } from './generateSchemaExample';
6
- import { json2xml } from './json2xml';
7
- import { stringifyOpenAPI } from './stringifyOpenAPI';
8
- import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
9
- import { checkIsReference, createStateKey, resolveDescription } from './utils';
3
+ import { OpenAPIEmptyExample, OpenAPIExample } from './OpenAPIExample';
4
+ import { OpenAPIMediaTypeContent } from './OpenAPIMediaType';
5
+ import { OpenAPIResponseExampleContent } from './OpenAPIResponseExampleContent';
6
+ import { type OpenAPIContext, getOpenAPIClientContext } from './context';
7
+ import type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
8
+ import { getExampleFromReference, getExamples } from './util/example';
9
+ import { createStateKey, getStatusCodeDefaultLabel } from './utils';
10
+ import { checkIsReference, resolveDescription } from './utils';
10
11
 
11
12
  /**
12
13
  * Display an example of the response content.
13
14
  */
14
15
  export function OpenAPIResponseExample(props: {
15
- data: OpenAPIOperationData;
16
- context: OpenAPIContextProps;
16
+ data: OpenAPIOperationData | OpenAPIWebhookData;
17
+ context: OpenAPIContext;
17
18
  }) {
18
19
  const { data, context } = props;
19
20
 
@@ -42,36 +43,41 @@ export function OpenAPIResponseExample(props: {
42
43
 
43
44
  const tabs = responses.map(([key, responseObject]) => {
44
45
  const description = resolveDescription(responseObject);
46
+ const label = description ? (
47
+ <Markdown source={description} />
48
+ ) : (
49
+ getStatusCodeDefaultLabel(key, context)
50
+ );
45
51
 
46
52
  if (checkIsReference(responseObject)) {
47
53
  return {
48
54
  key: key,
49
- label: key,
55
+ label,
56
+ statusCode: key,
50
57
  body: (
51
58
  <OpenAPIExample
52
- example={getExampleFromReference(responseObject)}
59
+ example={getExampleFromReference(responseObject, context)}
53
60
  context={context}
54
61
  syntax="json"
55
62
  />
56
63
  ),
57
- footer: description ? <Markdown source={description} /> : undefined,
58
64
  };
59
65
  }
60
66
 
61
67
  if (!responseObject.content || Object.keys(responseObject.content).length === 0) {
62
68
  return {
63
69
  key: key,
64
- label: key,
65
- body: <OpenAPIEmptyResponseExample />,
66
- footer: description ? <Markdown source={description} /> : undefined,
70
+ label,
71
+ statusCode: key,
72
+ body: <OpenAPIEmptyExample context={context} />,
67
73
  };
68
74
  }
69
75
 
70
76
  return {
71
77
  key: key,
72
- label: key,
78
+ label,
79
+ statusCode: key,
73
80
  body: <OpenAPIResponse context={context} content={responseObject.content} />,
74
- footer: description ? <Markdown source={description} /> : undefined,
75
81
  };
76
82
  });
77
83
 
@@ -80,16 +86,16 @@ export function OpenAPIResponseExample(props: {
80
86
  }
81
87
 
82
88
  return (
83
- <OpenAPITabs stateKey={createStateKey('response-example')} items={tabs}>
84
- <StaticSection header={<OpenAPITabsList />} className="openapi-response-example">
85
- <OpenAPITabsPanels />
86
- </StaticSection>
87
- </OpenAPITabs>
89
+ <OpenAPIResponseExampleContent
90
+ selectIcon={context.icons.chevronDown}
91
+ blockKey={context.blockKey}
92
+ items={tabs}
93
+ />
88
94
  );
89
95
  }
90
96
 
91
97
  function OpenAPIResponse(props: {
92
- context: OpenAPIContextProps;
98
+ context: OpenAPIContext;
93
99
  content: {
94
100
  [media: string]: OpenAPIV3.MediaTypeObject;
95
101
  };
@@ -103,206 +109,26 @@ function OpenAPIResponse(props: {
103
109
  throw new Error('One media type is required');
104
110
  }
105
111
 
106
- if (entries.length === 1) {
107
- const [mediaType, mediaTypeObject] = firstEntry;
108
- return (
109
- <OpenAPIResponseMediaType
110
- context={context}
111
- mediaType={mediaType}
112
- mediaTypeObject={mediaTypeObject}
113
- />
114
- );
115
- }
116
-
117
112
  const tabs = entries.map((entry) => {
118
113
  const [mediaType, mediaTypeObject] = entry;
119
114
  return {
120
115
  key: mediaType,
121
116
  label: mediaType,
122
- body: (
123
- <OpenAPIResponseMediaType
124
- context={context}
125
- mediaType={mediaType}
126
- mediaTypeObject={mediaTypeObject}
127
- />
128
- ),
129
- };
130
- });
131
-
132
- return (
133
- <OpenAPITabs stateKey={createStateKey('response-media-types')} items={tabs}>
134
- <StaticSection header={<OpenAPITabsList />} className="openapi-response-media-types">
135
- <OpenAPITabsPanels />
136
- </StaticSection>
137
- </OpenAPITabs>
138
- );
139
- }
140
-
141
- function OpenAPIResponseMediaType(props: {
142
- mediaTypeObject: OpenAPIV3.MediaTypeObject;
143
- mediaType: string;
144
- context: OpenAPIContextProps;
145
- }) {
146
- const { mediaTypeObject, mediaType } = props;
147
- const examples = getExamplesFromMediaTypeObject({ mediaTypeObject, mediaType });
148
- const syntax = getSyntaxFromMediaType(mediaType);
149
- const firstExample = examples[0];
150
-
151
- if (!firstExample) {
152
- return <OpenAPIEmptyResponseExample />;
153
- }
154
-
155
- if (examples.length === 1) {
156
- return (
157
- <OpenAPIExample
158
- example={firstExample.example}
159
- context={props.context}
160
- syntax={syntax}
161
- />
162
- );
163
- }
164
-
165
- const tabs = examples.map((example) => {
166
- return {
167
- key: example.key,
168
- label: example.example.summary || example.key,
169
- body: (
170
- <OpenAPIExample example={example.example} context={props.context} syntax={syntax} />
171
- ),
117
+ body: <></>,
118
+ examples: getExamples({
119
+ mediaTypeObject,
120
+ mediaType,
121
+ context,
122
+ }),
172
123
  };
173
124
  });
174
125
 
175
126
  return (
176
- <OpenAPITabs stateKey={createStateKey('response-media-type-examples')} items={tabs}>
177
- <StaticSection
178
- header={<OpenAPITabsList />}
179
- className="openapi-response-media-type-examples"
180
- >
181
- <OpenAPITabsPanels />
182
- </StaticSection>
183
- </OpenAPITabs>
127
+ <OpenAPIMediaTypeContent
128
+ selectIcon={context.icons.chevronDown}
129
+ stateKey={createStateKey('response-media-types', context.blockKey)}
130
+ items={tabs}
131
+ context={getOpenAPIClientContext(context)}
132
+ />
184
133
  );
185
134
  }
186
-
187
- /**
188
- * Display an example.
189
- */
190
- function OpenAPIExample(props: {
191
- example: OpenAPIV3.ExampleObject;
192
- context: OpenAPIContextProps;
193
- syntax: string;
194
- }) {
195
- const { example, context, syntax } = props;
196
- const code = stringifyExample({ example, xml: syntax === 'xml' });
197
-
198
- if (code === null) {
199
- return <OpenAPIEmptyResponseExample />;
200
- }
201
-
202
- return context.renderCodeBlock({ code, syntax });
203
- }
204
-
205
- function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean }): string | null {
206
- const { example, xml } = args;
207
-
208
- if (!example.value) {
209
- return null;
210
- }
211
-
212
- if (typeof example.value === 'string') {
213
- return example.value;
214
- }
215
-
216
- if (xml) {
217
- return json2xml(example.value);
218
- }
219
-
220
- return stringifyOpenAPI(example.value, null, 2);
221
- }
222
-
223
- /**
224
- * Get the syntax from a media type.
225
- */
226
- function getSyntaxFromMediaType(mediaType: string): string {
227
- if (mediaType.includes('json')) {
228
- return 'json';
229
- }
230
-
231
- if (mediaType === 'application/xml') {
232
- return 'xml';
233
- }
234
-
235
- return 'text';
236
- }
237
-
238
- /**
239
- * Get examples from a media type object.
240
- */
241
- function getExamplesFromMediaTypeObject(args: {
242
- mediaType: string;
243
- mediaTypeObject: OpenAPIV3.MediaTypeObject;
244
- }): { key: string; example: OpenAPIV3.ExampleObject }[] {
245
- const { mediaTypeObject, mediaType } = args;
246
- if (mediaTypeObject.examples) {
247
- return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
248
- return {
249
- key,
250
- example: checkIsReference(example) ? getExampleFromReference(example) : example,
251
- };
252
- });
253
- }
254
-
255
- if (mediaTypeObject.example) {
256
- return [{ key: 'default', example: { value: mediaTypeObject.example } }];
257
- }
258
-
259
- if (mediaTypeObject.schema) {
260
- if (mediaType === 'application/xml') {
261
- // @TODO normally we should use the name of the schema but we don't have it
262
- // fix it when we got the reference name
263
- const root = mediaTypeObject.schema.xml?.name ?? 'object';
264
- return [
265
- {
266
- key: 'default',
267
- example: {
268
- value: {
269
- [root]: generateSchemaExample(mediaTypeObject.schema, {
270
- xml: mediaType === 'application/xml',
271
- mode: 'read',
272
- }),
273
- },
274
- },
275
- },
276
- ];
277
- }
278
- return [
279
- {
280
- key: 'default',
281
- example: {
282
- value: generateSchemaExample(mediaTypeObject.schema, {
283
- mode: 'read',
284
- }),
285
- },
286
- },
287
- ];
288
- }
289
- return [];
290
- }
291
-
292
- /**
293
- * Empty response example.
294
- */
295
- function OpenAPIEmptyResponseExample() {
296
- return (
297
- <pre className="openapi-response-example-empty">
298
- <p>No body</p>
299
- </pre>
300
- );
301
- }
302
-
303
- /**
304
- * Generate an example from a reference object.
305
- */
306
- function getExampleFromReference(ref: OpenAPIV3.ReferenceObject): OpenAPIV3.ExampleObject {
307
- return { summary: 'Unresolved reference', value: { $ref: ref.$ref } };
308
- }
@@ -0,0 +1,123 @@
1
+ 'use client';
2
+
3
+ import clsx from 'clsx';
4
+ import type { Key } from 'react-aria';
5
+ import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
6
+ import { StaticSection } from './StaticSection';
7
+ import { createStateKey, getStatusCodeClassName } from './utils';
8
+
9
+ type OpenAPIResponseExampleItem = OpenAPISelectItem & {
10
+ statusCode: string;
11
+ body: React.ReactNode;
12
+ };
13
+
14
+ /**
15
+ * Get the state of the response examples select.
16
+ */
17
+ export function useResponseExamplesState(
18
+ blockKey: string | undefined,
19
+ initialKey: Key = 'default'
20
+ ) {
21
+ return useSelectState(getResponseExampleStateKey(blockKey), initialKey);
22
+ }
23
+
24
+ export function OpenAPIResponseExampleContent(props: {
25
+ items: OpenAPIResponseExampleItem[];
26
+ blockKey?: string;
27
+ selectIcon?: React.ReactNode;
28
+ }) {
29
+ const { blockKey, items, selectIcon } = props;
30
+
31
+ return (
32
+ <StaticSection
33
+ header={
34
+ <OpenAPIResponseExampleHeader
35
+ selectIcon={selectIcon}
36
+ blockKey={blockKey}
37
+ items={items}
38
+ />
39
+ }
40
+ className="openapi-response-examples"
41
+ >
42
+ <OpenAPIResponseExampleBody blockKey={blockKey} items={items} />
43
+ </StaticSection>
44
+ );
45
+ }
46
+
47
+ function OpenAPIResponseExampleHeader(props: {
48
+ items: OpenAPIResponseExampleItem[];
49
+ blockKey?: string;
50
+ selectIcon?: React.ReactNode;
51
+ }) {
52
+ const { items, blockKey, selectIcon } = props;
53
+
54
+ if (items.length === 1) {
55
+ const item = items[0];
56
+
57
+ if (!item) {
58
+ return null;
59
+ }
60
+
61
+ return (
62
+ <span className="openapi-response-examples-statuscode-title">
63
+ <span
64
+ className={clsx(
65
+ 'openapi-statuscode',
66
+ `openapi-statuscode-${getStatusCodeClassName(item.statusCode)}`,
67
+ 'openapi-response-examples-statuscode'
68
+ )}
69
+ >
70
+ {item.statusCode}
71
+ </span>
72
+ <span>{item.label}</span>
73
+ </span>
74
+ );
75
+ }
76
+
77
+ return (
78
+ <OpenAPISelect
79
+ items={items}
80
+ icon={selectIcon}
81
+ stateKey={getResponseExampleStateKey(blockKey)}
82
+ placement="bottom start"
83
+ >
84
+ {items.map((item) => (
85
+ <OpenAPISelectItem key={item.key} id={item.key} value={item}>
86
+ <span
87
+ className={clsx(
88
+ 'openapi-statuscode',
89
+ `openapi-statuscode-${getStatusCodeClassName(item.statusCode)}`,
90
+ 'openapi-response-examples-statuscode'
91
+ )}
92
+ >
93
+ {item.statusCode}
94
+ </span>
95
+ <span>{item.label}</span>
96
+ </OpenAPISelectItem>
97
+ ))}
98
+ </OpenAPISelect>
99
+ );
100
+ }
101
+
102
+ function OpenAPIResponseExampleBody(props: {
103
+ items: OpenAPIResponseExampleItem[];
104
+ blockKey?: string;
105
+ }) {
106
+ const { blockKey, items } = props;
107
+ const state = useResponseExamplesState(blockKey, items[0]?.key);
108
+
109
+ const selectedItem = items.find((item) => item.key === state.key) ?? items[0];
110
+
111
+ if (!selectedItem) {
112
+ return null;
113
+ }
114
+
115
+ return <div className="openapi-response-examples-panel">{selectedItem.body}</div>;
116
+ }
117
+
118
+ /**
119
+ * Return the state key for the response examples.
120
+ */
121
+ function getResponseExampleStateKey(blockKey: string | undefined) {
122
+ return createStateKey('openapi-responses', blockKey);
123
+ }
@@ -1,9 +1,15 @@
1
+ 'use client';
2
+
1
3
  import type { OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser';
4
+ import clsx from 'clsx';
2
5
  import { Markdown } from './Markdown';
3
6
  import { OpenAPIDisclosureGroup } from './OpenAPIDisclosureGroup';
4
7
  import { OpenAPIResponse } from './OpenAPIResponse';
8
+ import { useResponseExamplesState } from './OpenAPIResponseExampleContent';
5
9
  import { StaticSection } from './StaticSection';
6
- import type { OpenAPIClientContext } from './types';
10
+ import type { OpenAPIClientContext } from './context';
11
+ import { t } from './translate';
12
+ import { createStateKey, getStatusCodeClassName, getStatusCodeDefaultLabel } from './utils';
7
13
 
8
14
  /**
9
15
  * Display an interactive response body.
@@ -14,70 +20,85 @@ export function OpenAPIResponses(props: {
14
20
  }) {
15
21
  const { responses, context } = props;
16
22
 
17
- return (
18
- <StaticSection header="Responses" className="openapi-responses">
19
- <OpenAPIDisclosureGroup
20
- allowsMultipleExpanded
21
- icon={context.icons.chevronRight}
22
- groups={Object.entries(responses).map(
23
- ([statusCode, response]: [string, OpenAPIV3.ResponseObject]) => {
24
- const tabs = (() => {
25
- // If there is no content, but there are headers, we need to show the headers
26
- if (
27
- (!response.content || !Object.keys(response.content).length) &&
28
- response.headers &&
29
- Object.keys(response.headers).length
30
- ) {
31
- return [
32
- {
33
- id: 'default',
34
- body: (
35
- <OpenAPIResponse
36
- response={response}
37
- mediaType={{}}
38
- context={context}
39
- />
40
- ),
41
- },
42
- ];
43
- }
23
+ const groups = Object.entries(responses).map(
24
+ ([statusCode, response]: [string, OpenAPIV3.ResponseObject]) => {
25
+ const tabs = (() => {
26
+ // If there is no content, but there are headers, we need to show the headers
27
+ if (
28
+ (!response.content || !Object.keys(response.content).length) &&
29
+ response.headers &&
30
+ Object.keys(response.headers).length
31
+ ) {
32
+ return [
33
+ {
34
+ key: 'default',
35
+ label: '',
36
+ body: (
37
+ <OpenAPIResponse
38
+ response={response}
39
+ mediaType={{}}
40
+ context={context}
41
+ />
42
+ ),
43
+ },
44
+ ];
45
+ }
44
46
 
45
- return Object.entries(response.content ?? {}).map(
46
- ([contentType, mediaType]) => ({
47
- id: contentType,
48
- label: contentType,
49
- body: (
50
- <OpenAPIResponse
51
- response={response}
52
- mediaType={mediaType}
53
- context={context}
54
- />
55
- ),
56
- })
57
- );
58
- })();
47
+ return Object.entries(response.content ?? {}).map(([contentType, mediaType]) => ({
48
+ key: contentType,
49
+ label: contentType,
50
+ body: (
51
+ <OpenAPIResponse
52
+ response={response}
53
+ mediaType={mediaType}
54
+ context={context}
55
+ />
56
+ ),
57
+ }));
58
+ })();
59
59
 
60
- const description = response.description;
60
+ const description = response.description;
61
61
 
62
- return {
63
- id: statusCode,
64
- label: (
65
- <div className="openapi-response-tab-content">
66
- <span className="openapi-response-statuscode">
67
- {statusCode}
68
- </span>
69
- {description ? (
70
- <Markdown
71
- source={description}
72
- className="openapi-response-description"
73
- />
74
- ) : null}
75
- </div>
76
- ),
77
- tabs,
78
- };
79
- }
80
- )}
62
+ return {
63
+ key: statusCode,
64
+ label: (
65
+ <div className="openapi-response-tab-content">
66
+ <span
67
+ className={clsx(
68
+ 'openapi-statuscode',
69
+ `openapi-statuscode-${getStatusCodeClassName(statusCode)}`
70
+ )}
71
+ >
72
+ {statusCode}
73
+ </span>
74
+ {description ? (
75
+ <Markdown
76
+ source={description}
77
+ className="openapi-response-description"
78
+ />
79
+ ) : (
80
+ getStatusCodeDefaultLabel(statusCode, context)
81
+ )}
82
+ </div>
83
+ ),
84
+ tabs,
85
+ };
86
+ }
87
+ );
88
+
89
+ const state = useResponseExamplesState(context.blockKey, groups[0]?.key);
90
+
91
+ return (
92
+ <StaticSection header={t(context.translation, 'responses')} className="openapi-responses">
93
+ <OpenAPIDisclosureGroup
94
+ icon={context.icons.chevronRight}
95
+ expandedKeys={state.key ? new Set([state.key]) : new Set()}
96
+ onExpandedChange={(keys) => {
97
+ const key = keys.values().next().value ?? null;
98
+ state.setKey(key);
99
+ }}
100
+ groups={groups}
101
+ selectStateKey={createStateKey('response-media-types', context.blockKey)}
81
102
  />
82
103
  </StaticSection>
83
104
  );