Support for x-enumDescriptions and x-gitbook-enum (#3066)

This commit is contained in:
Nolann B.
2025-03-31 12:18:57 +02:00
committed by GitHub
parent 6f71da88d8
commit 813b2af06b
5 changed files with 99 additions and 18 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@gitbook/openapi-parser': patch
'@gitbook/react-openapi': patch
'gitbook': patch
---
Support for x-enumDescriptions and x-gitbook-enum
@@ -244,7 +244,11 @@
/* Schema Enum */ /* Schema Enum */
.openapi-schema-enum { .openapi-schema-enum {
@apply flex flex-row text-sm leading-relaxed flex-wrap text-tint; @apply flex flex-row text-sm leading-relaxed gap-2 flex-wrap text-tint;
}
.openapi-schema-enum-list {
@apply flex flex-row gap-1.5 items-center;
} }
.openapi-schema-enum-value { .openapi-schema-enum-value {
@@ -693,3 +697,7 @@
opacity: 0; opacity: 0;
} }
} }
.openapi-copy-button {
@apply hover:brightness-95;
}
+15
View File
@@ -47,6 +47,21 @@ export interface OpenAPICustomOperationProperties {
* Description in Document format. * Description in Document format.
*/ */
'x-gitbook-description-document'?: object; 'x-gitbook-description-document'?: object;
/**
* Enums with name and description
*/
'x-enumDescriptions'?: object;
/**
* Enums with name and description
*/
'x-gitbook-enum'?: {
[key: string]: {
description?: string;
name?: string;
};
};
} }
/** /**
@@ -6,10 +6,16 @@ import { Button, type ButtonProps, Tooltip, TooltipTrigger } from 'react-aria-co
export function OpenAPICopyButton( export function OpenAPICopyButton(
props: ButtonProps & { props: ButtonProps & {
value: string; value: string;
children: React.ReactNode;
label?: string;
/**
* Whether to show a tooltip.
* @default true
*/
withTooltip?: boolean;
} }
) { ) {
const { value } = props; const { value, label, children, onPress, className, withTooltip = true } = props;
const { children, onPress, className } = props;
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@@ -21,12 +27,19 @@ export function OpenAPICopyButton(
setTimeout(() => { setTimeout(() => {
setCopied(false); setCopied(false);
setIsOpen(false);
}, 2000); }, 2000);
}); });
}; };
return ( return (
<TooltipTrigger isOpen={isOpen} onOpenChange={setIsOpen} closeDelay={200} delay={200}> <TooltipTrigger
isOpen={isOpen}
onOpenChange={setIsOpen}
isDisabled={!withTooltip}
closeDelay={200}
delay={200}
>
<Button <Button
type="button" type="button"
preventFocusOnPress preventFocusOnPress
@@ -47,7 +60,7 @@ export function OpenAPICopyButton(
offset={4} offset={4}
className="openapi-tooltip" className="openapi-tooltip"
> >
{copied ? 'Copied' : 'Copy to clipboard'}{' '} {copied ? 'Copied' : label || 'Copy to clipboard'}
</Tooltip> </Tooltip>
</TooltipTrigger> </TooltipTrigger>
); );
+51 -13
View File
@@ -2,11 +2,12 @@
// This component does not use any client feature but we don't want to // This component does not use any client feature but we don't want to
// render it server-side because it has recursion. // render it server-side because it has recursion.
import type { OpenAPIV3 } from '@gitbook/openapi-parser'; import type { OpenAPICustomOperationProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
import { useId } from 'react'; import { useId } from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import { Markdown } from './Markdown'; import { Markdown } from './Markdown';
import { OpenAPICopyButton } from './OpenAPICopyButton';
import { OpenAPIDisclosure } from './OpenAPIDisclosure'; import { OpenAPIDisclosure } from './OpenAPIDisclosure';
import { OpenAPISchemaName } from './OpenAPISchemaName'; import { OpenAPISchemaName } from './OpenAPISchemaName';
import { retrocycle } from './decycle'; import { retrocycle } from './decycle';
@@ -236,20 +237,59 @@ function OpenAPISchemaCircularRef(props: { id: string; schema: OpenAPIV3.SchemaO
/** /**
* Render the enum value for a schema. * Render the enum value for a schema.
*/ */
function OpenAPISchemaEnum(props: { enumValues: any[] }) { function OpenAPISchemaEnum(props: {
const { enumValues } = props; schema: OpenAPIV3.SchemaObject & OpenAPICustomOperationProperties;
}) {
const { schema } = props;
const enumValues = (() => {
// Render x-gitbook-enum first, as it has a different format
if (schema['x-gitbook-enum']) {
return Object.entries(schema['x-gitbook-enum']).map(([name, { description }]) => {
return {
value: name,
description,
};
});
}
if (schema['x-enumDescriptions']) {
return Object.entries(schema['x-enumDescriptions']).map(([value, description]) => {
return {
value,
description,
};
});
}
return schema.enum?.map((value) => {
return {
value,
description: undefined,
};
});
})();
if (!enumValues?.length) {
return null;
}
return ( return (
<div className="openapi-schema-enum"> <div className="openapi-schema-enum">
<span> <span>Available options:</span>
Options:{' '} <div className="openapi-schema-enum-list">
{enumValues.map((value, index) => ( {enumValues.map((item, index) => (
<span key={index} className="openapi-schema-enum-value"> <span key={index} className="openapi-schema-enum-value">
<code>{`${value}`}</code> <OpenAPICopyButton
{index < enumValues.length - 1 ? ', ' : ''} value={item.value}
label={item.description}
withTooltip={!!item.description}
>
<code>{`${item.value}`}</code>
</OpenAPICopyButton>
</span> </span>
))} ))}
</span> </div>
</div> </div>
); );
} }
@@ -294,9 +334,7 @@ function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry
Pattern: <code>{schema.pattern}</code> Pattern: <code>{schema.pattern}</code>
</div> </div>
) : null} ) : null}
{schema.enum && schema.enum.length > 0 ? ( <OpenAPISchemaEnum schema={schema} />
<OpenAPISchemaEnum enumValues={schema.enum} />
) : null}
</div> </div>
); );
} }
@@ -421,7 +459,7 @@ function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string {
// Otherwise try to infer a nice title // Otherwise try to infer a nice title
let type = 'any'; let type = 'any';
if (schema.enum) { if (schema.enum || schema['x-enumDescriptions'] || schema['x-gitbook-enum']) {
type = `${schema.type} · enum`; type = `${schema.type} · enum`;
// check array AND schema.items as this is sometimes null despite what the type indicates // check array AND schema.items as this is sometimes null despite what the type indicates
} else if (schema.type === 'array' && !!schema.items) { } else if (schema.type === 'array' && !!schema.items) {