Skip to content

Commit d67699a

Browse files
authored
Add OpenAPI Webhook block (#3160)
1 parent 3363a18 commit d67699a

31 files changed

+684
-305
lines changed

.changeset/light-mirrors-give.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@gitbook/react-openapi': minor
3+
'gitbook': minor
4+
---
5+
6+
Add OpenAPI Webhook block

bun.lock

+13-13
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"name": "@gitbook/cache-tags",
2727
"version": "0.3.1",
2828
"dependencies": {
29-
"@gitbook/api": "^0.109.0",
29+
"@gitbook/api": "^0.111.0",
3030
"assert-never": "^1.2.1",
3131
},
3232
"devDependencies": {
@@ -259,7 +259,7 @@
259259
},
260260
"overrides": {
261261
"@codemirror/state": "6.4.1",
262-
"@gitbook/api": "0.109.0",
262+
"@gitbook/api": "0.111.0",
263263
"react": "18.3.1",
264264
"react-dom": "18.3.1",
265265
},
@@ -628,7 +628,7 @@
628628

629629
"@fortawesome/fontawesome-svg-core": ["@fortawesome/[email protected]", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
630630

631-
"@gitbook/api": ["@gitbook/api@0.109.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-nzhxmiNR2MMHh+HoU7fFdOJ+u4BFyXjtsVquQf3VhZMjeI71yeLx5JEbjkOcijVd/GMAtvpYlYR2qcPqzq27gw=="],
631+
"@gitbook/api": ["@gitbook/api@0.111.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-E5Pk28kPD4p6XNWdwFM9pgDijdByseIZQqcFK+/hoW5tEZa5Yw/plRKJyN1hmwfPL6SKq6Maf0fbIzTQiVXyQQ=="],
632632

633633
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
634634

@@ -4032,7 +4032,7 @@
40324032

40334033
"gaxios/node-fetch": ["[email protected]", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
40344034

4035-
"gitbook-v2/next": ["[email protected]", "", { "dependencies": { "@next/env": "15.3.1-canary.7", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.1-canary.7", "@next/swc-darwin-x64": "15.3.1-canary.7", "@next/swc-linux-arm64-gnu": "15.3.1-canary.7", "@next/swc-linux-arm64-musl": "15.3.1-canary.7", "@next/swc-linux-x64-gnu": "15.3.1-canary.7", "@next/swc-linux-x64-musl": "15.3.1-canary.7", "@next/swc-win32-arm64-msvc": "15.3.1-canary.7", "@next/swc-win32-x64-msvc": "15.3.1-canary.7", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RblvC7A7k8cKpSa6JSCbaatb6JAncv2niUSH7TqXt6xHUoItnxa3Fu7xkducFJQdcW2Hv2h0+NBu6kaKf0S4zw=="],
4035+
"gitbook-v2/next": ["[email protected]", "", { "dependencies": { "@next/env": "15.3.1-canary.8", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.1-canary.8", "@next/swc-darwin-x64": "15.3.1-canary.8", "@next/swc-linux-arm64-gnu": "15.3.1-canary.8", "@next/swc-linux-arm64-musl": "15.3.1-canary.8", "@next/swc-linux-x64-gnu": "15.3.1-canary.8", "@next/swc-linux-x64-musl": "15.3.1-canary.8", "@next/swc-win32-arm64-msvc": "15.3.1-canary.8", "@next/swc-win32-x64-msvc": "15.3.1-canary.8", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Of5a3BTTIl/iUvL2a9Jh7m7G/H8z4Pj5Vs54CLvcdadokxSNgLOpjzbDgFR8J4PawLx6+MOMy19m9Cvr6EPGug=="],
40364036

40374037
"global-dirs/ini": ["[email protected]", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="],
40384038

@@ -4908,23 +4908,23 @@
49084908

49094909
"gaxios/https-proxy-agent/debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
49104910

4911-
"gitbook-v2/next/@next/env": ["@next/[email protected].7", "", {}, "sha512-QSzAlzxaGrQYj3Nzgxd35WM6pmxmNk9lOCn0nsNLPidvc3H6wj/fV3Pl8/tVCYQP61luGuA0Ib0VwZc8ja119w=="],
4911+
"gitbook-v2/next/@next/env": ["@next/[email protected].8", "", {}, "sha512-ShZTo0hNhbTRrp7k6oUDSck4Xx4hhfSeLBp35jvGaw1QMZzWYr5v/oc0kEt0bfMdl+833flwKV7kFR3BnrULfg=="],
49124912

4913-
"gitbook-v2/next/@next/swc-darwin-arm64": ["@next/[email protected].7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fs7JYU0MKtpgsDBIvbT/wn1MwKP8C+bqcb6IjWnDtVcciFKzYDs/tPCBW6CtYg1wpHqm9horZDA1ytb/sAghxA=="],
4913+
"gitbook-v2/next/@next/swc-darwin-arm64": ["@next/[email protected].8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZaDynM+pbrnLLlBAxH/CDGp9KN79OFrLcT1ejlWyo86V3SS9Gyqr4nmTuvTevByTTpr1VHReQel8Zbq0Pttu7Q=="],
49144914

4915-
"gitbook-v2/next/@next/swc-darwin-x64": ["@next/[email protected].7", "", { "os": "darwin", "cpu": "x64" }, "sha512-Efl6es/IEXJf5groF78fc1PrTEwK9bOyi6fRjp1bsa/AWOHp8rTc3l0T6EeY5T0SWeYilisZZa9rax7nTgw4bw=="],
4915+
"gitbook-v2/next/@next/swc-darwin-x64": ["@next/[email protected].8", "", { "os": "darwin", "cpu": "x64" }, "sha512-BsMR8WqeCDAX8C9RYAO8TI4ttpuqKk2oYMb1+bCrOYi857SMfB4vWD4PWmMvj7mwFXGqrxly4W6CBQlD66A+fg=="],
49164916

4917-
"gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/[email protected].7", "", { "os": "linux", "cpu": "arm64" }, "sha512-IS71I8z2wlecRZRHXqWTtxVozZt1h9IH/2MX1P/WkCjQH98acFmCkw0jo/y70mrZ4lw6tRGTNkSBMun+5P5P+w=="],
4917+
"gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/[email protected].8", "", { "os": "linux", "cpu": "arm64" }, "sha512-xL+K2SW+/46j/KnKNf1gizM1bxwcEaE56eCEG9RPoYS/lfxHLuHcR9O2MlcPI90g/rIN2HXmHeuKRbXbnVy15g=="],
49184918

4919-
"gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/[email protected].7", "", { "os": "linux", "cpu": "arm64" }, "sha512-uJ4env9fZxIFM+aRBCjNeG91HHZ2cRXiF25xJqGmCC1Y5jT5oYAB/tIBCFRSj6NRzdt4pZAamCMRS41WzzVVUQ=="],
4919+
"gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/[email protected].8", "", { "os": "linux", "cpu": "arm64" }, "sha512-bzXlCUXkjIRsMTb6rr7OsWEQmdO2rZgKijnMGBJzEpa9ROq95VJTF+rdyrLmHuc4fPsAGDn/C18V3E1YOi4ipQ=="],
49204920

4921-
"gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/[email protected].7", "", { "os": "linux", "cpu": "x64" }, "sha512-/+efTL2TxoSAYCxdf+g6WvDKIJDoaETvF3I+lxpixhctfrIwpr2qnG0e6u2M55k1iYiiyFIOA63fpIsGnIMJZA=="],
4921+
"gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/[email protected].8", "", { "os": "linux", "cpu": "x64" }, "sha512-snbPQ9th7eoYYMZpNGhEvX4EbqGjjkiXzTEm2F/oDadlZR2wI6egtfQHiZqeczL7XwG3M66hBJ1/U20wdTDtvA=="],
49224922

4923-
"gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/[email protected].7", "", { "os": "linux", "cpu": "x64" }, "sha512-Hb+agfULOjEhJAmq/r9OTVAo8KxFlnuCT7BEUbSuVP/Gxn883al6juSG7ZZfk7P4a+ZU6iVYyHKIFAKCarZmUA=="],
4923+
"gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/[email protected].8", "", { "os": "linux", "cpu": "x64" }, "sha512-5hmcaGazc3w6rg/gbQOrmuw0kKShf4Egs0JlUPop/ZiRDfZO5KlyrGY0hUD+2pLG7Yx3B/fTbj86cHKfQZDMBw=="],
49244924

