@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
@@ -35,6 +35,86 @@ describe('getSchemaAlternatives', () => {
35
35
  ]);
36
36
  });
37
37
 
38
+ it('merges string enum', () => {
39
+ expect(
40
+ getSchemaAlternatives({
41
+ oneOf: [
42
+ {
43
+ oneOf: [
44
+ {
45
+ type: 'string',
46
+ enum: ['a', 'b'],
47
+ },
48
+ {
49
+ type: 'string',
50
+ enum: ['c', 'd'],
51
+ nullable: true,
52
+ },
53
+ ],
54
+ },
55
+ ],
56
+ })
57
+ ).toEqual([
58
+ {
59
+ type: 'string',
60
+ enum: ['a', 'b', 'c', 'd'],
61
+ nullable: true,
62
+ },
63
+ ]);
64
+ });
65
+
66
+ it('merges objects with allOf', () => {
67
+ expect(
68
+ getSchemaAlternatives({
69
+ allOf: [
70
+ {
71
+ type: 'object',
72
+ properties: {
73
+ name: {
74
+ type: 'string',
75
+ },
76
+ map: {
77
+ type: 'string',
78
+ },
79
+ description: {
80
+ type: 'string',
81
+ },
82
+ },
83
+ required: ['name'],
84
+ },
85
+ {
86
+ type: 'object',
87
+ properties: {
88
+ externalId: {
89
+ type: 'string',
90
+ },
91
+ },
92
+ required: ['map', 'externalId'],
93
+ },
94
+ ],
95
+ })
96
+ ).toEqual([
97
+ {
98
+ type: 'object',
99
+ properties: {
100
+ name: {
101
+ type: 'string',
102
+ },
103
+ map: {
104
+ type: 'string',
105
+ },
106
+ description: {
107
+ type: 'string',
108
+ },
109
+ externalId: {
110
+ type: 'string',
111
+ },
112
+ },
113
+ required: ['name', 'map', 'externalId'],
114
+ },
115
+ ]);
116
+ });
117
+
38
118
  it('should not flatten oneOf and allOf', () => {
39
119
  expect(
40
120
  getSchemaAlternatives({
@@ -10,8 +10,10 @@ import { Markdown } from './Markdown';
10
10
  import { OpenAPICopyButton } from './OpenAPICopyButton';
11
11
  import { OpenAPIDisclosure } from './OpenAPIDisclosure';
12
12
  import { OpenAPISchemaName } from './OpenAPISchemaName';
13
+ import type { OpenAPIClientContext } from './context';
13
14
  import { retrocycle } from './decycle';
14
- import type { OpenAPIClientContext } from './types';
15
+ import { stringifyOpenAPI } from './stringifyOpenAPI';
16
+ import { tString } from './translate';
15
17
  import { checkIsReference, resolveDescription, resolveFirstExample } from './utils';
16
18
 
17
19
  type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
@@ -39,7 +41,7 @@ function OpenAPISchemaProperty(props: {
39
41
 
40
42
  return (
41
43
  <div id={id} className={clsx('openapi-schema', className)}>
42
- <OpenAPISchemaPresentation property={property} />
44
+ <OpenAPISchemaPresentation context={context} property={property} />
43
45
  {(() => {
44
46
  const circularRefId = parentCircularRefs.get(schema);
45
47
  // Avoid recursing infinitely, and instead render a link to the parent schema
@@ -53,7 +55,12 @@ function OpenAPISchemaProperty(props: {
53
55
  const properties = getSchemaProperties(schema);
54
56
  if (properties?.length) {
55
57
  return (
56
- <OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
58
+ <OpenAPIDisclosure
59
+ icon={context.icons.plus}
60
+ label={(isExpanded) =>
61
+ getDisclosureLabel({ schema, isExpanded, context })
62
+ }
63
+ >
57
64
  <OpenAPISchemaProperties
58
65
  properties={properties}
59
66
  circularRefs={circularRefs}
@@ -145,17 +152,23 @@ function OpenAPIRootSchema(props: {
145
152
 
146
153
  const id = useId();
147
154
  const properties = getSchemaProperties(schema);
155
+ const description = resolveDescription(schema);
148
156
 
149
157
  if (properties?.length) {
150
158
  const circularRefs = new Map(parentCircularRefs);
151
159
  circularRefs.set(schema, id);
152
160
 
153
161
  return (
154
- <OpenAPISchemaProperties
155
- properties={properties}
156
- circularRefs={circularRefs}
157
- context={context}
158
- />
162
+ <>
163
+ {description ? (
164
+ <Markdown source={description} className="openapi-schema-root-description" />
165
+ ) : null}
166
+ <OpenAPISchemaProperties
167
+ properties={properties}
168
+ circularRefs={circularRefs}
169
+ context={context}
170
+ />
171
+ </>
159
172
  );
160
173
  }
161
174
 
@@ -201,7 +214,10 @@ function OpenAPISchemaAlternative(props: {
201
214
  {description ? (
202
215
  <Markdown source={description} className="openapi-schema-description" />
203
216
  ) : null}
204
- <OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
217
+ <OpenAPIDisclosure
218
+ icon={context.icons.plus}
219
+ label={(isExpanded) => getDisclosureLabel({ schema, isExpanded, context })}
220
+ >
205
221
  {properties?.length ? (
206
222
  <OpenAPISchemaProperties
207
223
  properties={properties}
@@ -239,8 +255,9 @@ function OpenAPISchemaCircularRef(props: { id: string; schema: OpenAPIV3.SchemaO
239
255
  */
240
256
  function OpenAPISchemaEnum(props: {
241
257
  schema: OpenAPIV3.SchemaObject & OpenAPICustomOperationProperties;
258
+ context: OpenAPIClientContext;
242
259
  }) {
243
- const { schema } = props;
260
+ const { schema, context } = props;
244
261
 
245
262
  const enumValues = (() => {
246
263
  // Render x-gitbook-enum first, as it has a different format
@@ -283,6 +300,7 @@ function OpenAPISchemaEnum(props: {
283
300
  value={item.value}
284
301
  label={item.description}
285
302
  withTooltip={!!item.description}
303
+ context={context}
286
304
  >
287
305
  <code>{`${item.value}`}</code>
288
306
  </OpenAPICopyButton>
@@ -295,9 +313,13 @@ function OpenAPISchemaEnum(props: {
295
313
  /**
296
314
  * Render the top row of a schema. e.g: name, type, and required status.
297
315
  */
298
- function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry }) {
316
+ function OpenAPISchemaPresentation(props: {
317
+ property: OpenAPISchemaPropertyEntry;
318
+ context: OpenAPIClientContext;
319
+ }) {
299
320
  const {
300
321
  property: { schema, propertyName, required },
322
+ context,
301
323
  } = props;
302
324
 
303
325
  const description = resolveDescription(schema);
@@ -310,6 +332,7 @@ function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry
310
332
  type={getSchemaTitle(schema)}
311
333
  propertyName={propertyName}
312
334
  required={required}
335
+ context={context}
313
336
  />
314
337
  {typeof schema['x-deprecated-sunset'] === 'string' ? (
315
338
  <div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
@@ -322,17 +345,27 @@ function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry
322
345
  {description ? (
323
346
  <Markdown source={description} className="openapi-schema-description" />
324
347
  ) : null}
348
+ {schema.default !== undefined ? (
349
+ <span className="openapi-schema-default">
350
+ Default:{' '}
351
+ <code>
352
+ {typeof schema.default === 'string' && schema.default
353
+ ? schema.default
354
+ : stringifyOpenAPI(schema.default)}
355
+ </code>
356
+ </span>
357
+ ) : null}
325
358
  {typeof example === 'string' ? (
326
- <div className="openapi-schema-example">
359
+ <span className="openapi-schema-example">
327
360
  Example: <code>{example}</code>
328
- </div>
361
+ </span>
329
362
  ) : null}
330
363
  {schema.pattern ? (
331
- <div className="openapi-schema-pattern">
364
+ <span className="openapi-schema-pattern">
332
365
  Pattern: <code>{schema.pattern}</code>
333
- </div>
366
+ </span>
334
367
  ) : null}
335
- <OpenAPISchemaEnum schema={schema} />
368
+ <OpenAPISchemaEnum schema={schema} context={context} />
336
369
  </div>
337
370
  );
338
371
  }
@@ -427,7 +460,91 @@ export function getSchemaAlternatives(
427
460
  }
428
461
 
429
462
  const [type, schemas] = alternatives;
430
- return flattenAlternatives(type, schemas, new Set(ancestors).add(schema));
463
+ return mergeAlternatives(
464
+ type,
465
+ flattenAlternatives(type, schemas, new Set(ancestors).add(schema))
466
+ );
467
+ }
468
+
469
+ /**
470
+ * Merge alternatives of the same type into a single schema.
471
+ * - Merge string enums
472
+ */
473
+ function mergeAlternatives(
474
+ alternativeType: AlternativeType,
475
+ schemasOrRefs: OpenAPIV3.SchemaObject[]
476
+ ): OpenAPIV3.SchemaObject[] | null {
477
+ switch (alternativeType) {
478
+ case 'oneOf': {
479
+ return schemasOrRefs.reduce<OpenAPIV3.SchemaObject[]>((acc, schemaOrRef) => {
480
+ const latest = acc.at(-1);
481
+
482
+ if (
483
+ latest &&
484
+ latest.type === 'string' &&
485
+ latest.enum &&
486
+ schemaOrRef.type === 'string' &&
487
+ schemaOrRef.enum
488
+ ) {
489
+ latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
490
+ latest.nullable = latest.nullable || schemaOrRef.nullable;
491
+ return acc;
492
+ }
493
+
494
+ acc.push(schemaOrRef);
495
+ return acc;
496
+ }, []);
497
+ }
498
+ case 'allOf': {
499
+ return schemasOrRefs.reduce<OpenAPIV3.SchemaObject[]>((acc, schemaOrRef) => {
500
+ const latest = acc.at(-1);
501
+
502
+ if (
503
+ latest &&
504
+ latest.type === 'string' &&
505
+ latest.enum &&
506
+ schemaOrRef.type === 'string' &&
507
+ schemaOrRef.enum
508
+ ) {
509
+ const keys = Object.keys(schemaOrRef);
510
+ if (keys.every((key) => ['type', 'enum', 'nullable'].includes(key))) {
511
+ latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
512
+ latest.nullable = latest.nullable || schemaOrRef.nullable;
513
+ return acc;
514
+ }
515
+ }
516
+
517
+ if (latest && latest.type === 'object' && schemaOrRef.type === 'object') {
518
+ const keys = Object.keys(schemaOrRef);
519
+ if (
520
+ keys.every((key) =>
521
+ ['type', 'properties', 'required', 'nullable'].includes(key)
522
+ )
523
+ ) {
524
+ latest.properties = {
525
+ ...latest.properties,
526
+ ...schemaOrRef.properties,
527
+ };
528
+ latest.required = Array.from(
529
+ new Set([
530
+ ...(Array.isArray(latest.required) ? latest.required : []),
531
+ ...(Array.isArray(schemaOrRef.required)
532
+ ? schemaOrRef.required
533
+ : []),
534
+ ])
535
+ );
536
+ latest.nullable = latest.nullable || schemaOrRef.nullable;
537
+ return acc;
538
+ }
539
+ }
540
+
541
+ acc.push(schemaOrRef);
542
+ return acc;
543
+ }, []);
544
+ }
545
+ default:
546
+ return schemasOrRefs;
547
+ }
431
548
  }
432
549
 
433
550
  function flattenAlternatives(
@@ -485,19 +602,26 @@ function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string {
485
602
  return type;
486
603
  }
487
604
 
488
- function getDisclosureLabel(schema: OpenAPIV3.SchemaObject): string {
605
+ function getDisclosureLabel(props: {
606
+ schema: OpenAPIV3.SchemaObject;
607
+ isExpanded: boolean;
608
+ context: OpenAPIClientContext;
609
+ }) {
610
+ const { schema, isExpanded, context } = props;
611
+ let label: string;
489
612
  if (schema.type === 'array' && !!schema.items) {
490
613
  if (schema.items.oneOf) {
491
- return 'available items';
614
+ label = tString(context.translation, 'available_items').toLowerCase();
492
615
  }
493
-
494
616
  // Fallback to "child attributes" for enums and objects
495
- if (schema.items.enum || schema.items.type === 'object') {
496
- return 'child attributes';
617
+ else if (schema.items.enum || schema.items.type === 'object') {
618
+ label = tString(context.translation, 'child_attributes').toLowerCase();
619
+ } else {
620
+ label = schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
497
621
  }
498
-
499
- return schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
622
+ } else {
623
+ label = schema.title || tString(context.translation, 'child_attributes').toLowerCase();
500
624
  }
501
625
 
502
- return schema.title || 'child attributes';
626
+ return `${isExpanded ? tString(context.translation, 'hide') : tString(context.translation, 'show')} ${label}`;
503
627
  }
@@ -1,12 +1,14 @@
1
1
  import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
2
  import type React from 'react';
3
- import { stringifyOpenAPI } from './stringifyOpenAPI';
3
+ import type { OpenAPIClientContext } from './context';
4
+ import { t, tString } from './translate';
4
5
 
5
6
  interface OpenAPISchemaNameProps {
6
7
  schema?: OpenAPIV3.SchemaObject;
7
8
  propertyName?: string | React.JSX.Element;
8
9
  required?: boolean;
9
10
  type?: string;
11
+ context: OpenAPIClientContext;
10
12
  }
11
13
 
12
14
  /**
@@ -14,12 +16,12 @@ interface OpenAPISchemaNameProps {
14
16
  * It includes the property name, type, required and deprecated status.
15
17
  */
16
18
  export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
17
- const { schema, type, propertyName, required } = props;
19
+ const { schema, type, propertyName, required, context } = props;
18
20
 
19
- const additionalItems = schema && getAdditionalItems(schema);
21
+ const additionalItems = schema && getAdditionalItems(schema, context);
20
22
 
21
23
  return (
22
- <div className="openapi-schema-name">
24
+ <span className="openapi-schema-name">
23
25
  {propertyName ? (
24
26
  <span data-deprecated={schema?.deprecated} className="openapi-schema-propertyname">
25
27
  {propertyName}
@@ -31,38 +33,45 @@ export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
31
33
  <span className="openapi-schema-type">{additionalItems}</span>
32
34
  ) : null}
33
35
  </span>
34
- {schema?.readOnly ? <span className="openapi-schema-readonly">read-only</span> : null}
36
+ {schema?.readOnly ? (
37
+ <span className="openapi-schema-readonly">
38
+ {t(context.translation, 'read_only')}
39
+ </span>
40
+ ) : null}
35
41
  {schema?.writeOnly ? (
36
- <span className="openapi-schema-writeonly">write-only</span>
42
+ <span className="openapi-schema-writeonly">
43
+ {t(context.translation, 'write_only')}
44
+ </span>
37
45
  ) : null}
38
46
  {required ? (
39
- <span className="openapi-schema-required">required</span>
47
+ <span className="openapi-schema-required">
48
+ {t(context.translation, 'required')}
49
+ </span>
40
50
  ) : (
41
- <span className="openapi-schema-optional">optional</span>
51
+ <span className="openapi-schema-optional">
52
+ {t(context.translation, 'optional')}
53
+ </span>
42
54
  )}
43
- {schema?.deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
44
- </div>
55
+ {schema?.deprecated ? (
56
+ <span className="openapi-deprecated">{t(context.translation, 'deprecated')}</span>
57
+ ) : null}
58
+ </span>
45
59
  );
46
60
  }
47
61
 
48
- function getAdditionalItems(schema: OpenAPIV3.SchemaObject): string {
62
+ function getAdditionalItems(schema: OpenAPIV3.SchemaObject, context: OpenAPIClientContext): string {
49
63
  let additionalItems = '';
50
64
 
51
65
  if (schema.minimum || schema.minLength || schema.minItems) {
52
- additionalItems += ` · min: ${schema.minimum || schema.minLength || schema.minItems}`;
66
+ additionalItems += ` · ${tString(context.translation, 'min').toLowerCase()}: ${schema.minimum || schema.minLength || schema.minItems}`;
53
67
  }
54
68
 
55
69
  if (schema.maximum || schema.maxLength || schema.maxItems) {
56
- additionalItems += ` · max: ${schema.maximum || schema.maxLength || schema.maxItems}`;
57
- }
58
-
59
- // If the schema has a default value, we display it
60
- if (typeof schema.default !== 'undefined') {
61
- additionalItems += ` · default: ${stringifyOpenAPI(schema.default)}`;
70
+ additionalItems += ` · ${tString(context.translation, 'max').toLowerCase()}: ${schema.maximum || schema.maxLength || schema.maxItems}`;
62
71
  }
63
72
 
64
73
  if (schema.nullable) {
65
- additionalItems = ' | nullable';
74
+ additionalItems = ` | ${tString(context.translation, 'nullable').toLowerCase()}`;
66
75
  }
67
76
 
68
77
  return additionalItems;
@@ -4,8 +4,8 @@ import {
4
4
  OpenAPISchemaPropertiesFromServer,
5
5
  type OpenAPISchemaPropertyEntry,
6
6
  } from './OpenAPISchema';
7
+ import type { OpenAPIClientContext } from './context';
7
8
  import { decycle } from './decycle';
8
- import type { OpenAPIClientContext } from './types';
9
9
 
10
10
  export function OpenAPISchemaProperties(props: {
11
11
  id?: string;
@@ -1,9 +1,10 @@
1
- import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
2
1
  import { InteractiveSection } from './InteractiveSection';
3
2
  import { Markdown } from './Markdown';
4
3
  import { OpenAPISchemaName } from './OpenAPISchemaName';
5
- import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
6
- import { resolveDescription } from './utils';
4
+ import type { OpenAPIClientContext } from './context';
5
+ import { t } from './translate';
6
+ import type { OpenAPIOperationData, OpenAPISecurityWithRequired } from './types';
7
+ import { createStateKey, resolveDescription } from './utils';
7
8
 
8
9
  /**
9
10
  * Present securities authorization that can be used for this operation.
@@ -20,10 +21,12 @@ export function OpenAPISecurities(props: {
20
21
 
21
22
  return (
22
23
  <InteractiveSection
23
- header="Authorizations"
24
+ header={t(context.translation, 'authorizations')}
25
+ stateKey={createStateKey('securities', context.blockKey)}
24
26
  toggeable
25
27
  defaultOpened={false}
26
28
  toggleIcon={context.icons.chevronRight}
29
+ selectIcon={context.icons.chevronDown}
27
30
  className="openapi-securities"
28
31
  tabs={securities.map(([key, security]) => {
29
32
  const description = resolveDescription(security);
@@ -33,7 +36,7 @@ export function OpenAPISecurities(props: {
33
36
  body: (
34
37
  <div className="openapi-schema">
35
38
  <div className="openapi-schema-presentation">
36
- {getLabelForType(security)}
39
+ {getLabelForType(security, context)}
37
40
 
38
41
  {description ? (
39
42
  <Markdown
@@ -50,26 +53,39 @@ export function OpenAPISecurities(props: {
50
53
  );
51
54
  }
52
55
 
53
- function getLabelForType(security: OpenAPIV3_1.SecuritySchemeObject) {
56
+ function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPIClientContext) {
54
57
  switch (security.type) {
55
58
  case 'apiKey':
56
59
  return (
57
60
  <OpenAPISchemaName
61
+ context={context}
58
62
  propertyName={security.name ?? 'apiKey'}
59
63
  type="string"
60
- required
64
+ required={security.required}
61
65
  />
62
66
  );
63
67
  case 'http':
64
68
  if (security.scheme === 'basic') {
65
- return <OpenAPISchemaName propertyName="Authorization" type="string" required />;
69
+ return (
70
+ <OpenAPISchemaName
71
+ context={context}
72
+ propertyName="Authorization"
73
+ type="string"
74
+ required={security.required}
75
+ />
76
+ );
66
77
  }
67
78
 
68
79
  if (security.scheme === 'bearer') {
69
80
  const description = resolveDescription(security);
70
81
  return (
71
82
  <>
72
- <OpenAPISchemaName propertyName="Authorization" type="string" required />
83
+ <OpenAPISchemaName
84
+ context={context}
85
+ propertyName="Authorization"
86
+ type="string"
87
+ required={security.required}
88
+ />
73
89
  {/** Show a default description if none is provided */}
74
90
  {!description ? (
75
91
  <Markdown
@@ -81,11 +97,29 @@ function getLabelForType(security: OpenAPIV3_1.SecuritySchemeObject) {
81
97
  );
82
98
  }
83
99
 
84
- return <OpenAPISchemaName propertyName="HTTP" required />;
100
+ return (
101
+ <OpenAPISchemaName
102
+ context={context}
103
+ propertyName="HTTP"
104
+ required={security.required}
105
+ />
106
+ );
85
107
  case 'oauth2':
86
- return <OpenAPISchemaName propertyName="OAuth2" required />;
108
+ return (
109
+ <OpenAPISchemaName
110
+ context={context}
111
+ propertyName="OAuth2"
112
+ required={security.required}
113
+ />
114
+ );
87
115
  case 'openIdConnect':
88
- return <OpenAPISchemaName propertyName="OpenID Connect" required />;
116
+ return (
117
+ <OpenAPISchemaName
118
+ context={context}
119
+ propertyName="OpenID Connect"
120
+ required={security.required}
121
+ />
122
+ );
89
123
  default:
90
124
  // @ts-ignore
91
125
  return security.type;
@@ -0,0 +1,96 @@
1
+ 'use client';
2
+
3
+ import clsx from 'clsx';
4
+ import { useCallback } from 'react';
5
+ import {
6
+ Button,
7
+ type Key,
8
+ ListBox,
9
+ ListBoxItem,
10
+ type ListBoxItemProps,
11
+ Popover,
12
+ type PopoverProps,
13
+ Select,
14
+ type SelectProps,
15
+ SelectValue,
16
+ } from 'react-aria-components';
17
+ import { useStore } from 'zustand';
18
+ import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
19
+
20
+ export type OpenAPISelectItem = {
21
+ key: Key;
22
+ label: string | React.ReactNode;
23
+ };
24
+
25
+ interface OpenAPISelectProps<T extends OpenAPISelectItem> extends Omit<SelectProps<T>, 'children'> {
26
+ items: T[];
27
+ children: React.ReactNode | ((item: T) => React.ReactNode);
28
+ placement?: PopoverProps['placement'];
29
+ stateKey?: string;
30
+ /**
31
+ * Icon to display in the select button.
32
+ */
33
+ icon?: React.ReactNode;
34
+ }
35
+
36
+ export function useSelectState(stateKey = 'select-state', initialKey?: Key) {
37
+ const store = useStore(getOrCreateStoreByKey(stateKey, initialKey));
38
+ return {
39
+ key: store.key,
40
+ setKey: useCallback((key: Key | null) => store.setKey(key), [store.setKey]),
41
+ };
42
+ }
43
+
44
+ export function OpenAPISelect<T extends OpenAPISelectItem>(props: OpenAPISelectProps<T>) {
45
+ const {
46
+ icon = '▼',
47
+ items,
48
+ children,
49
+ className,
50
+ placement,
51
+ stateKey,
52
+ selectedKey,
53
+ onSelectionChange,
54
+ } = props;
55
+
56
+ const state = useSelectState(stateKey, items[0]?.key);
57
+
58
+ const selected = items.find((item) => item.key === state.key) || items[0];
59
+
60
+ return (
61
+ <Select
62
+ aria-label="OpenAPI Select"
63
+ {...props}
64
+ selectedKey={selectedKey || selected?.key}
65
+ onSelectionChange={(key) => {
66
+ onSelectionChange?.(key);
67
+ state.setKey(key);
68
+ }}
69
+ className={clsx('openapi-select', className)}
70
+ >
71
+ <Button>
72
+ <SelectValue />
73
+ {icon}
74
+ </Button>
75
+ <Popover placement={placement} className="openapi-select-popover">
76
+ <ListBox className="openapi-select-listbox" items={items}>
77
+ {children}
78
+ </ListBox>
79
+ </Popover>
80
+ </Select>
81
+ );
82
+ }
83
+
84
+ export function OpenAPISelectItem(props: ListBoxItemProps) {
85
+ return (
86
+ <ListBoxItem
87
+ {...props}
88
+ className={({ isFocused, isSelected }) =>
89
+ clsx('openapi-select-item', {
90
+ 'openapi-select-item-focused': isFocused,
91
+ 'openapi-select-item-selected': isSelected,
92
+ })
93
+ }
94
+ />
95
+ );
96
+ }