@gitbook/react-openapi 1.0.2 → 1.0.4
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.
- package/CHANGELOG.md +24 -0
- package/dist/OpenAPICodeSample.jsx +17 -16
- package/dist/OpenAPIDisclosure.d.ts +2 -1
- package/dist/OpenAPIDisclosure.jsx +1 -1
- package/dist/OpenAPIDisclosureGroup.d.ts +1 -1
- package/dist/OpenAPIDisclosureGroup.jsx +2 -2
- package/dist/OpenAPIOperation.jsx +21 -7
- package/dist/OpenAPIPath.d.ts +3 -2
- package/dist/OpenAPIPath.jsx +4 -15
- package/dist/OpenAPIRequestBody.jsx +1 -1
- package/dist/OpenAPIResponse.jsx +1 -1
- package/dist/OpenAPIResponseExample.jsx +6 -5
- package/dist/OpenAPIResponses.d.ts +1 -1
- package/dist/OpenAPIResponses.jsx +2 -2
- package/dist/OpenAPISchema.d.ts +5 -1
- package/dist/OpenAPISchema.jsx +72 -61
- package/dist/OpenAPISchemaName.d.ts +5 -3
- package/dist/OpenAPISchemaName.jsx +25 -4
- package/dist/OpenAPISecurities.jsx +2 -2
- package/dist/OpenAPITabs.d.ts +3 -3
- package/dist/OpenAPITabs.jsx +17 -14
- package/dist/ScalarApiButton.jsx +1 -1
- package/dist/code-samples.js +239 -17
- package/dist/contentTypeChecks.d.ts +9 -0
- package/dist/contentTypeChecks.js +27 -0
- package/dist/generateSchemaExample.js +2 -1
- package/dist/resolveOpenAPIOperation.d.ts +3 -3
- package/dist/resolveOpenAPIOperation.js +1 -1
- package/dist/stringifyOpenAPI.d.ts +1 -1
- package/dist/stringifyOpenAPI.js +8 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +14 -2
- package/dist/util/server.d.ts +9 -0
- package/dist/util/server.js +44 -0
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +7 -6
- package/package.json +3 -8
- package/src/InteractiveSection.tsx +4 -4
- package/src/OpenAPICodeSample.tsx +20 -19
- package/src/OpenAPIDisclosure.tsx +4 -3
- package/src/OpenAPIDisclosureGroup.tsx +5 -5
- package/src/OpenAPIOperation.tsx +32 -10
- package/src/OpenAPIOperationContext.tsx +1 -1
- package/src/OpenAPIPath.tsx +11 -10
- package/src/OpenAPIRequestBody.tsx +2 -2
- package/src/OpenAPIResponse.tsx +3 -3
- package/src/OpenAPIResponseExample.tsx +7 -6
- package/src/OpenAPIResponses.tsx +4 -4
- package/src/OpenAPISchema.test.ts +5 -5
- package/src/OpenAPISchema.tsx +134 -73
- package/src/OpenAPISchemaName.tsx +40 -7
- package/src/OpenAPISecurities.tsx +3 -3
- package/src/OpenAPITabs.tsx +23 -17
- package/src/ScalarApiButton.tsx +3 -3
- package/src/code-samples.test.ts +594 -2
- package/src/code-samples.ts +238 -17
- package/src/contentTypeChecks.ts +35 -0
- package/src/generateSchemaExample.ts +22 -18
- package/src/json2xml.test.ts +1 -1
- package/src/resolveOpenAPIOperation.test.ts +6 -6
- package/src/resolveOpenAPIOperation.ts +7 -7
- package/src/stringifyOpenAPI.ts +13 -2
- package/src/types.ts +11 -1
- package/src/util/server.test.ts +58 -0
- package/src/util/server.ts +47 -0
- package/src/utils.ts +9 -5
- package/dist/OpenAPIServerURL.d.ts +0 -11
- package/dist/OpenAPIServerURL.jsx +0 -67
- package/dist/OpenAPIServerURLVariable.d.ts +0 -8
- package/dist/OpenAPIServerURLVariable.jsx +0 -8
- package/src/OpenAPIServerURL.tsx +0 -73
- package/src/OpenAPIServerURLVariable.tsx +0 -14
package/src/OpenAPISchema.tsx
CHANGED
|
@@ -4,11 +4,11 @@ import { useId } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import { InteractiveSection } from './InteractiveSection';
|
|
6
6
|
import { Markdown } from './Markdown';
|
|
7
|
+
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
|
|
8
|
+
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
|
9
|
+
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
7
10
|
import type { OpenAPIClientContext } from './types';
|
|
8
11
|
import { checkIsReference, resolveDescription } from './utils';
|
|
9
|
-
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
10
|
-
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
|
11
|
-
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
|
|
12
12
|
|
|
13
13
|
type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
|
|
14
14
|
|
|
@@ -27,7 +27,7 @@ export function OpenAPISchemaProperty(
|
|
|
27
27
|
circularRefs?: CircularRefsIds;
|
|
28
28
|
context: OpenAPIClientContext;
|
|
29
29
|
className?: string;
|
|
30
|
-
}
|
|
30
|
+
}
|
|
31
31
|
) {
|
|
32
32
|
const {
|
|
33
33
|
schema,
|
|
@@ -47,12 +47,24 @@ export function OpenAPISchemaProperty(
|
|
|
47
47
|
? null
|
|
48
48
|
: getSchemaAlternatives(schema, new Set(circularRefs.keys()));
|
|
49
49
|
|
|
50
|
+
if (alternatives?.[0]?.length) {
|
|
51
|
+
return (
|
|
52
|
+
<OpenAPISchemaAlternativesItem
|
|
53
|
+
{...props}
|
|
54
|
+
circularRefs={circularRefs}
|
|
55
|
+
context={context}
|
|
56
|
+
alternatives={alternatives}
|
|
57
|
+
parentCircularRef={parentCircularRef}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
50
62
|
if ((properties && properties.length > 0) || schema.type === 'object') {
|
|
51
63
|
return (
|
|
52
64
|
<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
|
|
53
65
|
<OpenAPISchemaPresentation {...props} />
|
|
54
66
|
{properties && properties.length > 0 ? (
|
|
55
|
-
<OpenAPIDisclosure context={context}>
|
|
67
|
+
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
|
56
68
|
<OpenAPISchemaProperties
|
|
57
69
|
properties={properties}
|
|
58
70
|
circularRefs={circularRefs}
|
|
@@ -60,22 +72,9 @@ export function OpenAPISchemaProperty(
|
|
|
60
72
|
/>
|
|
61
73
|
</OpenAPIDisclosure>
|
|
62
74
|
) : null}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (alternatives?.[0]?.length) {
|
|
68
|
-
return (
|
|
69
|
-
<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
|
|
70
|
-
<OpenAPISchemaPresentation {...props} />
|
|
71
|
-
{alternatives[0].map((alternative, index) => (
|
|
72
|
-
<OpenAPISchemaAlternative
|
|
73
|
-
key={`alternative-${index}`}
|
|
74
|
-
schema={alternative}
|
|
75
|
-
circularRefs={circularRefs}
|
|
76
|
-
context={context}
|
|
77
|
-
/>
|
|
78
|
-
))}
|
|
75
|
+
{parentCircularRef ? (
|
|
76
|
+
<OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />
|
|
77
|
+
) : null}
|
|
79
78
|
</InteractiveSection>
|
|
80
79
|
);
|
|
81
80
|
}
|
|
@@ -166,16 +165,72 @@ function OpenAPISchemaAlternative(props: {
|
|
|
166
165
|
const { schema, circularRefs, context } = props;
|
|
167
166
|
const id = useId();
|
|
168
167
|
const subProperties = getSchemaProperties(schema);
|
|
168
|
+
const description = resolveDescription(schema);
|
|
169
|
+
const alternatives = getSchemaAlternatives(schema, new Set(circularRefs?.keys()));
|
|
170
|
+
|
|
171
|
+
if (alternatives?.[0]?.length && !subProperties?.length) {
|
|
172
|
+
return (
|
|
173
|
+
<>
|
|
174
|
+
{description ? (
|
|
175
|
+
<Markdown source={description} className="openapi-schema-description" />
|
|
176
|
+
) : null}
|
|
177
|
+
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
|
178
|
+
<OpenAPISchemaAlternativesItem
|
|
179
|
+
schema={schema}
|
|
180
|
+
circularRefs={circularRefs}
|
|
181
|
+
context={context}
|
|
182
|
+
alternatives={alternatives}
|
|
183
|
+
/>
|
|
184
|
+
</OpenAPIDisclosure>
|
|
185
|
+
</>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
169
188
|
|
|
170
189
|
return (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
190
|
+
<>
|
|
191
|
+
{description ? (
|
|
192
|
+
<Markdown source={description} className="openapi-schema-description" />
|
|
193
|
+
) : null}
|
|
194
|
+
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
|
195
|
+
<OpenAPISchemaProperties
|
|
196
|
+
id={id}
|
|
197
|
+
properties={subProperties ?? [{ schema }]}
|
|
198
|
+
circularRefs={
|
|
199
|
+
subProperties ? new Map(circularRefs).set(schema, id) : circularRefs
|
|
200
|
+
}
|
|
201
|
+
context={context}
|
|
202
|
+
/>
|
|
203
|
+
</OpenAPIDisclosure>
|
|
204
|
+
</>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function OpenAPISchemaAlternativesItem(
|
|
209
|
+
props: OpenAPISchemaPropertyEntry & {
|
|
210
|
+
circularRefs?: CircularRefsIds;
|
|
211
|
+
context: OpenAPIClientContext;
|
|
212
|
+
alternatives: OpenAPISchemaAlternatives;
|
|
213
|
+
parentCircularRef?: string;
|
|
214
|
+
}
|
|
215
|
+
) {
|
|
216
|
+
const id = useId();
|
|
217
|
+
const { schema, circularRefs, context, alternatives, parentCircularRef } = props;
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<InteractiveSection id={id} className={clsx('openapi-schema')}>
|
|
221
|
+
<OpenAPISchemaPresentation {...props} />
|
|
222
|
+
{alternatives[0].map((alternative, index) => (
|
|
223
|
+
<OpenAPISchemaAlternative
|
|
224
|
+
key={`alternative-${index}`}
|
|
225
|
+
schema={alternative}
|
|
226
|
+
circularRefs={circularRefs}
|
|
227
|
+
context={context}
|
|
228
|
+
/>
|
|
229
|
+
))}
|
|
230
|
+
{parentCircularRef ? (
|
|
231
|
+
<OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />
|
|
232
|
+
) : null}
|
|
233
|
+
</InteractiveSection>
|
|
179
234
|
);
|
|
180
235
|
}
|
|
181
236
|
|
|
@@ -219,7 +274,7 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
|
|
219
274
|
|
|
220
275
|
const shouldDisplayExample = (schema: OpenAPIV3.SchemaObject): boolean => {
|
|
221
276
|
return (
|
|
222
|
-
typeof schema.example === 'string' ||
|
|
277
|
+
(typeof schema.example === 'string' && !!schema.example) ||
|
|
223
278
|
typeof schema.example === 'number' ||
|
|
224
279
|
typeof schema.example === 'boolean' ||
|
|
225
280
|
(Array.isArray(schema.example) && schema.example.length > 0) ||
|
|
@@ -234,10 +289,10 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
|
|
234
289
|
return (
|
|
235
290
|
<div className="openapi-schema-presentation">
|
|
236
291
|
<OpenAPISchemaName
|
|
292
|
+
schema={schema}
|
|
237
293
|
type={getSchemaTitle(schema)}
|
|
238
294
|
propertyName={propertyName}
|
|
239
295
|
required={required}
|
|
240
|
-
deprecated={schema.deprecated}
|
|
241
296
|
/>
|
|
242
297
|
{schema['x-deprecated-sunset'] ? (
|
|
243
298
|
<div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
|
|
@@ -252,12 +307,7 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
|
|
252
307
|
) : null}
|
|
253
308
|
{shouldDisplayExample(schema) ? (
|
|
254
309
|
<div className="openapi-schema-example">
|
|
255
|
-
Example:{
|
|
256
|
-
<code>
|
|
257
|
-
{typeof schema.example === 'string'
|
|
258
|
-
? schema.example
|
|
259
|
-
: stringifyOpenAPI(schema.example)}
|
|
260
|
-
</code>
|
|
310
|
+
Example: <code>{formatExample(schema.example)}</code>
|
|
261
311
|
</div>
|
|
262
312
|
) : null}
|
|
263
313
|
{schema.pattern ? (
|
|
@@ -276,17 +326,6 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
|
|
276
326
|
* Get the sub-properties of a schema.
|
|
277
327
|
*/
|
|
278
328
|
function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISchemaPropertyEntry[] {
|
|
279
|
-
if (schema.allOf) {
|
|
280
|
-
return schema.allOf.reduce((acc, subSchema) => {
|
|
281
|
-
const properties = getSchemaProperties(subSchema) ?? [
|
|
282
|
-
{
|
|
283
|
-
schema: subSchema,
|
|
284
|
-
},
|
|
285
|
-
];
|
|
286
|
-
return [...acc, ...properties];
|
|
287
|
-
}, [] as OpenAPISchemaPropertyEntry[]);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
329
|
// check array AND schema.items as this is sometimes null despite what the type indicates
|
|
291
330
|
if (schema.type === 'array' && !!schema.items) {
|
|
292
331
|
const items = schema.items;
|
|
@@ -295,6 +334,11 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
|
|
|
295
334
|
return itemProperties;
|
|
296
335
|
}
|
|
297
336
|
|
|
337
|
+
// If the items are a primitive type, we don't need to display them
|
|
338
|
+
if (['string', 'number', 'boolean', 'integer'].includes(items.type) && !items.enum) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
298
342
|
return [
|
|
299
343
|
{
|
|
300
344
|
propertyName: 'items',
|
|
@@ -333,13 +377,18 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
|
|
|
333
377
|
return null;
|
|
334
378
|
}
|
|
335
379
|
|
|
380
|
+
type OpenAPISchemaAlternatives = [
|
|
381
|
+
OpenAPIV3.SchemaObject[],
|
|
382
|
+
OpenAPIV3.DiscriminatorObject | undefined,
|
|
383
|
+
];
|
|
384
|
+
|
|
336
385
|
/**
|
|
337
386
|
* Get the alternatives to display for a schema.
|
|
338
387
|
*/
|
|
339
388
|
export function getSchemaAlternatives(
|
|
340
389
|
schema: OpenAPIV3.SchemaObject,
|
|
341
|
-
ancestors: Set<OpenAPIV3.SchemaObject> = new Set()
|
|
342
|
-
): null |
|
|
390
|
+
ancestors: Set<OpenAPIV3.SchemaObject> = new Set()
|
|
391
|
+
): null | OpenAPISchemaAlternatives {
|
|
343
392
|
const downAncestors = new Set(ancestors).add(schema);
|
|
344
393
|
|
|
345
394
|
if (schema.anyOf) {
|
|
@@ -351,8 +400,7 @@ export function getSchemaAlternatives(
|
|
|
351
400
|
}
|
|
352
401
|
|
|
353
402
|
if (schema.allOf) {
|
|
354
|
-
|
|
355
|
-
return null;
|
|
403
|
+
return [flattenAlternatives('allOf', schema.allOf, downAncestors), schema.discriminator];
|
|
356
404
|
}
|
|
357
405
|
|
|
358
406
|
return null;
|
|
@@ -361,14 +409,16 @@ export function getSchemaAlternatives(
|
|
|
361
409
|
function flattenAlternatives(
|
|
362
410
|
alternativeType: 'oneOf' | 'allOf' | 'anyOf',
|
|
363
411
|
alternatives: OpenAPIV3.SchemaObject[],
|
|
364
|
-
ancestors: Set<OpenAPIV3.SchemaObject
|
|
412
|
+
ancestors: Set<OpenAPIV3.SchemaObject>
|
|
365
413
|
): OpenAPIV3.SchemaObject[] {
|
|
366
414
|
return alternatives.reduce((acc, alternative) => {
|
|
367
415
|
if (!!alternative[alternativeType] && !ancestors.has(alternative)) {
|
|
368
|
-
|
|
416
|
+
acc.push(...(getSchemaAlternatives(alternative, ancestors)?.[0] || []));
|
|
417
|
+
} else {
|
|
418
|
+
acc.push(alternative);
|
|
369
419
|
}
|
|
370
420
|
|
|
371
|
-
return
|
|
421
|
+
return acc;
|
|
372
422
|
}, [] as OpenAPIV3.SchemaObject[]);
|
|
373
423
|
}
|
|
374
424
|
|
|
@@ -376,13 +426,8 @@ export function getSchemaTitle(
|
|
|
376
426
|
schema: OpenAPIV3.SchemaObject,
|
|
377
427
|
|
|
378
428
|
/** If the title is inferred in a oneOf with discriminator, we can use it to optimize the title */
|
|
379
|
-
discriminator?: OpenAPIV3.DiscriminatorObject
|
|
429
|
+
discriminator?: OpenAPIV3.DiscriminatorObject
|
|
380
430
|
): string {
|
|
381
|
-
if (schema.title) {
|
|
382
|
-
// If the schema has a title, use it
|
|
383
|
-
return schema.title;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
431
|
// Try using the discriminator
|
|
387
432
|
if (discriminator?.propertyName && schema.properties) {
|
|
388
433
|
const discriminatorProperty = schema.properties[discriminator.propertyName];
|
|
@@ -409,7 +454,9 @@ export function getSchemaTitle(
|
|
|
409
454
|
if (schema.format) {
|
|
410
455
|
type += ` · ${schema.format}`;
|
|
411
456
|
}
|
|
412
|
-
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if ('anyOf' in schema) {
|
|
413
460
|
type = 'any of';
|
|
414
461
|
} else if ('oneOf' in schema) {
|
|
415
462
|
type = 'one of';
|
|
@@ -419,21 +466,35 @@ export function getSchemaTitle(
|
|
|
419
466
|
type = 'not';
|
|
420
467
|
}
|
|
421
468
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
469
|
+
return type;
|
|
470
|
+
}
|
|
425
471
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
472
|
+
function getDisclosureLabel(schema: OpenAPIV3.SchemaObject): string | undefined {
|
|
473
|
+
if (schema.type === 'array' && !!schema.items) {
|
|
474
|
+
if (schema.items.oneOf) {
|
|
475
|
+
return 'available items';
|
|
476
|
+
}
|
|
429
477
|
|
|
430
|
-
|
|
431
|
-
|
|
478
|
+
// Fallback to "child attributes" for enums and objects
|
|
479
|
+
if (schema.items.enum || schema.items.type === 'object') {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
|
|
432
484
|
}
|
|
433
485
|
|
|
434
|
-
|
|
435
|
-
|
|
486
|
+
return schema.title;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function formatExample(example: any): string {
|
|
490
|
+
if (typeof example === 'string') {
|
|
491
|
+
return example
|
|
492
|
+
.replace(/\n/g, ' ') // Replace newlines with spaces
|
|
493
|
+
.replace(/\s+/g, ' ') // Collapse multiple spaces/newlines into a single space
|
|
494
|
+
.replace(/([\{\}:,])\s+/g, '$1 ') // Ensure a space after {, }, :, and ,
|
|
495
|
+
.replace(/\s+([\{\}:,])/g, ' $1') // Ensure a space before {, }, :, and ,
|
|
496
|
+
.trim();
|
|
436
497
|
}
|
|
437
498
|
|
|
438
|
-
return
|
|
499
|
+
return stringifyOpenAPI(example);
|
|
439
500
|
}
|
|
@@ -1,27 +1,60 @@
|
|
|
1
|
+
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
|
|
1
4
|
interface OpenAPISchemaNameProps {
|
|
2
|
-
|
|
5
|
+
schema?: OpenAPIV3.SchemaObject;
|
|
6
|
+
propertyName?: string | React.JSX.Element;
|
|
3
7
|
required?: boolean;
|
|
4
8
|
type?: string;
|
|
5
|
-
deprecated?: boolean;
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Display the schema name row.
|
|
10
13
|
* It includes the property name, type, required and deprecated status.
|
|
11
14
|
*/
|
|
12
|
-
export function OpenAPISchemaName(props: OpenAPISchemaNameProps)
|
|
13
|
-
const { type, propertyName, required
|
|
15
|
+
export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
|
|
16
|
+
const { schema, type, propertyName, required } = props;
|
|
17
|
+
|
|
18
|
+
const additionalItems = schema && getAdditionalItems(schema);
|
|
14
19
|
|
|
15
20
|
return (
|
|
16
21
|
<div className="openapi-schema-name">
|
|
17
22
|
{propertyName ? (
|
|
18
|
-
<span data-deprecated={deprecated} className="openapi-schema-propertyname">
|
|
23
|
+
<span data-deprecated={schema?.deprecated} className="openapi-schema-propertyname">
|
|
19
24
|
{propertyName}
|
|
20
25
|
</span>
|
|
21
26
|
) : null}
|
|
22
|
-
|
|
27
|
+
<span>
|
|
28
|
+
{type ? <span className="openapi-schema-type">{type}</span> : null}
|
|
29
|
+
{additionalItems ? (
|
|
30
|
+
<span className="openapi-schema-type">{additionalItems}</span>
|
|
31
|
+
) : null}
|
|
32
|
+
</span>
|
|
23
33
|
{required ? <span className="openapi-schema-required">required</span> : null}
|
|
24
|
-
{deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
|
|
34
|
+
{schema?.deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
|
|
25
35
|
</div>
|
|
26
36
|
);
|
|
27
37
|
}
|
|
38
|
+
|
|
39
|
+
function getAdditionalItems(schema: OpenAPIV3.SchemaObject): string {
|
|
40
|
+
let additionalItems = '';
|
|
41
|
+
|
|
42
|
+
if (schema.minimum || schema.minLength) {
|
|
43
|
+
additionalItems += ` · min: ${schema.minimum || schema.minLength}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (schema.maximum || schema.maxLength) {
|
|
47
|
+
additionalItems += ` · max: ${schema.maximum || schema.maxLength}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// If the schema has a default value, we display it
|
|
51
|
+
if (typeof schema.default !== 'undefined') {
|
|
52
|
+
additionalItems += ` · default: ${schema.default}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (schema.nullable) {
|
|
56
|
+
additionalItems = ' | nullable';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return additionalItems;
|
|
60
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
|
|
2
|
-
import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
|
|
3
2
|
import { InteractiveSection } from './InteractiveSection';
|
|
4
3
|
import { Markdown } from './Markdown';
|
|
5
4
|
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
|
5
|
+
import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
|
|
6
6
|
import { resolveDescription } from './utils';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -65,7 +65,7 @@ function getLabelForType(security: OpenAPIV3_1.SecuritySchemeObject) {
|
|
|
65
65
|
return <OpenAPISchemaName propertyName="Authorization" type="string" required />;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
if (security.scheme
|
|
68
|
+
if (security.scheme === 'bearer') {
|
|
69
69
|
const description = resolveDescription(security);
|
|
70
70
|
return (
|
|
71
71
|
<>
|
|
@@ -73,7 +73,7 @@ function getLabelForType(security: OpenAPIV3_1.SecuritySchemeObject) {
|
|
|
73
73
|
{/** Show a default description if none is provided */}
|
|
74
74
|
{!description ? (
|
|
75
75
|
<Markdown
|
|
76
|
-
source={`Bearer authentication header of the form Bearer ${
|
|
76
|
+
source={`Bearer authentication header of the form Bearer ${'<token>'}.`}
|
|
77
77
|
className="openapi-securities-description"
|
|
78
78
|
/>
|
|
79
79
|
) : null}
|
package/src/OpenAPITabs.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
|
4
|
-
import { Key, Tab, TabList, TabPanel, Tabs, TabsProps } from 'react-aria-components';
|
|
4
|
+
import { type Key, Tab, TabList, TabPanel, Tabs, type TabsProps } from 'react-aria-components';
|
|
5
|
+
import { useIntersectionObserver } from 'usehooks-ts';
|
|
5
6
|
import { Markdown } from './Markdown';
|
|
6
7
|
import { useSyncedTabsGlobalState } from './useSyncedTabsGlobalState';
|
|
7
|
-
import { useIntersectionObserver } from 'usehooks-ts';
|
|
8
8
|
|
|
9
|
-
export type
|
|
9
|
+
export type TabItem = {
|
|
10
10
|
key: Key;
|
|
11
11
|
label: string;
|
|
12
12
|
body: React.ReactNode;
|
|
@@ -14,8 +14,8 @@ export type Tab = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
type OpenAPITabsContextData = {
|
|
17
|
-
items:
|
|
18
|
-
selectedTab:
|
|
17
|
+
items: TabItem[];
|
|
18
|
+
selectedTab: TabItem;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
const OpenAPITabsContext = createContext<OpenAPITabsContextData | null>(null);
|
|
@@ -32,17 +32,16 @@ function useOpenAPITabsContext() {
|
|
|
32
32
|
* The OpenAPI Tabs wrapper component.
|
|
33
33
|
*/
|
|
34
34
|
export function OpenAPITabs(
|
|
35
|
-
props: React.PropsWithChildren<TabsProps & { items:
|
|
35
|
+
props: React.PropsWithChildren<TabsProps & { items: TabItem[]; stateKey?: string }>
|
|
36
36
|
) {
|
|
37
37
|
const { children, items, stateKey } = props;
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
const [syncedTabs, setSyncedTabs] = useSyncedTabsGlobalState<Tab>();
|
|
38
|
+
const [ref, isIntersectionVisible] = useIntersectionObserver({
|
|
39
|
+
threshold: 0.1,
|
|
40
|
+
rootMargin: '200px',
|
|
41
|
+
});
|
|
42
|
+
const isVisible = stateKey ? isIntersectionVisible : true;
|
|
43
|
+
const defaultTab = items[0] as TabItem;
|
|
44
|
+
const [syncedTabs, setSyncedTabs] = useSyncedTabsGlobalState<TabItem>();
|
|
46
45
|
const [selectedTabKey, setSelectedTabKey] = useState(() => {
|
|
47
46
|
if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
|
|
48
47
|
const tabFromState = syncedTabs.get(stateKey);
|
|
@@ -50,7 +49,7 @@ export function OpenAPITabs(
|
|
|
50
49
|
}
|
|
51
50
|
return items[0]?.key;
|
|
52
51
|
});
|
|
53
|
-
const [selectedTab, setSelectedTab] = useState<
|
|
52
|
+
const [selectedTab, setSelectedTab] = useState<TabItem>(defaultTab);
|
|
54
53
|
|
|
55
54
|
const handleSelectionChange = (key: Key) => {
|
|
56
55
|
setSelectedTabKey(key);
|
|
@@ -74,11 +73,17 @@ export function OpenAPITabs(
|
|
|
74
73
|
const tabFromState = syncedTabs.get(stateKey);
|
|
75
74
|
|
|
76
75
|
if (!items.some((item) => item.key === tabFromState?.key)) {
|
|
77
|
-
return;
|
|
76
|
+
return setSelectedTab(defaultTab);
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
if (tabFromState && tabFromState?.key !== selectedTab?.key) {
|
|
81
|
-
|
|
80
|
+
const tabFromItems = items.find((item) => item.key === tabFromState.key);
|
|
81
|
+
|
|
82
|
+
if (!tabFromItems) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setSelectedTab(tabFromItems);
|
|
82
87
|
}
|
|
83
88
|
}
|
|
84
89
|
}, [isVisible, stateKey, syncedTabs, selectedTabKey]);
|
|
@@ -88,6 +93,7 @@ export function OpenAPITabs(
|
|
|
88
93
|
return (
|
|
89
94
|
<OpenAPITabsContext.Provider value={contextValue}>
|
|
90
95
|
<Tabs
|
|
96
|
+
ref={ref}
|
|
91
97
|
className="openapi-tabs"
|
|
92
98
|
onSelectionChange={handleSelectionChange}
|
|
93
99
|
selectedKey={selectedTab?.key}
|
package/src/ScalarApiButton.tsx
CHANGED
|
@@ -4,8 +4,8 @@ import { ApiClientModalProvider, useApiClientModal } from '@scalar/api-client-re
|
|
|
4
4
|
import { useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
6
|
|
|
7
|
-
import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
|
|
8
7
|
import { useEventCallback } from 'usehooks-ts';
|
|
8
|
+
import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Button which launches the Scalar API Client
|
|
@@ -48,7 +48,7 @@ export function ScalarApiButton({
|
|
|
48
48
|
path={path}
|
|
49
49
|
specUrl={specUrl}
|
|
50
50
|
/>,
|
|
51
|
-
document.body
|
|
51
|
+
document.body
|
|
52
52
|
)}
|
|
53
53
|
</div>
|
|
54
54
|
);
|
|
@@ -88,7 +88,7 @@ function ScalarModalController(props: {
|
|
|
88
88
|
useImperativeHandle(
|
|
89
89
|
props.controllerRef,
|
|
90
90
|
() => ({ openClient: openClient ? () => openClient() : undefined }),
|
|
91
|
-
[openClient]
|
|
91
|
+
[openClient]
|
|
92
92
|
);
|
|
93
93
|
|
|
94
94
|
// Open the client when the component is mounted.
|