4925-
"gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/[email protected].7", "", { "os": "win32", "cpu": "arm64" }, "sha512-gNd5Vq7k9xIEQj85JoavHGRsyvu1zKOjVoPUYtsZRxXw6kcBg84x2g8NeT/3SmwH1PiFU/sjQXtRzr2Kuc2j3A=="],
4925+
"gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/[email protected].8", "", { "os": "win32", "cpu": "arm64" }, "sha512-t6uwWC/UbQ8CQGyBVbEmpcJC41yAvz2eZZGec9EoO0ZyQ/fbvjOkqfPnf+WcjWifWNeuiWogjN1UBg7YK9IaVA=="],
49264926

4927-
"gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/[email protected].7", "", { "os": "win32", "cpu": "x64" }, "sha512-D5i+KinvNNmspXfgIvEZ1RFOKVhOgdtoSC7/xEUm55hEkcWFVHOtVVPvgV7WUeo1elNxbgxytzmyKbLaifP03g=="],
4927+
"gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/[email protected].8", "", { "os": "win32", "cpu": "x64" }, "sha512-93gNqVYwlr9bz6Pm5dQnqjpuOEQyvDre0+H39UqS7h2KuFh3sf2MejhAEr2uFEux4mb7TN0PlohaihO7YxJ3fw=="],
49284928

