Skip to content

Commit 9943276

Browse files
authored
Revert PR with bugs (#2819)
1 parent c31cf52 commit 9943276

16 files changed

+163
-237
lines changed

.changeset/fifty-donkeys-talk.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/six-trainers-bathe.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/react-openapi/src/InteractiveSection.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,40 @@
11
'use client';
22

33
import classNames from 'classnames';
4-
import React from 'react';
4+
import React, { useCallback } from 'react';
55
import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
66
import { useDisclosureState } from 'react-stately';
7-
import { useSyncedTabsGlobalState } from './useSyncedTabsGlobalState';
87

98
interface InteractiveSectionTab {
109
key: string;
1110
label: string;
1211
body: React.ReactNode;
1312
}
1413

14+
let globalState: Record<string, string> = {};
15+
const listeners = new Set<() => void>();
16+
17+
function useSyncedTabsGlobalState() {
18+
const subscribe = useCallback((callback: () => void) => {
19+
listeners.add(callback);
20+
return () => listeners.delete(callback);
21+
}, []);
22+
23+
const getSnapshot = useCallback(() => globalState, []);
24+
25+
const setSyncedTabs = useCallback(
26+
(updater: (tabs: Record<string, string>) => Record<string, string>) => {
27+
globalState = updater(globalState);
28+
listeners.forEach((listener) => listener());
29+
},
30+
[],
31+
);
32+
33+
const tabs = React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
34+
35+
return [tabs, setSyncedTabs] as const;
36+
}
37+
1538
/**
1639
* To optimize rendering, most of the components are server-components,
1740
* and the interactiveness is mainly handled by a few key components like this one.

packages/react-openapi/src/OpenAPICodeSample.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ import { generateMediaTypeExample, generateSchemaExample } from './generateSchem
66
import { InteractiveSection } from './InteractiveSection';
77
import { getServersURL } from './OpenAPIServerURL';
88
import { OpenAPIContextProps } from './types';
9-
import { createStateKey } from './utils';
9+
import { noReference } from './utils';
1010
import { stringifyOpenAPI } from './stringifyOpenAPI';
1111
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
12-
import { OpenAPIV3 } from '@scalar/openapi-types';
13-
import { checkIsReference } from './utils';
1412

1513
/**
1614
* Display code samples to execute the operation.
@@ -25,19 +23,24 @@ export function OpenAPICodeSample(props: {
2523
const searchParams = new URLSearchParams();
2624
const headersObject: { [k: string]: string } = {};
2725

28-
data.operation.parameters?.forEach((param) => {
26+
data.operation.parameters?.forEach((rawParam) => {
27+
const param = noReference(rawParam);
2928
if (!param) {
3029
return;
3130
}
3231

3332
if (param.in === 'header' && param.required) {
34-
const example = param.schema ? generateSchemaExample(param.schema) : undefined;
33+
const example = param.schema
34+
? generateSchemaExample(noReference(param.schema))
35+
: undefined;
3536
if (example !== undefined && param.name) {
3637
headersObject[param.name] =
3738
typeof example !== 'string' ? stringifyOpenAPI(example) : example;
3839
}
3940
} else if (param.in === 'query' && param.required) {
40-
const example = param.schema ? generateSchemaExample(param.schema) : undefined;
41+
const example = param.schema
42+
? generateSchemaExample(noReference(param.schema))
43+
: undefined;
4144
if (example !== undefined && param.name) {
4245
searchParams.append(
4346
param.name,
@@ -47,9 +50,7 @@ export function OpenAPICodeSample(props: {
4750
}
4851
});
4952

50-
const requestBody = !checkIsReference(data.operation.requestBody)
51-
? data.operation.requestBody
52-
: undefined;
53+
const requestBody = noReference(data.operation.requestBody);
5354
const requestBodyContentEntries = requestBody?.content
5455
? Object.entries(requestBody.content)
5556
: undefined;
@@ -117,7 +118,7 @@ export function OpenAPICodeSample(props: {
117118
}
118119

119120
return (
120-
<OpenAPITabs stateKey={createStateKey('codesample')} items={samples}>
121+
<OpenAPITabs items={samples}>
121122
<InteractiveSection header={<OpenAPITabsList />} className="openapi-codesample">
122123
<OpenAPITabsPanels />
123124
</InteractiveSection>

packages/react-openapi/src/OpenAPIRequestBody.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,19 @@ import * as React from 'react';
22

33
import { OpenAPIV3 } from '@scalar/openapi-types';
44
import { OpenAPIRootSchema } from './OpenAPISchema';
5+
import { noReference } from './utils';
56
import { OpenAPIClientContext } from './types';
67
import { InteractiveSection } from './InteractiveSection';
7-
import { checkIsReference } from './utils';
88

99
/**
1010
* Display an interactive request body.
1111
*/
1212
export function OpenAPIRequestBody(props: {
13-
requestBody: OpenAPIV3.RequestBodyObject | OpenAPIV3.ReferenceObject;
13+
requestBody: OpenAPIV3.RequestBodyObject;
1414
context: OpenAPIClientContext;
1515
}) {
1616
const { requestBody, context } = props;
1717

18-
if (checkIsReference(requestBody)) {
19-
return null;
20-
}
21-
2218
return (
2319
<InteractiveSection
2420
header="Body"
@@ -30,7 +26,7 @@ export function OpenAPIRequestBody(props: {
3026
label: contentType,
3127
body: (
3228
<OpenAPIRootSchema
33-
schema={mediaTypeObject.schema ?? {}}
29+
schema={noReference(mediaTypeObject.schema) ?? {}}
3430
context={context}
3531
/>
3632
),

packages/react-openapi/src/OpenAPIResponse.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import classNames from 'classnames';
22
import { OpenAPIV3 } from '@scalar/openapi-types';
33
import { OpenAPISchemaProperties } from './OpenAPISchema';
4+
import { checkIsReference, noReference } from './utils';
45
import { OpenAPIClientContext } from './types';
56
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
67

@@ -14,7 +15,7 @@ export function OpenAPIResponse(props: {
1415
}) {
1516
const { response, context, mediaType } = props;
1617
const headers = Object.entries(response.headers ?? {}).map(
17-
([name, header]) => [name, header ?? {}] as const,
18+
([name, header]) => [name, noReference(header) ?? {}] as const,
1819
);
1920
const content = Object.entries(mediaType.schema ?? {});
2021

@@ -29,7 +30,7 @@ export function OpenAPIResponse(props: {
2930
<OpenAPISchemaProperties
3031
properties={headers.map(([name, header]) => ({
3132
propertyName: name,
32-
schema: header.schema ?? {},
33+
schema: noReference(header.schema) ?? {},
3334
required: header.required,
3435
}))}
3536
context={context}
@@ -41,7 +42,7 @@ export function OpenAPIResponse(props: {
4142
id={`response-${context.blockKey}`}
4243
properties={[
4344
{
44-
schema: mediaType.schema ?? {},
45+
schema: handleUnresolvedReference(mediaType.schema) ?? {},
4546
},
4647
]}
4748
context={context}
@@ -50,3 +51,17 @@ export function OpenAPIResponse(props: {
5051
</div>
5152
);
5253
}
54+
55+
function handleUnresolvedReference(
56+
input: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined,
57+
): OpenAPIV3.SchemaObject {
58+
const isReference = checkIsReference(input);
59+
60+
if (isReference || input === undefined) {
61+
// If we find a reference that wasn't resolved or needed to be resolved externally, do not try to render it.
62+
// Instead we render `any`
63+
return {};
64+
}
65+
66+
return input;
67+
}

packages/react-openapi/src/OpenAPIResponseExample.tsx

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { OpenAPIOperationData } from './fetchOpenAPIOperation';
22
import { generateSchemaExample } from './generateSchemaExample';
33
import { OpenAPIContextProps } from './types';
4-
import { createStateKey } from './utils';
4+
import { checkIsReference, noReference } from './utils';
55
import { stringifyOpenAPI } from './stringifyOpenAPI';
66
import { OpenAPIV3 } from '@scalar/openapi-types';
77
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
@@ -41,7 +41,7 @@ export function OpenAPIResponseExample(props: {
4141

4242
const examples = responses
4343
.map(([key, value]) => {
44-
const responseObject = value;
44+
const responseObject = noReference(value);
4545
const mediaTypeObject = (() => {
4646
if (!responseObject.content) {
4747
return null;
@@ -61,28 +61,30 @@ export function OpenAPIResponseExample(props: {
6161
};
6262
}
6363

64-
const example: OpenAPIV3.ExampleObject | null = (() => {
65-
const { examples, example } = mediaTypeObject;
66-
if (examples) {
67-
const firstKey = Object.keys(examples)[0];
68-
// @TODO handle multiple examples
69-
const firstExample = examples[firstKey];
70-
if (firstExample) {
71-
return firstExample;
64+
const example = handleUnresolvedReference(
65+
(() => {
66+
const { examples, example } = mediaTypeObject;
67+
if (examples) {
68+
const firstKey = Object.keys(examples)[0];
69+
// @TODO handle multiple examples
70+
const firstExample = noReference(examples[firstKey]);
71+
if (firstExample) {
72+
return firstExample;
73+
}
7274
}
73-
}
7475

75-
if (example) {
76-
return { value: example };
77-
}
76+
if (example) {
77+
return { value: example };
78+
}
7879

79-
const schema = mediaTypeObject.schema;
80-
if (!schema) {
81-
return null;
82-
}
80+
const schema = noReference(mediaTypeObject.schema);
81+
if (!schema) {
82+
return null;
83+
}
8384

84-
return { value: generateSchemaExample(schema) };
85-
})();
85+
return { value: generateSchemaExample(schema) };
86+
})(),
87+
);
8688

8789
return {
8890
key: key,
@@ -111,7 +113,7 @@ export function OpenAPIResponseExample(props: {
111113
}
112114

113115
return (
114-
<OpenAPITabs stateKey={createStateKey('response-example')} items={examples}>
116+
<OpenAPITabs items={examples}>
115117
<InteractiveSection header={<OpenAPITabsList />} className="openapi-response-example">
116118
<OpenAPITabsPanels />
117119
</InteractiveSection>
@@ -126,3 +128,16 @@ function OpenAPIEmptyResponseExample() {
126128
</pre>
127129
);
128130
}
131+
132+
function handleUnresolvedReference(
133+
input: OpenAPIV3.ExampleObject | null,
134+
): OpenAPIV3.ExampleObject | null {
135+
const isReference = checkIsReference(input?.value);
136+
137+
if (isReference) {
138+
// If we find a reference that wasn't resolved or needed to be resolved externally, render out the URL
139+
return { value: input.value.$ref };
140+
}
141+
142+
return input;
143+
}

packages/react-openapi/src/OpenAPIResponses.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import * as React from 'react';
22
import classNames from 'classnames';
3-
import { createStateKey } from './utils';
3+
import { createStateKey, noReference } from './utils';
44
import { OpenAPIResponse } from './OpenAPIResponse';
55
import { OpenAPIClientContext } from './types';
66
import { InteractiveSection } from './InteractiveSection';
77
import { OpenAPIV3, OpenAPIV3_1 } from '@scalar/openapi-types';
88
import { OpenAPIDisclosureGroup } from './OpenAPIDisclosureGroup';
99
import { Markdown } from './Markdown';
10+
import { OpenAPIRootSchema, OpenAPISchemaProperties, OpenAPISchemaProperty } from './OpenAPISchema';
11+
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
1012

1113
/**
1214
* Display an interactive response body.

0 commit comments

Comments
 (0)