@gitbook/react-openapi 1.1.10 → 1.2.1
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 +21 -0
- package/dist/InteractiveSection.d.ts +4 -0
- package/dist/InteractiveSection.jsx +11 -11
- package/dist/OpenAPICodeSample.d.ts +2 -1
- package/dist/OpenAPICodeSample.jsx +6 -5
- package/dist/OpenAPICodeSampleInteractive.d.ts +3 -0
- package/dist/OpenAPICodeSampleInteractive.jsx +19 -43
- package/dist/OpenAPICodeSampleSelector.d.ts +3 -4
- package/dist/OpenAPICodeSampleSelector.jsx +6 -11
- package/dist/OpenAPICopyButton.d.ts +2 -0
- package/dist/OpenAPICopyButton.jsx +5 -2
- package/dist/OpenAPIDisclosure.d.ts +4 -3
- package/dist/OpenAPIDisclosure.jsx +8 -11
- package/dist/OpenAPIDisclosureGroup.d.ts +7 -3
- package/dist/OpenAPIDisclosureGroup.jsx +18 -18
- package/dist/OpenAPIExample.d.ts +4 -22
- package/dist/OpenAPIExample.jsx +5 -72
- package/dist/OpenAPIMediaType.d.ts +21 -0
- package/dist/OpenAPIMediaType.jsx +61 -0
- package/dist/OpenAPIOperation.d.ts +3 -2
- package/dist/OpenAPIOperation.jsx +9 -68
- package/dist/OpenAPIOperationDescription.d.ts +9 -0
- package/dist/OpenAPIOperationDescription.jsx +22 -0
- package/dist/OpenAPIOperationStability.d.ts +9 -0
- package/dist/OpenAPIOperationStability.jsx +27 -0
- package/dist/OpenAPIPath.d.ts +2 -0
- package/dist/OpenAPIPath.jsx +3 -2
- package/dist/OpenAPIRequestBody.d.ts +3 -1
- package/dist/OpenAPIRequestBody.jsx +4 -3
- package/dist/OpenAPIResponse.d.ts +1 -1
- package/dist/OpenAPIResponse.jsx +1 -1
- package/dist/OpenAPIResponseExample.d.ts +3 -2
- package/dist/OpenAPIResponseExample.jsx +24 -63
- package/dist/OpenAPIResponseExampleContent.d.ts +19 -0
- package/dist/OpenAPIResponseExampleContent.jsx +57 -0
- package/dist/OpenAPIResponses.d.ts +1 -1
- package/dist/OpenAPIResponses.jsx +49 -36
- package/dist/OpenAPISchema.d.ts +1 -1
- package/dist/OpenAPISchema.jsx +103 -15
- package/dist/OpenAPISchemaName.d.ts +2 -0
- package/dist/OpenAPISchemaName.jsx +19 -10
- package/dist/OpenAPISchemaServer.d.ts +1 -1
- package/dist/OpenAPISecurities.d.ts +2 -1
- package/dist/OpenAPISecurities.jsx +11 -10
- package/dist/OpenAPISelect.d.ts +10 -3
- package/dist/OpenAPISelect.jsx +20 -9
- package/dist/OpenAPISpec.d.ts +3 -2
- package/dist/OpenAPISpec.jsx +11 -9
- package/dist/OpenAPIWebhook.d.ts +10 -0
- package/dist/OpenAPIWebhook.jsx +23 -0
- package/dist/OpenAPIWebhookExample.d.ts +6 -0
- package/dist/OpenAPIWebhookExample.jsx +41 -0
- package/dist/ScalarApiButton.d.ts +2 -0
- package/dist/ScalarApiButton.jsx +4 -3
- package/dist/StaticSection.d.ts +4 -1
- package/dist/StaticSection.jsx +13 -4
- package/dist/code-samples.js +57 -39
- package/dist/common/OpenAPIColumnSpec.d.ts +6 -0
- package/dist/common/OpenAPIColumnSpec.jsx +20 -0
- package/dist/common/OpenAPIOperationDescription.d.ts +6 -0
- package/dist/common/OpenAPIOperationDescription.jsx +19 -0
- package/dist/common/OpenAPIStability.d.ts +4 -0
- package/dist/common/OpenAPIStability.jsx +15 -0
- package/dist/common/OpenAPISummary.d.ts +6 -0
- package/dist/common/OpenAPISummary.jsx +30 -0
- package/dist/context.d.ts +23 -2
- package/dist/context.js +32 -0
- package/dist/getOrCreateStoreByKey.d.ts +1 -1
- package/dist/getOrCreateStoreByKey.js +0 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/resolveOpenAPIWebhook.d.ts +11 -0
- package/dist/resolveOpenAPIWebhook.js +127 -0
- package/dist/schemas/OpenAPISchemas.d.ts +2 -2
- package/dist/schemas/OpenAPISchemas.jsx +19 -23
- package/dist/stringifyOpenAPI.d.ts +1 -1
- package/dist/stringifyOpenAPI.js +6 -3
- package/dist/translate.d.ts +10 -0
- package/dist/translate.jsx +75 -0
- package/dist/translations/de.d.ts +37 -0
- package/dist/translations/de.js +37 -0
- package/dist/translations/en.d.ts +37 -0
- package/dist/translations/en.js +37 -0
- package/dist/translations/es.d.ts +37 -0
- package/dist/translations/es.js +37 -0
- package/dist/translations/fr.d.ts +37 -0
- package/dist/translations/fr.js +37 -0
- package/dist/translations/index.d.ts +341 -0
- package/dist/translations/index.js +27 -0
- package/dist/translations/ja.d.ts +37 -0
- package/dist/translations/ja.js +37 -0
- package/dist/translations/nl.d.ts +37 -0
- package/dist/translations/nl.js +37 -0
- package/dist/translations/no.d.ts +37 -0
- package/dist/translations/no.js +37 -0
- package/dist/translations/pt-br.d.ts +37 -0
- package/dist/translations/pt-br.js +37 -0
- package/dist/translations/types.d.ts +5 -0
- package/dist/translations/types.js +1 -0
- package/dist/translations/zh.d.ts +37 -0
- package/dist/translations/zh.js +37 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +8 -50
- package/dist/util/example.d.ts +35 -0
- package/dist/util/example.jsx +103 -0
- package/dist/utils.d.ts +18 -0
- package/dist/utils.js +57 -0
- package/package.json +3 -3
- package/src/InteractiveSection.tsx +16 -14
- package/src/OpenAPICodeSample.tsx +22 -4
- package/src/OpenAPICodeSampleInteractive.tsx +38 -58
- package/src/OpenAPICodeSampleSelector.tsx +19 -12
- package/src/OpenAPICopyButton.tsx +7 -2
- package/src/OpenAPIDisclosure.tsx +20 -22
- package/src/OpenAPIDisclosureGroup.tsx +41 -22
- package/src/OpenAPIExample.tsx +8 -82
- package/src/OpenAPIMediaType.tsx +139 -0
- package/src/OpenAPIOperation.tsx +11 -100
- package/src/OpenAPIOperationDescription.tsx +34 -0
- package/src/OpenAPIOperationStability.tsx +39 -0
- package/src/OpenAPIPath.tsx +4 -1
- package/src/OpenAPIRequestBody.tsx +9 -4
- package/src/OpenAPIResponse.tsx +2 -2
- package/src/OpenAPIResponseExample.tsx +39 -108
- package/src/OpenAPIResponseExampleContent.tsx +123 -0
- package/src/OpenAPIResponses.tsx +84 -62
- package/src/OpenAPISchema.test.ts +80 -0
- package/src/OpenAPISchema.tsx +123 -16
- package/src/OpenAPISchemaName.tsx +26 -11
- package/src/OpenAPISchemaServer.tsx +1 -1
- package/src/OpenAPISecurities.tsx +33 -12
- package/src/OpenAPISelect.tsx +42 -16
- package/src/OpenAPISpec.tsx +21 -10
- package/src/OpenAPIWebhook.tsx +33 -0
- package/src/OpenAPIWebhookExample.tsx +60 -0
- package/src/ScalarApiButton.tsx +6 -6
- package/src/StaticSection.tsx +37 -5
- package/src/code-samples.test.ts +3 -1
- package/src/code-samples.ts +67 -54
- package/src/common/OpenAPIColumnSpec.tsx +31 -0
- package/src/common/OpenAPIOperationDescription.tsx +31 -0
- package/src/common/OpenAPIStability.tsx +23 -0
- package/src/common/OpenAPISummary.tsx +45 -0
- package/src/context.ts +37 -2
- package/src/getOrCreateStoreByKey.ts +1 -3
- package/src/index.ts +5 -1
- package/src/resolveOpenAPIWebhook.ts +99 -0
- package/src/schemas/OpenAPISchemas.tsx +34 -34
- package/src/stringifyOpenAPI.ts +11 -3
- package/src/translate.tsx +80 -0
- package/src/translations/de.ts +37 -0
- package/src/translations/en.ts +37 -0
- package/src/translations/es.ts +37 -0
- package/src/translations/fr.ts +37 -0
- package/src/translations/index.ts +33 -0
- package/src/translations/ja.ts +37 -0
- package/src/translations/nl.ts +37 -0
- package/src/translations/no.ts +37 -0
- package/src/translations/pt-br.ts +37 -0
- package/src/translations/types.ts +7 -0
- package/src/translations/zh.ts +37 -0
- package/src/types.ts +11 -53
- package/src/util/example.tsx +129 -0
- package/src/utils.ts +67 -0
|
@@ -6,6 +6,7 @@ import { useStore } from 'zustand';
|
|
|
6
6
|
import { OpenAPIPath } from './OpenAPIPath';
|
|
7
7
|
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
|
|
8
8
|
import { StaticSection } from './StaticSection';
|
|
9
|
+
import type { OpenAPIClientContext } from './context';
|
|
9
10
|
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
|
|
10
11
|
import type { OpenAPIOperationData } from './types';
|
|
11
12
|
|
|
@@ -22,25 +23,22 @@ type CodeSampleItem = OpenAPISelectItem & {
|
|
|
22
23
|
footer?: React.ReactNode;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
function OpenAPICodeSampleHeader(props: {
|
|
26
27
|
items: CodeSampleItem[];
|
|
27
28
|
data: OpenAPIOperationData;
|
|
29
|
+
selectIcon?: React.ReactNode;
|
|
30
|
+
context: OpenAPIClientContext;
|
|
28
31
|
}) {
|
|
29
|
-
const { data, items } = props;
|
|
30
|
-
|
|
31
|
-
const state = useCodeSampleState(items[0]?.key ?? '');
|
|
32
|
-
const selected = items.find((item) => item.key === state.key) || items[0];
|
|
32
|
+
const { data, items, selectIcon, context } = props;
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
35
|
<>
|
|
36
|
-
<OpenAPIPath canCopy={false} withServer={false} data={data} />
|
|
36
|
+
<OpenAPIPath context={context} canCopy={false} withServer={false} data={data} />
|
|
37
37
|
{items.length > 1 ? (
|
|
38
38
|
<OpenAPISelect
|
|
39
|
-
|
|
40
|
-
onSelectionChange={(key) => {
|
|
41
|
-
state.setKey(key);
|
|
42
|
-
}}
|
|
39
|
+
icon={selectIcon}
|
|
43
40
|
items={items}
|
|
41
|
+
stateKey="codesample"
|
|
44
42
|
placement="bottom end"
|
|
45
43
|
>
|
|
46
44
|
{items.map((item) => (
|
|
@@ -59,8 +57,10 @@ export function OpenAPICodeSampleHeader(props: {
|
|
|
59
57
|
export function OpenAPICodeSampleBody(props: {
|
|
60
58
|
items: CodeSampleItem[];
|
|
61
59
|
data: OpenAPIOperationData;
|
|
60
|
+
selectIcon?: React.ReactNode;
|
|
61
|
+
context: OpenAPIClientContext;
|
|
62
62
|
}) {
|
|
63
|
-
const { items, data } = props;
|
|
63
|
+
const { items, data, selectIcon, context } = props;
|
|
64
64
|
if (!items[0]) {
|
|
65
65
|
throw new Error('No items provided');
|
|
66
66
|
}
|
|
@@ -75,7 +75,14 @@ export function OpenAPICodeSampleBody(props: {
|
|
|
75
75
|
|
|
76
76
|
return (
|
|
77
77
|
<StaticSection
|
|
78
|
-
header={
|
|
78
|
+
header={
|
|
79
|
+
<OpenAPICodeSampleHeader
|
|
80
|
+
context={context}
|
|
81
|
+
selectIcon={selectIcon}
|
|
82
|
+
data={data}
|
|
83
|
+
items={items}
|
|
84
|
+
/>
|
|
85
|
+
}
|
|
79
86
|
className="openapi-codesample"
|
|
80
87
|
>
|
|
81
88
|
<div id={selected.key as string} className="openapi-codesample-panel">
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { Button, type ButtonProps, Tooltip, TooltipTrigger } from 'react-aria-components';
|
|
5
|
+
import type { OpenAPIClientContext } from './context';
|
|
6
|
+
import { t } from './translate';
|
|
5
7
|
|
|
6
8
|
export function OpenAPICopyButton(
|
|
7
9
|
props: ButtonProps & {
|
|
8
10
|
value: string;
|
|
9
11
|
children: React.ReactNode;
|
|
12
|
+
context: OpenAPIClientContext;
|
|
10
13
|
label?: string;
|
|
11
14
|
/**
|
|
12
15
|
* Whether to show a tooltip.
|
|
@@ -15,7 +18,7 @@ export function OpenAPICopyButton(
|
|
|
15
18
|
withTooltip?: boolean;
|
|
16
19
|
}
|
|
17
20
|
) {
|
|
18
|
-
const { value, label, children, onPress, className, withTooltip = true } = props;
|
|
21
|
+
const { value, label, children, onPress, className, context, withTooltip = true } = props;
|
|
19
22
|
const [copied, setCopied] = useState(false);
|
|
20
23
|
const [isOpen, setIsOpen] = useState(false);
|
|
21
24
|
|
|
@@ -60,7 +63,9 @@ export function OpenAPICopyButton(
|
|
|
60
63
|
offset={4}
|
|
61
64
|
className="openapi-tooltip"
|
|
62
65
|
>
|
|
63
|
-
{copied
|
|
66
|
+
{copied
|
|
67
|
+
? t(context.translation, 'copied')
|
|
68
|
+
: label || t(context.translation, 'copy_to_clipboard')}
|
|
64
69
|
</Tooltip>
|
|
65
70
|
</TooltipTrigger>
|
|
66
71
|
);
|
|
@@ -1,41 +1,39 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import type React from 'react';
|
|
2
4
|
import { useState } from 'react';
|
|
3
|
-
import { Button, Disclosure, DisclosurePanel
|
|
4
|
-
import type { OpenAPIClientContext } from './types';
|
|
5
|
+
import { Button, Disclosure, DisclosurePanel } from 'react-aria-components';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Display an interactive OpenAPI disclosure.
|
|
8
9
|
*/
|
|
9
10
|
export function OpenAPIDisclosure(props: {
|
|
10
|
-
|
|
11
|
+
icon: React.ReactNode;
|
|
11
12
|
children: React.ReactNode;
|
|
12
|
-
label: string;
|
|
13
|
+
label: string | ((isExpanded: boolean) => string);
|
|
14
|
+
className?: string;
|
|
13
15
|
}): React.JSX.Element {
|
|
14
|
-
const {
|
|
16
|
+
const { icon, children, label, className } = props;
|
|
15
17
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
16
18
|
|
|
17
19
|
return (
|
|
18
20
|
<Disclosure
|
|
19
|
-
className=
|
|
21
|
+
className={clsx('openapi-disclosure', className)}
|
|
20
22
|
isExpanded={isExpanded}
|
|
21
23
|
onExpandedChange={setIsExpanded}
|
|
22
24
|
>
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{isExpanded ? 'Hide' : 'Show'} {label}
|
|
36
|
-
</span>
|
|
37
|
-
</Button>
|
|
38
|
-
</Heading>
|
|
25
|
+
<Button
|
|
26
|
+
slot="trigger"
|
|
27
|
+
className="openapi-disclosure-trigger"
|
|
28
|
+
style={({ isFocusVisible }) => ({
|
|
29
|
+
outline: isFocusVisible
|
|
30
|
+
? '2px solid rgb(var(--primary-color-500) / 0.4)'
|
|
31
|
+
: 'none',
|
|
32
|
+
})}
|
|
33
|
+
>
|
|
34
|
+
{icon}
|
|
35
|
+
<span>{typeof label === 'function' ? label(isExpanded) : label}</span>
|
|
36
|
+
</Button>
|
|
39
37
|
<DisclosurePanel className="openapi-disclosure-panel">
|
|
40
38
|
{isExpanded ? children : null}
|
|
41
39
|
</DisclosurePanel>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { createContext, useContext, useRef
|
|
3
|
+
import { createContext, useContext, useRef } from 'react';
|
|
4
4
|
import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria';
|
|
5
5
|
import {
|
|
6
6
|
type DisclosureGroupProps,
|
|
@@ -8,18 +8,23 @@ import {
|
|
|
8
8
|
useDisclosureGroupState,
|
|
9
9
|
useDisclosureState,
|
|
10
10
|
} from 'react-stately';
|
|
11
|
+
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
|
|
11
12
|
|
|
12
13
|
interface Props {
|
|
13
14
|
groups: TDisclosureGroup[];
|
|
14
15
|
icon?: React.ReactNode;
|
|
16
|
+
/** State key to use with a store */
|
|
17
|
+
selectStateKey?: string;
|
|
18
|
+
/** Icon to display for the select */
|
|
19
|
+
selectIcon?: React.ReactNode;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
type TDisclosureGroup = {
|
|
18
|
-
|
|
23
|
+
key: string;
|
|
19
24
|
label: string | React.ReactNode;
|
|
20
25
|
tabs?: {
|
|
21
|
-
|
|
22
|
-
label
|
|
26
|
+
key: string;
|
|
27
|
+
label: string | React.ReactNode;
|
|
23
28
|
body?: React.ReactNode;
|
|
24
29
|
}[];
|
|
25
30
|
};
|
|
@@ -30,24 +35,35 @@ const DisclosureGroupStateContext = createContext<DisclosureGroupState | null>(n
|
|
|
30
35
|
* Display an interactive OpenAPI disclosure group.
|
|
31
36
|
*/
|
|
32
37
|
export function OpenAPIDisclosureGroup(props: DisclosureGroupProps & Props) {
|
|
33
|
-
const { icon, groups } = props;
|
|
38
|
+
const { icon, groups, selectStateKey, selectIcon } = props;
|
|
34
39
|
|
|
35
40
|
const state = useDisclosureGroupState(props);
|
|
36
41
|
|
|
37
42
|
return (
|
|
38
43
|
<DisclosureGroupStateContext.Provider value={state}>
|
|
39
44
|
{groups.map((group) => (
|
|
40
|
-
<DisclosureItem
|
|
45
|
+
<DisclosureItem
|
|
46
|
+
selectStateKey={selectStateKey}
|
|
47
|
+
selectIcon={selectIcon}
|
|
48
|
+
icon={icon}
|
|
49
|
+
key={group.key}
|
|
50
|
+
group={group}
|
|
51
|
+
/>
|
|
41
52
|
))}
|
|
42
53
|
</DisclosureGroupStateContext.Provider>
|
|
43
54
|
);
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
function DisclosureItem(props: {
|
|
47
|
-
|
|
57
|
+
function DisclosureItem(props: {
|
|
58
|
+
group: TDisclosureGroup;
|
|
59
|
+
icon?: React.ReactNode;
|
|
60
|
+
selectStateKey?: string;
|
|
61
|
+
selectIcon?: React.ReactNode;
|
|
62
|
+
}) {
|
|
63
|
+
const { icon, group, selectStateKey, selectIcon } = props;
|
|
48
64
|
|
|
49
65
|
const defaultId = useId();
|
|
50
|
-
const id = group.
|
|
66
|
+
const id = group.key || defaultId;
|
|
51
67
|
const groupState = useContext(DisclosureGroupStateContext);
|
|
52
68
|
const isExpanded = groupState?.expandedKeys.has(id) || false;
|
|
53
69
|
const state = useDisclosureState({
|
|
@@ -74,9 +90,9 @@ function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode
|
|
|
74
90
|
const { buttonProps } = useButton(triggerProps, triggerRef);
|
|
75
91
|
const { isFocusVisible, focusProps } = useFocusRing();
|
|
76
92
|
|
|
77
|
-
const defaultTab = group.tabs?.[0]?.
|
|
78
|
-
const
|
|
79
|
-
const selectedTab = group.tabs?.find((tab) => tab.
|
|
93
|
+
const defaultTab = group.tabs?.[0]?.key || '';
|
|
94
|
+
const store = useSelectState(selectStateKey, defaultTab);
|
|
95
|
+
const selectedTab = group.tabs?.find((tab) => tab.key === store.key) || group.tabs?.[0];
|
|
80
96
|
|
|
81
97
|
return (
|
|
82
98
|
<div className="openapi-disclosure-group" aria-expanded={state.isExpanded}>
|
|
@@ -104,23 +120,26 @@ function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode
|
|
|
104
120
|
{group.label}
|
|
105
121
|
</button>
|
|
106
122
|
{group.tabs ? (
|
|
107
|
-
<div
|
|
123
|
+
<div
|
|
124
|
+
className="openapi-disclosure-group-mediatype"
|
|
125
|
+
onClick={(e) => e.stopPropagation()}
|
|
126
|
+
>
|
|
108
127
|
{group.tabs?.length > 1 ? (
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
onChange={(event) => {
|
|
114
|
-
setSelectedTabKey(event.target.value);
|
|
128
|
+
<OpenAPISelect
|
|
129
|
+
icon={selectIcon}
|
|
130
|
+
stateKey={selectStateKey}
|
|
131
|
+
onSelectionChange={() => {
|
|
115
132
|
state.expand();
|
|
116
133
|
}}
|
|
134
|
+
items={group.tabs}
|
|
135
|
+
placement="bottom end"
|
|
117
136
|
>
|
|
118
137
|
{group.tabs.map((tab) => (
|
|
119
|
-
<
|
|
138
|
+
<OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
|
120
139
|
{tab.label}
|
|
121
|
-
</
|
|
140
|
+
</OpenAPISelectItem>
|
|
122
141
|
))}
|
|
123
|
-
</
|
|
142
|
+
</OpenAPISelect>
|
|
124
143
|
) : group.tabs[0]?.label ? (
|
|
125
144
|
<span>{group.tabs[0].label}</span>
|
|
126
145
|
) : null}
|
package/src/OpenAPIExample.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
-
import {
|
|
2
|
+
import type { OpenAPIContext, OpenAPIUniversalContext } from './context';
|
|
3
3
|
import { json2xml } from './json2xml';
|
|
4
4
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
5
|
-
import
|
|
6
|
-
import { checkIsReference } from './utils';
|
|
5
|
+
import { t } from './translate';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Display an example.
|
|
@@ -17,7 +16,7 @@ export function OpenAPIExample(props: {
|
|
|
17
16
|
const code = stringifyExample({ example, xml: syntax === 'xml' });
|
|
18
17
|
|
|
19
18
|
if (code === null) {
|
|
20
|
-
return <OpenAPIEmptyExample />;
|
|
19
|
+
return <OpenAPIEmptyExample context={context} />;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
return context.renderCodeBlock({ code, syntax });
|
|
@@ -44,86 +43,13 @@ function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean
|
|
|
44
43
|
/**
|
|
45
44
|
* Empty response example.
|
|
46
45
|
*/
|
|
47
|
-
export function OpenAPIEmptyExample(
|
|
46
|
+
export function OpenAPIEmptyExample(props: {
|
|
47
|
+
context: OpenAPIUniversalContext;
|
|
48
|
+
}) {
|
|
49
|
+
const { context } = props;
|
|
48
50
|
return (
|
|
49
51
|
<pre className="openapi-example-empty">
|
|
50
|
-
<p>
|
|
52
|
+
<p>{t(context.translation, 'no_content')}</p>
|
|
51
53
|
</pre>
|
|
52
54
|
);
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Generate an example from a reference object.
|
|
57
|
-
*/
|
|
58
|
-
export function getExampleFromReference(ref: OpenAPIV3.ReferenceObject): OpenAPIV3.ExampleObject {
|
|
59
|
-
return { summary: 'Unresolved reference', value: { $ref: ref.$ref } };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get examples from a media type object.
|
|
64
|
-
*/
|
|
65
|
-
export function getExamplesFromMediaTypeObject(args: {
|
|
66
|
-
mediaType: string;
|
|
67
|
-
mediaTypeObject: OpenAPIV3.MediaTypeObject;
|
|
68
|
-
}): { key: string; example: OpenAPIV3.ExampleObject }[] {
|
|
69
|
-
const { mediaTypeObject, mediaType } = args;
|
|
70
|
-
if (mediaTypeObject.examples) {
|
|
71
|
-
return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
|
|
72
|
-
return {
|
|
73
|
-
key,
|
|
74
|
-
example: checkIsReference(example) ? getExampleFromReference(example) : example,
|
|
75
|
-
};
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (mediaTypeObject.example) {
|
|
80
|
-
return [{ key: 'default', example: { value: mediaTypeObject.example } }];
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (mediaTypeObject.schema) {
|
|
84
|
-
if (mediaType === 'application/xml') {
|
|
85
|
-
// @TODO normally we should use the name of the schema but we don't have it
|
|
86
|
-
// fix it when we got the reference name
|
|
87
|
-
const root = mediaTypeObject.schema.xml?.name ?? 'object';
|
|
88
|
-
return [
|
|
89
|
-
{
|
|
90
|
-
key: 'default',
|
|
91
|
-
example: {
|
|
92
|
-
value: {
|
|
93
|
-
[root]: generateSchemaExample(mediaTypeObject.schema, {
|
|
94
|
-
xml: mediaType === 'application/xml',
|
|
95
|
-
mode: 'read',
|
|
96
|
-
}),
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
];
|
|
101
|
-
}
|
|
102
|
-
return [
|
|
103
|
-
{
|
|
104
|
-
key: 'default',
|
|
105
|
-
example: {
|
|
106
|
-
value: generateSchemaExample(mediaTypeObject.schema, {
|
|
107
|
-
mode: 'read',
|
|
108
|
-
}),
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
];
|
|
112
|
-
}
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Get example from a schema object.
|
|
118
|
-
*/
|
|
119
|
-
export function getExampleFromSchema(args: {
|
|
120
|
-
schema: OpenAPIV3.SchemaObject;
|
|
121
|
-
}): OpenAPIV3.ExampleObject {
|
|
122
|
-
const { schema } = args;
|
|
123
|
-
|
|
124
|
-
if (schema.example) {
|
|
125
|
-
return { value: schema.example };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return { value: generateSchemaExample(schema, { mode: 'read' }) };
|
|
129
|
-
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { Key } from 'react-aria';
|
|
4
|
+
import { OpenAPIEmptyExample } from './OpenAPIExample';
|
|
5
|
+
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
|
|
6
|
+
import { StaticSection } from './StaticSection';
|
|
7
|
+
import type { OpenAPIClientContext } from './context';
|
|
8
|
+
|
|
9
|
+
type OpenAPIMediaTypeItem = OpenAPISelectItem & {
|
|
10
|
+
body: React.ReactNode;
|
|
11
|
+
examples?: OpenAPIMediaTypeItem[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get the state of the response examples select.
|
|
16
|
+
*/
|
|
17
|
+
export function useMediaTypesState(stateKey: string | undefined, initialKey: Key = 'default') {
|
|
18
|
+
return useSelectState(stateKey, initialKey);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function useMediaTypeExamplesState(stateKey: string | undefined, initialKey: Key = 'default') {
|
|
22
|
+
return useSelectState(stateKey, initialKey);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function OpenAPIMediaTypeContent(props: {
|
|
26
|
+
items: OpenAPIMediaTypeItem[];
|
|
27
|
+
selectIcon?: React.ReactNode;
|
|
28
|
+
stateKey: string;
|
|
29
|
+
context: OpenAPIClientContext;
|
|
30
|
+
}) {
|
|
31
|
+
const { stateKey, items, selectIcon, context } = props;
|
|
32
|
+
const state = useMediaTypesState(stateKey, items[0]?.key);
|
|
33
|
+
|
|
34
|
+
const examples = items.find((item) => item.key === state.key)?.examples ?? [];
|
|
35
|
+
|
|
36
|
+
if (!items.length && !examples.length) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<StaticSection
|
|
42
|
+
footer={
|
|
43
|
+
items.length > 1 || examples.length > 1 ? (
|
|
44
|
+
<OpenAPIMediaTypeFooter
|
|
45
|
+
items={items}
|
|
46
|
+
examples={examples}
|
|
47
|
+
selectIcon={selectIcon}
|
|
48
|
+
stateKey={stateKey}
|
|
49
|
+
/>
|
|
50
|
+
) : null
|
|
51
|
+
}
|
|
52
|
+
className="openapi-response-media-types-examples"
|
|
53
|
+
>
|
|
54
|
+
<OpenAPIMediaTypeBody
|
|
55
|
+
context={context}
|
|
56
|
+
stateKey={stateKey}
|
|
57
|
+
items={items}
|
|
58
|
+
examples={examples}
|
|
59
|
+
/>
|
|
60
|
+
</StaticSection>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function OpenAPIMediaTypeFooter(props: {
|
|
65
|
+
items: OpenAPIMediaTypeItem[];
|
|
66
|
+
examples?: OpenAPIMediaTypeItem[];
|
|
67
|
+
selectIcon?: React.ReactNode;
|
|
68
|
+
stateKey: string;
|
|
69
|
+
}) {
|
|
70
|
+
const { items, examples, stateKey, selectIcon } = props;
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<>
|
|
74
|
+
{items.length > 1 && (
|
|
75
|
+
<OpenAPISelect
|
|
76
|
+
icon={selectIcon}
|
|
77
|
+
items={items}
|
|
78
|
+
stateKey={stateKey}
|
|
79
|
+
placement="bottom start"
|
|
80
|
+
>
|
|
81
|
+
{items.map((item) => (
|
|
82
|
+
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
83
|
+
<span>{item.label}</span>
|
|
84
|
+
</OpenAPISelectItem>
|
|
85
|
+
))}
|
|
86
|
+
</OpenAPISelect>
|
|
87
|
+
)}
|
|
88
|
+
|
|
89
|
+
{examples && examples.length > 1 ? (
|
|
90
|
+
<OpenAPISelect
|
|
91
|
+
icon={selectIcon}
|
|
92
|
+
items={examples}
|
|
93
|
+
stateKey={`${stateKey}-examples`}
|
|
94
|
+
placement="bottom start"
|
|
95
|
+
>
|
|
96
|
+
{examples.map((example) => (
|
|
97
|
+
<OpenAPISelectItem key={example.key} id={example.key} value={example}>
|
|
98
|
+
<span>{example.label}</span>
|
|
99
|
+
</OpenAPISelectItem>
|
|
100
|
+
))}
|
|
101
|
+
</OpenAPISelect>
|
|
102
|
+
) : null}
|
|
103
|
+
</>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function OpenAPIMediaTypeBody(props: {
|
|
108
|
+
items: OpenAPIMediaTypeItem[];
|
|
109
|
+
examples?: OpenAPIMediaTypeItem[];
|
|
110
|
+
stateKey: string;
|
|
111
|
+
context: OpenAPIClientContext;
|
|
112
|
+
}) {
|
|
113
|
+
const { stateKey, items, examples, context } = props;
|
|
114
|
+
const state = useMediaTypesState(stateKey, items[0]?.key);
|
|
115
|
+
|
|
116
|
+
const selectedItem = items.find((item) => item.key === state.key) ?? items[0];
|
|
117
|
+
|
|
118
|
+
const exampleState = useMediaTypeExamplesState(
|
|
119
|
+
`${stateKey}-examples`,
|
|
120
|
+
selectedItem?.examples?.[0]?.key
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!selectedItem) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (examples) {
|
|
128
|
+
const selectedExample =
|
|
129
|
+
examples.find((example) => example.key === exampleState.key) ?? examples[0];
|
|
130
|
+
|
|
131
|
+
if (!selectedExample) {
|
|
132
|
+
return <OpenAPIEmptyExample context={context} />;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return selectedExample.body;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return selectedItem.body;
|
|
139
|
+
}
|
package/src/OpenAPIOperation.tsx
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
OpenAPICustomOperationProperties,
|
|
5
|
-
OpenAPIStability,
|
|
6
|
-
OpenAPIV3,
|
|
7
|
-
} from '@gitbook/openapi-parser';
|
|
8
|
-
import { Markdown } from './Markdown';
|
|
9
2
|
import { OpenAPICodeSample } from './OpenAPICodeSample';
|
|
10
|
-
import { OpenAPIPath } from './OpenAPIPath';
|
|
11
3
|
import { OpenAPIResponseExample } from './OpenAPIResponseExample';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import type
|
|
15
|
-
import {
|
|
4
|
+
import { OpenAPIColumnSpec } from './common/OpenAPIColumnSpec';
|
|
5
|
+
import { OpenAPISummary } from './common/OpenAPISummary';
|
|
6
|
+
import { type OpenAPIContextInput, resolveOpenAPIContext } from './context';
|
|
7
|
+
import type { OpenAPIOperationData } from './types';
|
|
16
8
|
|
|
17
9
|
/**
|
|
18
10
|
* Display an interactive OpenAPI operation.
|
|
@@ -20,105 +12,24 @@ import { resolveDescription } from './utils';
|
|
|
20
12
|
export function OpenAPIOperation(props: {
|
|
21
13
|
className?: string;
|
|
22
14
|
data: OpenAPIOperationData;
|
|
23
|
-
context:
|
|
15
|
+
context: OpenAPIContextInput;
|
|
24
16
|
}) {
|
|
25
|
-
const { className, data, context } = props;
|
|
26
|
-
const { operation } = data;
|
|
17
|
+
const { className, data, context: contextInput } = props;
|
|
27
18
|
|
|
28
|
-
const
|
|
19
|
+
const context = resolveOpenAPIContext(contextInput);
|
|
29
20
|
|
|
30
21
|
return (
|
|
31
22
|
<div className={clsx('openapi-operation', className)}>
|
|
32
|
-
<
|
|
33
|
-
{(operation.deprecated || operation['x-stability']) && (
|
|
34
|
-
<div className="openapi-summary-tags">
|
|
35
|
-
{operation.deprecated && (
|
|
36
|
-
<div className="openapi-deprecated">Deprecated</div>
|
|
37
|
-
)}
|
|
38
|
-
{operation['x-stability'] && (
|
|
39
|
-
<OpenAPIOperationStability stability={operation['x-stability']} />
|
|
40
|
-
)}
|
|
41
|
-
</div>
|
|
42
|
-
)}
|
|
43
|
-
{operation.summary
|
|
44
|
-
? context.renderHeading({
|
|
45
|
-
deprecated: operation.deprecated ?? false,
|
|
46
|
-
stability: operation['x-stability'],
|
|
47
|
-
title: operation.summary,
|
|
48
|
-
})
|
|
49
|
-
: null}
|
|
50
|
-
<OpenAPIPath data={data} />
|
|
51
|
-
</div>
|
|
23
|
+
<OpenAPISummary data={data} context={context} />
|
|
52
24
|
<div className="openapi-columns">
|
|
53
|
-
<
|
|
54
|
-
{operation['x-deprecated-sunset'] ? (
|
|
55
|
-
<div className="openapi-deprecated-sunset openapi-description openapi-markdown">
|
|
56
|
-
This operation is deprecated and will be sunset on{' '}
|
|
57
|
-
<span className="openapi-deprecated-sunset-date">
|
|
58
|
-
{operation['x-deprecated-sunset']}
|
|
59
|
-
</span>
|
|
60
|
-
{'.'}
|
|
61
|
-
</div>
|
|
62
|
-
) : null}
|
|
63
|
-
<OpenAPIOperationDescription operation={operation} context={context} />
|
|
64
|
-
<OpenAPISpec data={data} context={clientContext} />
|
|
65
|
-
</div>
|
|
25
|
+
<OpenAPIColumnSpec data={data} context={context} />
|
|
66
26
|
<div className="openapi-column-preview">
|
|
67
27
|
<div className="openapi-column-preview-body">
|
|
68
|
-
<OpenAPICodeSample {
|
|
69
|
-
<OpenAPIResponseExample {
|
|
28
|
+
<OpenAPICodeSample data={data} context={context} />
|
|
29
|
+
<OpenAPIResponseExample data={data} context={context} />
|
|
70
30
|
</div>
|
|
71
31
|
</div>
|
|
72
32
|
</div>
|
|
73
33
|
</div>
|
|
74
34
|
);
|
|
75
35
|
}
|
|
76
|
-
|
|
77
|
-
function OpenAPIOperationDescription(props: {
|
|
78
|
-
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
|
79
|
-
context: OpenAPIContext;
|
|
80
|
-
}) {
|
|
81
|
-
const { operation } = props;
|
|
82
|
-
if (operation['x-gitbook-description-document']) {
|
|
83
|
-
return (
|
|
84
|
-
<div className="openapi-intro">
|
|
85
|
-
{props.context.renderDocument({
|
|
86
|
-
document: operation['x-gitbook-description-document'],
|
|
87
|
-
})}
|
|
88
|
-
</div>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const description = resolveDescription(operation);
|
|
93
|
-
if (!description) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<div className="openapi-intro">
|
|
99
|
-
<Markdown className="openapi-description" source={description} />
|
|
100
|
-
</div>
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const stabilityEnum = {
|
|
105
|
-
experimental: 'Experimental',
|
|
106
|
-
alpha: 'Alpha',
|
|
107
|
-
beta: 'Beta',
|
|
108
|
-
} as const;
|
|
109
|
-
|
|
110
|
-
function OpenAPIOperationStability(props: { stability: OpenAPIStability }) {
|
|
111
|
-
const { stability } = props;
|
|
112
|
-
|
|
113
|
-
const foundStability = stabilityEnum[stability];
|
|
114
|
-
|
|
115
|
-
if (!foundStability) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return (
|
|
120
|
-
<div className={`openapi-stability openapi-stability-${foundStability.toLowerCase()}`}>
|
|
121
|
-
{foundStability}
|
|
122
|
-
</div>
|
|
123
|
-
);
|
|
124
|
-
}
|