Skip to content

Commit 82cd9f2

Browse files
authored
Add support for anchor link in OpenAPI blocks (#2858)
1 parent a3f1fea commit 82cd9f2

File tree

7 files changed

+65
-71
lines changed

7 files changed

+65
-71
lines changed

.changeset/sixty-planets-fly.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@gitbook/react-openapi': patch
3+
'gitbook': patch
4+
---
5+
6+
Add support for anchor links in OpenAPI blocks

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

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { Icon } from '@gitbook/icons';
33
import { OpenAPIOperation } from '@gitbook/react-openapi';
44
import React from 'react';
55

6-
import { LoadingPane } from '@/components/primitives';
76
import { fetchOpenAPIBlock } from '@/lib/openapi/fetch';
87
import { tcls } from '@/lib/tailwind';
98

109
import { BlockProps } from '../Block';
1110
import { PlainCodeBlock } from '../CodeBlock';
11+
import { Heading } from '../Heading';
1212

1313
import './style.css';
1414
import './scalar.css';
@@ -54,6 +54,31 @@ async function OpenAPIBody(props: BlockProps<DocumentBlockOpenAPI>) {
5454
plus: <Icon icon="plus" />,
5555
},
5656
CodeBlock: PlainCodeBlock,
57+
renderHeading: (headingProps) => (
58+
<Heading
59+
document={props.document}
60+
ancestorBlocks={props.ancestorBlocks}
61+
isEstimatedOffscreen={props.isEstimatedOffscreen}
62+
context={props.context}
63+
style={headingProps.deprecated ? 'line-through' : undefined}
64+
block={{
65+
object: 'block',
66+
key: `${block.key}-heading`,
67+
meta: block.meta,
68+
data: {},
69+
type: 'heading-2',
70+
nodes: [
71+
{
72+
key: `${block.key}-heading-text`,
73+
object: 'text',
74+
leaves: [
75+
{ text: headingProps.title, object: 'leaf', marks: [] },
76+
],
77+
},
78+
],
79+
}}
80+
/>
81+
),
5782
defaultInteractiveOpened: context.mode === 'print',
5883
id: block.meta?.id,
5984
blockKey: block.key,
@@ -62,32 +87,3 @@ async function OpenAPIBody(props: BlockProps<DocumentBlockOpenAPI>) {
6287
/>
6388
);
6489
}
65-
66-
function OpenAPIFallback() {
67-
return (
68-
<div
69-
role="status"
70-
aria-busy
71-
className={'openapi-block ' + tcls('flex', 'flex-1', 'flex-col', 'gap-3')}
72-
>
73-
<LoadingPane
74-
tile={12}
75-
style={['rounded-md', 'h-[47px]', '[max-width:calc(48rem-1px)]']}
76-
/>
77-
<LoadingPane
78-
tile={12}
79-
style={['rounded-md', 'h-[35px]', '[max-width:calc(48rem-1px)]']}
80-
/>
81-
<div className={tcls('flex', 'gap-[25px]')}>
82-
<div className={tcls('flex', 'flex-1', 'flex-col', 'gap-3')}>
83-
<LoadingPane tile={24} style={['rounded-md', 'aspect-[2.5/1]', 'w-full']} />
84-
<LoadingPane tile={24} style={['rounded-md', 'aspect-[2.5/1]', 'w-full']} />
85-
</div>
86-
<div className={tcls('flex', 'flex-1', 'flex-col', 'gap-3')}>
87-
<LoadingPane tile={24} style={['rounded-md', 'aspect-[4/1]', 'w-full']} />
88-
<LoadingPane tile={24} style={['rounded-md', 'aspect-[4/1]', 'w-full']} />
89-
</div>
90-
</div>
91-
</div>
92-
);
93-
}

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
/* Layout Components */
22
.openapi-operation {
3-
content-visibility: auto;
4-
contain-intrinsic-height: 600px;
53
@apply flex-1 flex flex-col gap-4 mb-14;
64
}
75

@@ -18,16 +16,8 @@
1816
@apply flex flex-col items-start justify-start gap-2;
1917
}
2018

