Support expand-all options for OpenAPI (#4116)

This commit is contained in:
Nolann B.
2026-03-25 23:53:51 +01:00
committed by GitHub
parent 1ba412ff6c
commit c56b569a05
16 changed files with 106 additions and 20 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"@gitbook/openapi-parser": patch
"@gitbook/react-openapi": patch
"gitbook": patch
---
Support expand-all options for OpenAPI
@@ -50,6 +50,10 @@ async function OpenAPIOperationBody(props: BlockProps<AnyOpenAPIOperationsBlock>
props,
specUrl: publicURL,
context: context.contentContext,
expandAllResponses:
data.operation['x-expandAllResponses'] ?? data['x-expandAllResponses'],
expandAllModelSections:
data.operation['x-expandAllModelSections'] ?? data['x-expandAllModelSections'],
})}
className="openapi-block"
/>
@@ -46,12 +46,13 @@ async function OpenAPISchemasBody(props: BlockProps<OpenAPISchemasBlock>) {
return (
<BaseOpenAPISchemas
schemas={data.schemas}
data={data}
grouped={block.data.grouped}
context={getOpenAPIContext({
props,
specUrl: publicURL,
context: context.contentContext,
expandAllModelSections: data['x-expandAllModelSections'],
})}
className="openapi-block"
/>
@@ -52,6 +52,10 @@ async function OpenAPIWebhookBody(props: BlockProps<OpenAPIWebhookBlock>) {
props,
specUrl: publicURL,
context: context.contentContext,
expandAllResponses:
data.operation['x-expandAllResponses'] ?? data['x-expandAllResponses'],
expandAllModelSections:
data.operation['x-expandAllModelSections'] ?? data['x-expandAllModelSections'],
})}
className="openapi-block"
/>
@@ -24,8 +24,10 @@ export function getOpenAPIContext(args: {
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock>;
specUrl: string | null;
context: GitBookAnyContext | undefined;
expandAllResponses?: boolean;
expandAllModelSections?: boolean;
}): OpenAPIContextInput {
const { props, specUrl, context } = args;
const { props, specUrl, context, expandAllResponses, expandAllModelSections } = args;
const { block } = props;
const customizationLocale = context ? getSpaceLocale(context) : DEFAULT_LOCALE;
@@ -91,7 +93,8 @@ export function getOpenAPIContext(args: {
}}
/>
),
defaultInteractiveOpened: props.context.mode === 'print',
expandAllResponses: expandAllResponses || props.context.mode === 'print',
expandAllModelSections: expandAllModelSections || props.context.mode === 'print',
id: block.meta?.id,
blockKey: block.key,
locale,
@@ -1,5 +1,5 @@
import { OpenAPIParseError, type OpenAPISchema } from '@gitbook/openapi-parser';
import { resolveOpenAPISchemas } from '@gitbook/react-openapi';
import { OpenAPIParseError } from '@gitbook/openapi-parser';
import { type OpenAPISchemasData, resolveOpenAPISchemas } from '@gitbook/react-openapi';
import { fetchOpenAPIFilesystem } from './fetch';
import type {
OpenAPISchemasBlock,
@@ -7,9 +7,7 @@ import type {
ResolveOpenAPIBlockResult,
} from './types';
type ResolveOpenAPISchemasBlockResult = ResolveOpenAPIBlockResult<{
schemas: OpenAPISchema[];
}>;
type ResolveOpenAPISchemasBlockResult = ResolveOpenAPIBlockResult<OpenAPISchemasData>;
const weakmap = new WeakMap<OpenAPISchemasBlock, Promise<ResolveOpenAPISchemasBlockResult>>();
+20
View File
@@ -23,6 +23,16 @@ export interface OpenAPICustomSpecProperties {
*/
'x-enable-proxy'?: boolean;
/**
* If `true`, all response sections will be expanded by default.
*/
'x-expandAllResponses'?: boolean;
/**
* If `true`, all model/schema sections will be expanded by default.
*/
'x-expandAllModelSections'?: boolean;
/**
* Description in HTML format.
*/
@@ -75,6 +85,16 @@ export interface OpenAPICustomOperationProperties {
*/
'x-enable-proxy'?: boolean;
/**
* If `true`, all response sections will be expanded by default.
*/
'x-expandAllResponses'?: boolean;
/**
* If `true`, all model/schema sections will be expanded by default.
*/
'x-expandAllModelSections'?: boolean;
/**
* Stability of the operation.
* @enum 'experimental' | 'alpha' | 'beta'
@@ -43,6 +43,7 @@ export function OpenAPIResponse(props: {
/>
}
icon={context.icons.plus}
defaultExpanded={context.expandAllModelSections}
label={(isExpanded) =>
tString(
context.translation,
@@ -107,11 +107,19 @@ export function OpenAPIResponses(props: {
const state = useResponseExamplesState(context.blockKey, groups[0]?.key);
const expandAll = context.expandAllResponses;
const expandedKeys = expandAll
? new Set(groups.map((g) => g.key))
: state.key
? new Set([state.key])
: new Set<string>();
return (
<StaticSection header={t(context.translation, 'responses')} className="openapi-responses">
<OpenAPIDisclosureGroup
icon={context.icons.chevronRight}
expandedKeys={state.key ? new Set([state.key]) : new Set()}
allowsMultipleExpanded={expandAll}
expandedKeys={expandedKeys}
onExpandedChange={(keys) => {
const key = keys.values().next().value ?? null;
state.setKey(key);
@@ -130,6 +130,7 @@ function OpenAPISchemaProperty(
label={(isExpanded) =>
getDisclosureLabel({ schema: effectiveSchema, isExpanded, context })
}
defaultExpanded={context.expandAllModelSections}
>
{content}
</OpenAPIDisclosure>
@@ -408,6 +409,7 @@ function OpenAPISchemaAlternative(props: {
icon={context.icons.plus}
header={<OpenAPISchemaPresentation property={{ schema }} context={context} />}
label={(isExpanded) => getDisclosureLabel({ schema, isExpanded, context })}
defaultExpanded={context.expandAllModelSections}
>
<OpenAPISchemaProperties
properties={properties}
+10 -3
View File
@@ -19,10 +19,16 @@ export interface OpenAPIClientContext {
};
/**
* Force all sections to be opened by default.
* If `true`, all response sections will be expanded by default.
* @default false
*/
defaultInteractiveOpened?: boolean;
expandAllResponses?: boolean;
/**
* If `true`, all model/schema sections will be expanded by default.
* @default false
*/
expandAllModelSections?: boolean;
/**
* The key of the block
@@ -107,7 +113,8 @@ export function getOpenAPIClientContext(context: OpenAPIUniversalContext): OpenA
return {
translation: context.translation,
icons: context.icons,
defaultInteractiveOpened: context.defaultInteractiveOpened,
expandAllResponses: context.expandAllResponses,
expandAllModelSections: context.expandAllModelSections,
blockKey: context.blockKey,
id: context.id,
proxyUrl: '$$isClientContext$$' in context ? context.proxyUrl : undefined,
@@ -83,6 +83,14 @@ export async function resolveOpenAPIOperation(
: undefined,
'x-enable-proxy':
typeof schema['x-enable-proxy'] === 'boolean' ? schema['x-enable-proxy'] : undefined,
'x-expandAllResponses':
typeof schema['x-expandAllResponses'] === 'boolean'
? schema['x-expandAllResponses']
: undefined,
'x-expandAllModelSections':
typeof schema['x-expandAllModelSections'] === 'boolean'
? schema['x-expandAllModelSections']
: undefined,
};
}
@@ -43,6 +43,14 @@ export async function resolveOpenAPIWebhook(
operation,
method,
name,
'x-expandAllResponses':
typeof schema['x-expandAllResponses'] === 'boolean'
? schema['x-expandAllResponses']
: undefined,
'x-expandAllModelSections':
typeof schema['x-expandAllModelSections'] === 'boolean'
? schema['x-expandAllModelSections']
: undefined,
};
}
@@ -23,6 +23,7 @@ export function OpenAPISchemaItem(props: {
icon={context.icons.plus}
header={name}
label={(isExpanded) => getDisclosureLabel({ schema, isExpanded, context })}
defaultExpanded={context.expandAllModelSections}
>
<Section className="openapi-section-schemas">
<SectionBody>
@@ -1,4 +1,3 @@
import type { OpenAPISchema } from '@gitbook/openapi-parser';
import clsx from 'classnames';
import { OpenAPIExample } from '../OpenAPIExample';
import { OpenAPIRootSchema } from '../OpenAPISchemaServer';
@@ -11,20 +10,22 @@ import {
import { t } from '../translate';
import { getExampleFromSchema } from '../util/example';
import { OpenAPISchemaItem } from './OpenAPISchemaItem';
import type { OpenAPISchemasData } from './resolveOpenAPISchemas';
/**
* OpenAPI Schemas component.
*/
export function OpenAPISchemas(props: {
className?: string;
schemas: OpenAPISchema[];
data: OpenAPISchemasData;
context: OpenAPIContextInput;
/**
* Whether to show the schema directly if there is only one.
*/
grouped?: boolean;
}) {
const { schemas, context: contextInput, grouped, className } = props;
const { data, context: contextInput, grouped, className } = props;
const { schemas } = data;
const firstSchema = schemas[0];
@@ -1,7 +1,16 @@
import type { Filesystem, OpenAPISchema, OpenAPIV3xDocument } from '@gitbook/openapi-parser';
import type {
Filesystem,
OpenAPICustomSpecProperties,
OpenAPISchema,
OpenAPIV3xDocument,
} from '@gitbook/openapi-parser';
import { filterSelectedOpenAPISchemas } from '@gitbook/openapi-parser';
import { dereferenceFilesystem } from '../dereference';
export type OpenAPISchemasData = Pick<OpenAPICustomSpecProperties, 'x-expandAllModelSections'> & {
schemas: OpenAPISchema[];
};
/**
* Resolve an OpenAPI schemas from a file and compile it to a more usable format.
* Schemas are extracted from the OpenAPI components.schemas
@@ -11,9 +20,7 @@ export async function resolveOpenAPISchemas(
options: {
schemas: string[];
}
): Promise<{
schemas: OpenAPISchema[];
} | null> {
): Promise<OpenAPISchemasData | null> {
const { schemas: selectedSchemas } = options;
const schema = await dereferenceFilesystem(filesystem);
@@ -24,5 +31,11 @@ export async function resolveOpenAPISchemas(
return null;
}
return { schemas };
return {
schemas,
'x-expandAllModelSections':
typeof schema['x-expandAllModelSections'] === 'boolean'
? schema['x-expandAllModelSections']
: undefined,
};
}