49294929
"gitbook-v2/next/postcss": ["[email protected]", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
49304930

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"@codemirror/state": "6.4.1",
1313
"react": "18.3.1",
1414
"react-dom": "18.3.1",
15-
"@gitbook/api": "0.109.0"
15+
"@gitbook/api": "0.111.0"
1616
},
1717
"private": true,
1818
"scripts": {

packages/cache-tags/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"version": "0.3.1",
1212
"dependencies": {
13-
"@gitbook/api": "^0.109.0",
13+
"@gitbook/api": "^0.111.0",
1414
"assert-never": "^1.2.1"
1515
},
1616
"devDependencies": {

packages/gitbook/src/components/DocumentView/Block.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { IntegrationBlock } from './Integration';
2525
import { List } from './List';
2626
import { ListItem } from './ListItem';
2727
import { BlockMath } from './Math';
28-
import { OpenAPIOperation, OpenAPISchemas } from './OpenAPI';
28+
import { OpenAPIOperation, OpenAPISchemas, OpenAPIWebhook } from './OpenAPI';
2929
import { Paragraph } from './Paragraph';
3030
import { Quote } from './Quote';
3131
import { ReusableContent } from './ReusableContent';
@@ -85,6 +85,8 @@ export function Block<T extends DocumentBlock>(props: BlockProps<T>) {
8585
return <OpenAPIOperation {...props} block={block} />;
8686
case 'openapi-schemas':
8787
return <OpenAPISchemas {...props} block={block} />;
88+
case 'openapi-webhook':
89+
return <OpenAPIWebhook {...props} block={block} />;
8890
case 'embed':
8991
return <Embed {...props} block={block} />;
9092
case 'blockquote':
@@ -159,6 +161,7 @@ export function BlockSkeleton(props: { block: DocumentBlock; style: ClassValue }
159161
case 'swagger':
160162
case 'openapi-operation':
161163
case 'openapi-schemas':
164+
case 'openapi-webhook':
162165
case 'math':
163166
case 'divider':
164167
case 'content-ref':
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { OpenAPIWebhook as BaseOpenAPIWebhook } from '@gitbook/react-openapi';
2+
3+
import { resolveOpenAPIWebhookBlock } from '@/lib/openapi/resolveOpenAPIWebhookBlock';
4+
import { tcls } from '@/lib/tailwind';
5+
6+
import type { BlockProps } from '../Block';
7+
8+
import './scalar.css';
9+
import './style.css';
10+
import type { OpenAPIWebhookBlock } from '@/lib/openapi/types';
11+
import { getOpenAPIContext } from './context';
12+
13+
/**
14+
* Render an openapi block or an openapi-webhook block.
15+
*/
16+
export async function OpenAPIWebhook(props: BlockProps<OpenAPIWebhookBlock>) {
17+
const { style } = props;
18+
return (
19+
<div className={tcls('flex w-full min-w-0', style, 'max-w-full')}>
20+
<OpenAPIWebhookBody {...props} />
21+
</div>
22+
);
23+
}
24+
25+
async function OpenAPIWebhookBody(props: BlockProps<OpenAPIWebhookBlock>) {
26+
const { block, context } = props;
27+
28+
if (!context.contentContext) {
29+
return null;
30+
}
31+
32+
const { data, specUrl, error } = await resolveOpenAPIWebhookBlock({
33+
block,
34+
context: context.contentContext,
35+
});
36+
37+
if (error) {
38+
return (
39+
<div className="hidden">
40+
<p>
41+
Error with {specUrl}: {error.message}
42+
</p>
43+
</div>
44+
);
45+
}
46+
47+
if (!data || !specUrl) {
48+
return null;
49+
}
50+
51+
return (
52+
<BaseOpenAPIWebhook
53+
data={data}
54+
context={getOpenAPIContext({ props, specUrl })}
55+
className="openapi-block"
56+
/>
57+
);
58+
}

packages/gitbook/src/components/DocumentView/OpenAPI/context.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ import { Heading } from '../Heading';
1111

1212
import './scalar.css';
1313
import './style.css';
14-
import type { AnyOpenAPIOperationsBlock, OpenAPISchemasBlock } from '@/lib/openapi/types';
14+
import type {
15+
AnyOpenAPIOperationsBlock,
16+
OpenAPISchemasBlock,
17+
OpenAPIWebhookBlock,
18+
} from '@/lib/openapi/types';
1519

1620
/**
1721
* Get the OpenAPI context to render a block.
1822
*/
1923
export function getOpenAPIContext(args: {
20-
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock>;
24+
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock>;
2125
specUrl: string;
2226
}): OpenAPIContext {
2327
const { props, specUrl } = args;
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './OpenAPIOperation';
22
export * from './OpenAPISchemas';
3+
export * from './OpenAPIWebhook';

packages/gitbook/src/components/DocumentView/OpenAPI/style.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* Layout Components */
22
.openapi-operation,
3-
.openapi-schemas {
3+
.openapi-schemas,
4+
.openapi-webhook {
45
@apply flex-1 flex flex-col gap-8 mb-14 min-w-0;
56
}
67

packages/gitbook/src/lib/openapi/fetch.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { type CacheFunctionOptions, cache, noCacheFetchOptions } from '@/lib/cac
55
import type {
66
AnyOpenAPIOperationsBlock,
77
OpenAPISchemasBlock,
8+
OpenAPIWebhookBlock,
89
ResolveOpenAPIBlockArgs,
910
} from '@/lib/openapi/types';
1011
import { getCloudflareRequestGlobal } from '@v2/lib/data/cloudflare';
@@ -16,7 +17,7 @@ import { isV2 } from '../v2';
1617
import { enrichFilesystem } from './enrich';
1718
import type { FetchOpenAPIFilesystemResult } from './types';
1819

19-
type AnyOpenAPIBlock = AnyOpenAPIOperationsBlock | OpenAPISchemasBlock;
20+
type AnyOpenAPIBlock = AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock;
2021

2122
/**
2223
* Fetch OpenAPI block.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { fetchOpenAPIFilesystem } from '@/lib/openapi/fetch';
2+
import { OpenAPIParseError } from '@gitbook/openapi-parser';
3+
import { type OpenAPIWebhookData, resolveOpenAPIWebhook } from '@gitbook/react-openapi';
4+
import type {
5+
OpenAPIWebhookBlock,
6+
ResolveOpenAPIBlockArgs,
7+
ResolveOpenAPIBlockResult,
8+
} from './types';
9+
10+
type ResolveOpenAPIWebhookBlockResult = ResolveOpenAPIBlockResult<OpenAPIWebhookData>;
11+
12+
const weakmap = new WeakMap<OpenAPIWebhookBlock, Promise<ResolveOpenAPIWebhookBlockResult>>();
13+
14+
/**
15+
* Cache the result of resolving an OpenAPI block.
16+
* It is important because the resolve is called in sections and in the block itself.
17+
*/
18+
export function resolveOpenAPIWebhookBlock(
19+
args: ResolveOpenAPIBlockArgs<OpenAPIWebhookBlock>
20+
): Promise<ResolveOpenAPIWebhookBlockResult> {
21+
if (weakmap.has(args.block)) {
22+
return weakmap.get(args.block)!;
23+
}
24+
25+
const result = baseResolveOpenAPIWebhookBlock(args);
26+
weakmap.set(args.block, result);
27+
return result;
28+
}
29+
30+
/**
31+
* Resolve OpenAPI webhook block.
32+
*/
33+
async function baseResolveOpenAPIWebhookBlock(
34+
args: ResolveOpenAPIBlockArgs<OpenAPIWebhookBlock>
35+
): Promise<ResolveOpenAPIWebhookBlockResult> {
36+
const { context, block } = args;
37+
if (!block.data.name || !block.data.method) {
38+
return { data: null, specUrl: null };
39+
}
40+
41+
try {
42+
const { filesystem, specUrl } = await fetchOpenAPIFilesystem({ block, context });
43+
44+
if (!filesystem) {
45+
return { data: null, specUrl: null };
46+
}
47+
48+
const data = await resolveOpenAPIWebhook(filesystem, {
49+
name: block.data.name,
50+
method: block.data.method,
51+
});
52+
53+
return { data, specUrl };
54+
} catch (error) {
55+
if (error instanceof OpenAPIParseError) {
56+
return { error };
57+
}
58+
59+
throw error;
60+
}
61+
}

packages/gitbook/src/lib/openapi/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
DocumentBlockOpenAPI,
33
DocumentBlockOpenAPIOperation,
44
DocumentBlockOpenAPISchemas,
5+
DocumentBlockOpenAPIWebhook,
56
} from '@gitbook/api';
67
import type { Filesystem, OpenAPIParseError, OpenAPIV3xDocument } from '@gitbook/openapi-parser';
78
import type { GitBookAnyContext } from '@v2/lib/context';
@@ -16,6 +17,11 @@ export type AnyOpenAPIOperationsBlock = DocumentBlockOpenAPI | DocumentBlockOpen
1617
*/
1718
export type OpenAPISchemasBlock = DocumentBlockOpenAPISchemas;
1819

20+
/**
21+
* Type for OpenAPI Webhook block
22+
*/
23+
export type OpenAPIWebhookBlock = DocumentBlockOpenAPIWebhook;
24+
1925
/**
2026
* Arguments for resolving OpenAPI block.
2127
*/

packages/react-openapi/src/OpenAPICodeSample.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function OpenAPICodeSample(props: {
2222
data: OpenAPIOperationData;
2323
context: OpenAPIContext;
2424
}) {
25-
const { data } = props;
25+
const { data, context } = props;
2626

2727
// If code samples are disabled at operation level, we don't display the code samples.
2828
if (data.operation['x-codeSamples'] === false) {
@@ -43,7 +43,9 @@ export function OpenAPICodeSample(props: {
4343
return null;
4444
}
4545

46-
return <OpenAPICodeSampleBody data={data} items={samples} />;
46+
return (
47+
<OpenAPICodeSampleBody data={data} items={samples} selectIcon={context.icons.chevronDown} />
48+
);
4749
}
4850

4951
/**
@@ -146,6 +148,7 @@ function generateCodeSamples(props: {
146148
method={data.method}
147149
path={data.path}
148150
renderers={renderers}
151+
blockKey={context.blockKey}
149152
/>
150153
),
151154
footer: (
@@ -207,6 +210,7 @@ function OpenAPICodeSampleFooter(props: {
207210
path={data.path}
208211
renderers={renderers}
209212
selectIcon={context.icons.chevronDown}
213+
blockKey={context.blockKey}
210214
/>
211215
) : (
212216
<span />

packages/react-openapi/src/OpenAPICodeSampleInteractive.tsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
import clsx from 'clsx';
33
import type { MediaTypeRenderer } from './OpenAPICodeSample';
44
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
5+
import { createStateKey } from './utils';
56

67
export function OpenAPIMediaTypeExamplesSelector(props: {
78
method: string;
89
path: string;
910
renderers: MediaTypeRenderer[];
1011
selectIcon?: React.ReactNode;
12+
blockKey?: string;
1113
}) {
12-
const { method, path, renderers, selectIcon } = props;
14+
const { method, path, renderers, selectIcon, blockKey } = props;
1315
if (!renderers[0]) {
1416
throw new Error('No renderers provided');
1517
}
16-
const stateKey = `media-type-${method}-${path}`;
18+
const stateKey = createStateKey('request-body-media-type', blockKey);
1719
const state = useSelectState(stateKey, renderers[0].mediaType);
1820
const selected = renderers.find((r) => r.mediaType === state.key) || renderers[0];
1921

@@ -102,13 +104,17 @@ export function OpenAPIMediaTypeExamplesBody(props: {
102104
method: string;
103105
path: string;
104106
renderers: MediaTypeRenderer[];
107+
blockKey?: string;
105108
}) {
106-
const { renderers, method, path } = props;
109+
const { renderers, method, path, blockKey } = props;
107110
if (!renderers[0]) {
108111
throw new Error('No renderers provided');
109112
}
110113

111-
const mediaTypeState = useSelectState(`media-type-${method}-${path}`, renderers[0].mediaType);
114+
const mediaTypeState = useSelectState(
115+
createStateKey('request-body-media-type', blockKey),
116+
renderers[0].mediaType
117+
);
112118
const selected = renderers.find((r) => r.mediaType === mediaTypeState.key) ?? renderers[0];
113119
if (selected.examples.length === 0) {
114120
return selected.element;

0 commit comments

Comments
 (0)