21-
.openapi-summary-title {
22-
@apply font-semibold text-xl;
23-
}
24-
25-
.openapi-summary-title[data-deprecated='true'] {
26-
@apply line-through;
27-
}
28-
2919
.openapi-deprecated {
30-
@apply py-0.5 px-1.5 min-w-[1.625rem] font-normal w-fit justify-center items-center ring-1 ring-inset ring-tint bg-tint rounded-full text-sm leading-[calc(max(1.20em,1.25rem))] before:!content-none after:!content-none;
20+
@apply py-0.5 px-1.5 min-w-[1.625rem] font-normal w-fit justify-center items-center ring-1 ring-inset ring-tint bg-tint rounded text-sm leading-[calc(max(1.20em,1.25rem))] before:!content-none after:!content-none;
3121
}
3222

3323
.openapi-deprecated-sunset-date {

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

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,29 @@ import { ClassValue, tcls } from '@/lib/tailwind';
1515
export function Text(props: { text: DocumentText }) {
1616
const { text } = props;
1717

18-
return (
19-
<>
20-
{text.leaves.map((leaf, index) => {
21-
return (
22-
<React.Fragment key={index}>
23-
{leaf.marks
24-
// Sort to have code marks at the end, so that they don't interfere with other marks
25-
.sort(
26-
(a, b) => (a.type === 'code' ? 1 : 0) - (b.type === 'code' ? 1 : 0),
27-
)
28-
.reduce<React.ReactNode>((children, mark, index) => {
29-
const Mark = MARK_STYLES[mark.type];
18+
return text.leaves.map((leaf, index) => {
19+
return (
20+
<React.Fragment key={index}>
21+
{leaf.marks
22+
// Sort to have code marks at the end, so that they don't interfere with other marks
23+
.sort((a, b) => (a.type === 'code' ? 1 : 0) - (b.type === 'code' ? 1 : 0))
24+
.reduce<React.ReactNode>((children, mark, index) => {
25+
const Mark = MARK_STYLES[mark.type];
3026

31-
if (!Mark) {
32-
return children;
33-
}
27+
if (!Mark) {
28+
return children;
29+
}
3430

35-
// @ts-ignore
36-
return <Mark mark={mark}>{children}</Mark>;
37-
}, leaf.text)}
38-
</React.Fragment>
39-
);
40-
})}
41-
</>
42-
);
31+
return (
32+
// @ts-ignore
33+
<Mark key="mark" mark={mark}>
34+
{children}
35+
</Mark>
36+
);
37+
}, leaf.text)}
38+
</React.Fragment>
39+
);
40+
});
4341
}
4442

4543
const MARK_STYLES = {

packages/react-openapi/src/OpenAPIOperation.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@ export function OpenAPIOperation(props: {
2525
blockKey: context.blockKey,
2626
};
2727

28-
const description = resolveDescription(operation)?.trim();
28+
const description = resolveDescription(operation);
2929

3030
return (
3131
<div className={clsx('openapi-operation', className)}>
32-
<div className="openapi-summary" id={context.id}>
33-
<h2 className="openapi-summary-title" data-deprecated={operation.deprecated}>
34-
{operation.summary}
35-
</h2>
32+
<div className="openapi-summary">
33+
{operation.summary
34+
? context.renderHeading({
35+
deprecated: operation.deprecated ?? false,
36+
title: operation.summary,
37+
})
38+
: null}
3639
{operation.deprecated && <div className="openapi-deprecated">Deprecated</div>}
3740
</div>
3841
<div className="openapi-columns">

packages/react-openapi/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66

77
export interface OpenAPIContextProps extends OpenAPIClientContext {
88
CodeBlock: React.ComponentType<{ code: string; syntax: string }>;
9+
renderHeading: (props: { deprecated: boolean; title: string }) => React.ReactNode;
910

1011
/** Spec url for the Scalar Api Client */
1112
specUrl: string;

packages/react-openapi/src/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ export function createStateKey(key: string, scope?: string) {
1616
export function resolveDescription(object: AnyObject) {
1717
return 'x-gitbook-description-html' in object &&
1818
typeof object['x-gitbook-description-html'] === 'string'
19-
? object['x-gitbook-description-html']
19+
? object['x-gitbook-description-html'].trim()
2020
: typeof object.description === 'string'
21-
? object.description
21+
? object.description.trim()
2222
: undefined;
2323
}
2424

0 commit comments

Comments
 (0)