@gitbook/react-openapi 0.7.0 → 1.0.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.
- package/CHANGELOG.md +51 -0
- package/dist/InteractiveSection.d.ts +4 -6
- package/dist/InteractiveSection.jsx +96 -0
- package/dist/Markdown.d.ts +1 -2
- package/dist/Markdown.jsx +5 -0
- package/dist/OpenAPICodeSample.d.ts +2 -4
- package/dist/OpenAPICodeSample.jsx +143 -0
- package/dist/OpenAPIDisclosure.d.ts +12 -0
- package/dist/OpenAPIDisclosure.jsx +32 -0
- package/dist/OpenAPIDisclosureGroup.d.ts +19 -0
- package/dist/OpenAPIDisclosureGroup.jsx +81 -0
- package/dist/OpenAPIOperation.d.ts +2 -4
- package/dist/OpenAPIOperation.jsx +51 -0
- package/dist/OpenAPIOperationContext.d.ts +16 -0
- package/dist/OpenAPIOperationContext.jsx +26 -0
- package/dist/OpenAPIPath.d.ts +8 -0
- package/dist/OpenAPIPath.jsx +54 -0
- package/dist/OpenAPIRequestBody.d.ts +3 -4
- package/dist/OpenAPIRequestBody.jsx +19 -0
- package/dist/OpenAPIResponse.d.ts +4 -4
- package/dist/OpenAPIResponse.jsx +49 -0
- package/dist/OpenAPIResponseExample.d.ts +2 -4
- package/dist/OpenAPIResponseExample.jsx +108 -0
- package/dist/OpenAPIResponses.d.ts +3 -4
- package/dist/OpenAPIResponses.jsx +36 -0
- package/dist/OpenAPISchema.d.ts +11 -8
- package/dist/OpenAPISchema.jsx +295 -0
- package/dist/OpenAPISchemaName.d.ts +12 -0
- package/dist/OpenAPISchemaName.jsx +15 -0
- package/dist/OpenAPISecurities.d.ts +2 -4
- package/dist/OpenAPISecurities.jsx +55 -0
- package/dist/OpenAPIServerURL.d.ts +2 -3
- package/dist/OpenAPIServerURL.jsx +67 -0
- package/dist/OpenAPIServerURLVariable.d.ts +2 -3
- package/dist/OpenAPIServerURLVariable.jsx +8 -0
- package/dist/OpenAPISpec.d.ts +3 -4
- package/dist/OpenAPISpec.jsx +91 -0
- package/dist/OpenAPITabs.d.ts +25 -0
- package/dist/OpenAPITabs.jsx +67 -0
- package/dist/ScalarApiButton.d.ts +5 -2
- package/dist/ScalarApiButton.jsx +51 -0
- package/dist/code-samples.d.ts +4 -0
- package/dist/code-samples.js +103 -38
- package/dist/fetchOpenAPIOperation.d.ts +9 -54
- package/dist/fetchOpenAPIOperation.js +178 -107
- package/dist/generateSchemaExample.d.ts +2 -2
- package/dist/generateSchemaExample.js +28 -100
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/resolveOpenAPIOperation.d.ts +11 -0
- package/dist/resolveOpenAPIOperation.js +194 -0
- package/dist/stringifyOpenAPI.d.ts +4 -0
- package/dist/stringifyOpenAPI.js +6 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +11 -12
- package/dist/utils.d.ts +6 -1
- package/dist/utils.js +15 -2
- package/package.json +12 -11
- package/src/InteractiveSection.tsx +119 -78
- package/src/Markdown.tsx +2 -3
- package/src/OpenAPICodeSample.tsx +35 -21
- package/src/OpenAPIDisclosure.tsx +50 -0
- package/src/OpenAPIDisclosureGroup.tsx +136 -0
- package/src/OpenAPIOperation.tsx +36 -42
- package/src/OpenAPIOperationContext.tsx +45 -0
- package/src/OpenAPIPath.tsx +65 -0
- package/src/OpenAPIRequestBody.tsx +3 -14
- package/src/OpenAPIResponse.tsx +39 -43
- package/src/OpenAPIResponseExample.tsx +89 -31
- package/src/OpenAPIResponses.tsx +51 -15
- package/src/OpenAPISchema.test.ts +1 -1
- package/src/OpenAPISchema.tsx +124 -92
- package/src/OpenAPISchemaName.tsx +27 -0
- package/src/OpenAPISecurities.tsx +45 -24
- package/src/OpenAPIServerURL.tsx +17 -10
- package/src/OpenAPIServerURLVariable.tsx +2 -4
- package/src/OpenAPISpec.tsx +56 -53
- package/src/OpenAPITabs.tsx +113 -0
- package/src/ScalarApiButton.tsx +86 -6
- package/src/code-samples.test.ts +51 -0
- package/src/code-samples.ts +95 -31
- package/src/generateSchemaExample.ts +25 -151
- package/src/index.ts +3 -2
- package/src/resolveOpenAPIOperation.test.ts +177 -0
- package/src/resolveOpenAPIOperation.ts +163 -0
- package/src/stringifyOpenAPI.ts +6 -0
- package/src/types.ts +17 -10
- package/src/utils.ts +17 -2
- package/dist/InteractiveSection.js +0 -47
- package/dist/Markdown.js +0 -6
- package/dist/OpenAPICodeSample.js +0 -110
- package/dist/OpenAPIOperation.js +0 -38
- package/dist/OpenAPIRequestBody.js +0 -18
- package/dist/OpenAPIResponse.js +0 -32
- package/dist/OpenAPIResponseExample.js +0 -54
- package/dist/OpenAPIResponses.js +0 -18
- package/dist/OpenAPISchema.js +0 -235
- package/dist/OpenAPISchema.test.d.ts +0 -1
- package/dist/OpenAPISchema.test.js +0 -91
- package/dist/OpenAPISecurities.js +0 -42
- package/dist/OpenAPIServerURL.js +0 -51
- package/dist/OpenAPIServerURLVariable.js +0 -10
- package/dist/OpenAPISpec.js +0 -70
- package/dist/ScalarApiButton.js +0 -14
- package/dist/fetchOpenAPIOperation.test.d.ts +0 -1
- package/dist/fetchOpenAPIOperation.test.js +0 -152
- package/dist/resolveOpenAPIPath.d.ts +0 -7
- package/dist/resolveOpenAPIPath.js +0 -112
- package/dist/resolveOpenAPIPath.test.d.ts +0 -1
- package/dist/resolveOpenAPIPath.test.js +0 -39
- package/src/fetchOpenAPIOperation.test.ts +0 -185
- package/src/fetchOpenAPIOperation.ts +0 -230
- package/src/resolveOpenAPIPath.test.ts +0 -60
- package/src/resolveOpenAPIPath.ts +0 -145
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
className?: string;
|
|
3
|
-
}>;
|
|
1
|
+
import type { OpenAPICustomOperationProperties, OpenAPICustomSpecProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
4
2
|
export interface OpenAPIContextProps extends OpenAPIClientContext {
|
|
5
3
|
CodeBlock: React.ComponentType<{
|
|
6
4
|
code: string;
|
|
@@ -13,6 +11,7 @@ export interface OpenAPIClientContext {
|
|
|
13
11
|
icons: {
|
|
14
12
|
chevronDown: React.ReactNode;
|
|
15
13
|
chevronRight: React.ReactNode;
|
|
14
|
+
plus: React.ReactNode;
|
|
16
15
|
};
|
|
17
16
|
/**
|
|
18
17
|
* Force all sections to be opened by default.
|
|
@@ -26,13 +25,13 @@ export interface OpenAPIClientContext {
|
|
|
26
25
|
/** Optional id attached to the OpenAPI Operation heading and used as an anchor */
|
|
27
26
|
id?: string;
|
|
28
27
|
}
|
|
29
|
-
export interface
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
|
29
|
+
path: string;
|
|
30
|
+
method: string;
|
|
31
|
+
/** Servers to be used for this operation */
|
|
32
|
+
servers: OpenAPIV3.ServerObject[];
|
|
33
|
+
/** Spec of the operation */
|
|
34
|
+
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
|
35
|
+
/** Securities that should be used for this operation */
|
|
36
|
+
securities: [string, OpenAPIV3.SecuritySchemeObject][];
|
|
38
37
|
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import { OpenAPIV3 } from 'openapi-
|
|
1
|
+
import type { AnyObject, OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
2
|
export declare function noReference<T>(input: T | OpenAPIV3.ReferenceObject): T;
|
|
3
|
+
export declare function checkIsReference(input: unknown): input is OpenAPIV3.ReferenceObject;
|
|
3
4
|
export declare function createStateKey(key: string, scope?: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the description of an object.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveDescription(object: AnyObject): string | undefined;
|
package/dist/utils.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
export function noReference(input) {
|
|
2
|
-
if (
|
|
2
|
+
if (checkIsReference(input)) {
|
|
3
3
|
throw new Error('Reference found');
|
|
4
4
|
}
|
|
5
5
|
return input;
|
|
6
6
|
}
|
|
7
|
+
export function checkIsReference(input) {
|
|
8
|
+
return typeof input === 'object' && !!input && '$ref' in input;
|
|
9
|
+
}
|
|
7
10
|
export function createStateKey(key, scope) {
|
|
8
|
-
return scope ?
|
|
11
|
+
return scope ? "".concat(scope, "_").concat(key) : key;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the description of an object.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveDescription(object) {
|
|
17
|
+
return 'x-description-html' in object && typeof object['x-description-html'] === 'string'
|
|
18
|
+
? object['x-description-html']
|
|
19
|
+
: typeof object.description === 'string'
|
|
20
|
+
? object.description
|
|
21
|
+
: undefined;
|
|
9
22
|
}
|
package/package.json
CHANGED
|
@@ -8,29 +8,30 @@
|
|
|
8
8
|
"default": "./dist/index.js"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"version": "0.
|
|
11
|
+
"version": "1.0.0",
|
|
12
|
+
"sideEffects": false,
|
|
12
13
|
"dependencies": {
|
|
13
|
-
"@
|
|
14
|
-
"
|
|
14
|
+
"@gitbook/openapi-parser": "workspace:*",
|
|
15
|
+
"@scalar/api-client-react": "1.0.87",
|
|
16
|
+
"@scalar/oas-utils": "^0.2.101",
|
|
17
|
+
"clsx": "^2.1.1",
|
|
15
18
|
"flatted": "^3.2.9",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
+
"react-aria-components": "^1.6.0",
|
|
20
|
+
"react-aria": "^3.37.0",
|
|
21
|
+
"usehooks-ts": "^3.1.0"
|
|
19
22
|
},
|
|
20
23
|
"devDependencies": {
|
|
21
|
-
"@types/swagger2openapi": "^7.0.4",
|
|
22
24
|
"bun-types": "^1.1.20",
|
|
23
25
|
"typescript": "^5.5.3"
|
|
24
26
|
},
|
|
25
27
|
"peerDependencies": {
|
|
26
|
-
"react": "*"
|
|
27
|
-
"recoil": "^0.7.7"
|
|
28
|
+
"react": "*"
|
|
28
29
|
},
|
|
29
30
|
"scripts": {
|
|
30
|
-
"build": "tsc",
|
|
31
|
+
"build": "tsc --project tsconfig.build.json",
|
|
31
32
|
"typecheck": "tsc --noEmit",
|
|
32
33
|
"unit": "bun test",
|
|
33
|
-
"dev": "
|
|
34
|
+
"dev": "bun run build -- --watch",
|
|
34
35
|
"clean": "rm -rf ./dist"
|
|
35
36
|
},
|
|
36
37
|
"files": [
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { useCallback, useRef, useState, useSyncExternalStore } from 'react';
|
|
5
|
+
import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
|
|
6
|
+
import { useDisclosureState } from 'react-stately';
|
|
6
7
|
|
|
7
8
|
interface InteractiveSectionTab {
|
|
8
9
|
key: string;
|
|
@@ -10,10 +11,29 @@ interface InteractiveSectionTab {
|
|
|
10
11
|
body: React.ReactNode;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
34
|
+
|
|
35
|
+
return [tabs, setSyncedTabs] as const;
|
|
36
|
+
}
|
|
17
37
|
|
|
18
38
|
/**
|
|
19
39
|
* To optimize rendering, most of the components are server-components,
|
|
@@ -27,15 +47,14 @@ export function InteractiveSection(props: {
|
|
|
27
47
|
toggeable?: boolean;
|
|
28
48
|
/** Default state of the toggle */
|
|
29
49
|
defaultOpened?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
toggleCloseIcon?: React.ReactNode;
|
|
50
|
+
/** Icon to display for the toggle */
|
|
51
|
+
toggleIcon?: React.ReactNode;
|
|
33
52
|
/** Tabs of content to display */
|
|
34
53
|
tabs?: Array<InteractiveSectionTab>;
|
|
35
54
|
/** Default tab to have opened */
|
|
36
55
|
defaultTab?: string;
|
|
37
56
|
/** Content of the header */
|
|
38
|
-
header
|
|
57
|
+
header?: React.ReactNode;
|
|
39
58
|
/** Body of the section */
|
|
40
59
|
children?: React.ReactNode;
|
|
41
60
|
/** Children to display within the container */
|
|
@@ -53,99 +72,121 @@ export function InteractiveSection(props: {
|
|
|
53
72
|
header,
|
|
54
73
|
children,
|
|
55
74
|
overlay,
|
|
56
|
-
|
|
57
|
-
toggleCloseIcon = '▼',
|
|
75
|
+
toggleIcon = '▶',
|
|
58
76
|
stateKey,
|
|
59
77
|
} = props;
|
|
60
|
-
const [syncedTabs, setSyncedTabs] =
|
|
78
|
+
const [syncedTabs, setSyncedTabs] = useSyncedTabsGlobalState();
|
|
61
79
|
const tabFromState =
|
|
62
80
|
stateKey && stateKey in syncedTabs
|
|
63
81
|
? tabs.find((tab) => tab.key === syncedTabs[stateKey])
|
|
64
82
|
: undefined;
|
|
65
|
-
|
|
66
|
-
const [opened, setOpened] = React.useState(defaultOpened);
|
|
67
|
-
const [selectedTabKey, setSelectedTab] = React.useState(tabFromState?.key ?? defaultTab);
|
|
83
|
+
const [selectedTabKey, setSelectedTab] = useState(tabFromState?.key ?? defaultTab);
|
|
68
84
|
const selectedTab: InteractiveSectionTab | undefined =
|
|
69
85
|
tabFromState ?? tabs.find((tab) => tab.key === selectedTabKey) ?? tabs[0];
|
|
70
86
|
|
|
87
|
+
const state = useDisclosureState({
|
|
88
|
+
defaultExpanded: defaultOpened,
|
|
89
|
+
});
|
|
90
|
+
const panelRef = useRef<HTMLDivElement | null>(null);
|
|
91
|
+
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
|
92
|
+
const { buttonProps: triggerProps, panelProps } = useDisclosure({}, state, panelRef);
|
|
93
|
+
const { buttonProps } = useButton(triggerProps, triggerRef);
|
|
94
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
95
|
+
|
|
71
96
|
return (
|
|
72
97
|
<div
|
|
73
98
|
id={id}
|
|
74
|
-
className={
|
|
99
|
+
className={clsx(
|
|
75
100
|
'openapi-section',
|
|
76
101
|
toggeable ? 'openapi-section-toggeable' : null,
|
|
77
102
|
className,
|
|
78
|
-
toggeable ? `${className}-${
|
|
103
|
+
toggeable ? `${className}-${state.isExpanded ? 'opened' : 'closed'}` : null,
|
|
79
104
|
)}
|
|
80
105
|
>
|
|
81
|
-
|
|
82
|
-
onClick={() => {
|
|
83
|
-
if (toggeable) {
|
|
84
|
-
setOpened(!opened);
|
|
85
|
-
}
|
|
86
|
-
}}
|
|
87
|
-
className={classNames('openapi-section-header', `${className}-header`)}
|
|
88
|
-
>
|
|
106
|
+
{header ? (
|
|
89
107
|
<div
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
onClick={() => {
|
|
109
|
+
if (toggeable) {
|
|
110
|
+
state.toggle();
|
|
111
|
+
}
|
|
112
|
+
}}
|
|
113
|
+
className={clsx('openapi-section-header', `${className}-header`)}
|
|
94
114
|
>
|
|
95
|
-
|
|
115
|
+
<div
|
|
116
|
+
className={clsx(
|
|
117
|
+
'openapi-section-header-content',
|
|
118
|
+
`${className}-header-content`,
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
{(children || selectedTab?.body) && toggeable ? (
|
|
122
|
+
<button
|
|
123
|
+
{...mergeProps(buttonProps, focusProps)}
|
|
124
|
+
ref={triggerRef}
|
|
125
|
+
className={clsx('openapi-section-toggle', `${className}-toggle`)}
|
|
126
|
+
style={{
|
|
127
|
+
outline: isFocusVisible
|
|
128
|
+
? '2px solid rgb(var(--primary-color-500) / 0.4)'
|
|
129
|
+
: 'none',
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
{toggleIcon}
|
|
133
|
+
</button>
|
|
134
|
+
) : null}
|
|
135
|
+
{header}
|
|
136
|
+
</div>
|
|
137
|
+
<div
|
|
138
|
+
className={clsx(
|
|
139
|
+
'openapi-section-header-controls',
|
|
140
|
+
`${className}-header-controls`,
|
|
141
|
+
)}
|
|
142
|
+
onClick={(event) => {
|
|
143
|
+
event.stopPropagation();
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
{tabs.length > 1 ? (
|
|
147
|
+
<select
|
|
148
|
+
className={clsx(
|
|
149
|
+
'openapi-section-select',
|
|
150
|
+
'openapi-select',
|
|
151
|
+
`${className}-tabs-select`,
|
|
152
|
+
)}
|
|
153
|
+
value={selectedTab?.key ?? ''}
|
|
154
|
+
onChange={(event) => {
|
|
155
|
+
setSelectedTab(event.target.value);
|
|
156
|
+
if (stateKey) {
|
|
157
|
+
setSyncedTabs((state) => ({
|
|
158
|
+
...state,
|
|
159
|
+
[stateKey]: event.target.value,
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
state.expand();
|
|
163
|
+
}}
|
|
164
|
+
>
|
|
165
|
+
{tabs.map((tab) => (
|
|
166
|
+
<option key={tab.key} value={tab.key}>
|
|
167
|
+
{tab.label}
|
|
168
|
+
</option>
|
|
169
|
+
))}
|
|
170
|
+
</select>
|
|
171
|
+
) : null}
|
|
172
|
+
</div>
|
|
96
173
|
</div>
|
|
174
|
+
) : null}
|
|
175
|
+
{(!toggeable || state.isExpanded) && (children || selectedTab?.body) ? (
|
|
97
176
|
<div
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)}
|
|
102
|
-
onClick={(event) => {
|
|
103
|
-
event.stopPropagation();
|
|
104
|
-
}}
|
|
177
|
+
ref={panelRef}
|
|
178
|
+
{...panelProps}
|
|
179
|
+
className={clsx('openapi-section-body', `${className}-body`)}
|
|
105
180
|
>
|
|
106
|
-
{tabs.length ? (
|
|
107
|
-
<select
|
|
108
|
-
className={classNames(
|
|
109
|
-
'openapi-section-select',
|
|
110
|
-
'openapi-select',
|
|
111
|
-
`${className}-tabs-select`,
|
|
112
|
-
)}
|
|
113
|
-
value={selectedTab.key}
|
|
114
|
-
onChange={(event) => {
|
|
115
|
-
setSelectedTab(event.target.value);
|
|
116
|
-
if (stateKey) {
|
|
117
|
-
setSyncedTabs((state) => ({
|
|
118
|
-
...state,
|
|
119
|
-
[stateKey]: event.target.value,
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
122
|
-
setOpened(true);
|
|
123
|
-
}}
|
|
124
|
-
>
|
|
125
|
-
{tabs.map((tab) => (
|
|
126
|
-
<option key={tab.key} value={tab.key}>
|
|
127
|
-
{tab.label}
|
|
128
|
-
</option>
|
|
129
|
-
))}
|
|
130
|
-
</select>
|
|
131
|
-
) : null}
|
|
132
|
-
{(children || selectedTab?.body) && toggeable ? (
|
|
133
|
-
<button
|
|
134
|
-
className={classNames('openapi-section-toggle', `${className}-toggle`)}
|
|
135
|
-
onClick={() => setOpened(!opened)}
|
|
136
|
-
>
|
|
137
|
-
{opened ? toggleCloseIcon : toggleOpenIcon}
|
|
138
|
-
</button>
|
|
139
|
-
) : null}
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
{(!toggeable || opened) && (children || selectedTab?.body) ? (
|
|
143
|
-
<div className={classNames('openapi-section-body', `${className}-body`)}>
|
|
144
181
|
{children}
|
|
145
182
|
{selectedTab?.body}
|
|
146
183
|
</div>
|
|
147
184
|
) : null}
|
|
148
|
-
{overlay
|
|
185
|
+
{overlay ? (
|
|
186
|
+
<div className={clsx('openapi-section-overlay', `${className}-overlay`)}>
|
|
187
|
+
{overlay}
|
|
188
|
+
</div>
|
|
189
|
+
) : null}
|
|
149
190
|
</div>
|
|
150
191
|
);
|
|
151
192
|
}
|
package/src/Markdown.tsx
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import classNames from 'classnames';
|
|
1
|
+
import clsx from 'clsx';
|
|
3
2
|
|
|
4
3
|
export function Markdown(props: { source: string; className?: string }) {
|
|
5
4
|
const { source, className } = props;
|
|
6
5
|
|
|
7
6
|
return (
|
|
8
7
|
<div
|
|
9
|
-
className={
|
|
8
|
+
className={clsx('openapi-markdown', className)}
|
|
10
9
|
dangerouslySetInnerHTML={{ __html: source }}
|
|
11
10
|
/>
|
|
12
11
|
);
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
1
|
import { CodeSampleInput, codeSampleGenerators } from './code-samples';
|
|
4
|
-
import { OpenAPIOperationData } from './fetchOpenAPIOperation';
|
|
5
2
|
import { generateMediaTypeExample, generateSchemaExample } from './generateSchemaExample';
|
|
6
3
|
import { InteractiveSection } from './InteractiveSection';
|
|
7
4
|
import { getServersURL } from './OpenAPIServerURL';
|
|
8
|
-
import {
|
|
9
|
-
import { OpenAPIContextProps } from './types';
|
|
5
|
+
import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
|
|
10
6
|
import { noReference } from './utils';
|
|
7
|
+
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
8
|
+
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* Display code samples to execute the operation.
|
|
@@ -32,15 +30,15 @@ export function OpenAPICodeSample(props: {
|
|
|
32
30
|
const example = param.schema
|
|
33
31
|
? generateSchemaExample(noReference(param.schema))
|
|
34
32
|
: undefined;
|
|
35
|
-
if (example !== undefined) {
|
|
33
|
+
if (example !== undefined && param.name) {
|
|
36
34
|
headersObject[param.name] =
|
|
37
|
-
typeof example !== 'string' ?
|
|
35
|
+
typeof example !== 'string' ? stringifyOpenAPI(example) : example;
|
|
38
36
|
}
|
|
39
37
|
} else if (param.in === 'query' && param.required) {
|
|
40
38
|
const example = param.schema
|
|
41
39
|
? generateSchemaExample(noReference(param.schema))
|
|
42
40
|
: undefined;
|
|
43
|
-
if (example !== undefined) {
|
|
41
|
+
if (example !== undefined && param.name) {
|
|
44
42
|
searchParams.append(
|
|
45
43
|
param.name,
|
|
46
44
|
String(Array.isArray(example) ? example[0] : example),
|
|
@@ -50,7 +48,10 @@ export function OpenAPICodeSample(props: {
|
|
|
50
48
|
});
|
|
51
49
|
|
|
52
50
|
const requestBody = noReference(data.operation.requestBody);
|
|
53
|
-
const
|
|
51
|
+
const requestBodyContentEntries = requestBody?.content
|
|
52
|
+
? Object.entries(requestBody.content)
|
|
53
|
+
: undefined;
|
|
54
|
+
const requestBodyContent = requestBodyContentEntries?.[0];
|
|
54
55
|
|
|
55
56
|
const input: CodeSampleInput = {
|
|
56
57
|
url:
|
|
@@ -108,21 +109,17 @@ export function OpenAPICodeSample(props: {
|
|
|
108
109
|
const codeSamplesDisabled =
|
|
109
110
|
data['x-codeSamples'] === false || data.operation['x-codeSamples'] === false;
|
|
110
111
|
const samples = customCodeSamples ?? (!codeSamplesDisabled ? autoCodeSamples : []);
|
|
112
|
+
|
|
111
113
|
if (samples.length === 0) {
|
|
112
114
|
return null;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
return (
|
|
116
|
-
<
|
|
117
|
-
header="
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'] ? null : (
|
|
122
|
-
<ScalarApiButton />
|
|
123
|
-
)
|
|
124
|
-
}
|
|
125
|
-
/>
|
|
118
|
+
<OpenAPITabs items={samples}>
|
|
119
|
+
<InteractiveSection header={<OpenAPITabsList />} className="openapi-codesample">
|
|
120
|
+
<OpenAPITabsPanels />
|
|
121
|
+
</InteractiveSection>
|
|
122
|
+
</OpenAPITabs>
|
|
126
123
|
);
|
|
127
124
|
}
|
|
128
125
|
|
|
@@ -130,6 +127,7 @@ function getSecurityHeaders(securities: OpenAPIOperationData['securities']): {
|
|
|
130
127
|
[key: string]: string;
|
|
131
128
|
} {
|
|
132
129
|
const security = securities[0];
|
|
130
|
+
|
|
133
131
|
if (!security) {
|
|
134
132
|
return {};
|
|
135
133
|
}
|
|
@@ -137,12 +135,28 @@ function getSecurityHeaders(securities: OpenAPIOperationData['securities']): {
|
|
|
137
135
|
switch (security[1].type) {
|
|
138
136
|
case 'http': {
|
|
139
137
|
let scheme = security[1].scheme;
|
|
140
|
-
|
|
138
|
+
let format = security[1].bearerFormat ?? 'YOUR_SECRET_TOKEN';
|
|
139
|
+
|
|
140
|
+
if (scheme?.includes('bearer')) {
|
|
141
141
|
scheme = 'Bearer';
|
|
142
|
+
} else if (scheme?.includes('basic')) {
|
|
143
|
+
scheme = 'Basic';
|
|
144
|
+
format = 'username:password';
|
|
145
|
+
} else if (scheme?.includes('token')) {
|
|
146
|
+
scheme = 'Token';
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
return {
|
|
145
|
-
Authorization: scheme + ' ' +
|
|
150
|
+
Authorization: scheme + ' ' + format,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
case 'apiKey': {
|
|
154
|
+
if (security[1].in !== 'header') return {};
|
|
155
|
+
|
|
156
|
+
const name = security[1].name ?? 'Authorization';
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
[name]: 'YOUR_API_KEY',
|
|
146
160
|
};
|
|
147
161
|
}
|
|
148
162
|
default: {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
import type { OpenAPIClientContext } from './types';
|
|
3
|
+
import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
|
|
4
|
+
import { useDisclosureState } from 'react-stately';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
context: OpenAPIClientContext;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Display an interactive OpenAPI disclosure.
|
|
14
|
+
* The label is optional and defaults to "child attributes".
|
|
15
|
+
*/
|
|
16
|
+
export function OpenAPIDisclosure({ context, children, label }: Props): JSX.Element {
|
|
17
|
+
const state = useDisclosureState({});
|
|
18
|
+
const panelRef = useRef<HTMLDivElement | null>(null);
|
|
19
|
+
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
|
20
|
+
const { buttonProps: triggerProps, panelProps } = useDisclosure({}, state, panelRef);
|
|
21
|
+
const { buttonProps } = useButton(triggerProps, triggerRef);
|
|
22
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="openapi-disclosure">
|
|
26
|
+
<button
|
|
27
|
+
ref={triggerRef}
|
|
28
|
+
{...mergeProps(buttonProps, focusProps)}
|
|
29
|
+
slot="trigger"
|
|
30
|
+
className="openapi-disclosure-trigger"
|
|
31
|
+
style={{
|
|
32
|
+
outline: isFocusVisible
|
|
33
|
+
? '2px solid rgb(var(--primary-color-500) / 0.4)'
|
|
34
|
+
: 'none',
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
{context.icons.plus}
|
|
38
|
+
<span>
|
|
39
|
+
{`${state.isExpanded ? 'Hide' : 'Show'} ${label ? label : `child attributes`}`}
|
|
40
|
+
</span>
|
|
41
|
+
</button>
|
|
42
|
+
|
|
43
|
+
{state.isExpanded && (
|
|
44
|
+
<div ref={panelRef} {...panelProps} className="openapi-disclosure-panel">
|
|
45
|
+
{children}
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|