Feat/openapi server select (#1046)

Co-authored-by: Fuma Nama <[email protected]>
This commit is contained in:
Goh Sheen An
2024-11-14 11:34:40 +08:00
committed by GitHub
co-authored by Fuma Nama
parent aa1f401cd3
commit 196b78b9cb
9 changed files with 1191 additions and 359 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-openapi': minor
---
OpenAPI: Server selector to allow interacting with different API environments
+1
View File
@@ -11,6 +11,7 @@ info:
url: 'https://opensource.org/license/mit/'
servers:
- url: 'https://redocly.com/_mock/docs/openapi/museum-api'
- url: 'https://example.com/api/v1'
paths:
/museum-hours:
get:
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -33,11 +33,13 @@ interface CodeSampleCompiled {
}
export function Operation({
baseUrls,
path,
method,
ctx,
hasHead,
}: {
baseUrls: string[];
path: string;
method: MethodInformation;
ctx: RenderContext;
@@ -147,7 +149,11 @@ export function Operation({
return (
<ctx.renderer.API>
<ctx.renderer.APIInfo method={method.method} route={path}>
<ctx.renderer.APIInfo
method={method.method}
route={path}
baseUrls={baseUrls}
>
{info}
</ctx.renderer.APIInfo>
<APIExample method={method} endpoint={endpoint} ctx={ctx} />
+1
View File
@@ -27,6 +27,7 @@ export interface ResponseProps {
export interface APIInfoProps {
method: string;
route: string;
baseUrls: string[];
children: ReactNode;
}
+3
View File
@@ -62,6 +62,9 @@ export async function APIPage(props: ApiPageProps): Promise<ReactElement> {
method={method}
path={item.path}
ctx={ctx}
baseUrls={
document.servers ? document.servers.map((s) => s.url) : []
}
hasHead={hasHead}
/>
);
+24
View File
@@ -74,4 +74,28 @@ export function CopyRouteButton({
);
}
export function BaseUrlSelect({ baseUrls }: { baseUrls: string[] }) {
const { baseUrl, setBaseUrl } = useApiContext();
if (baseUrls.length === 0) return null;
return (
<div className="flex flex-row items-center gap-1 px-1">
<span className="p-0.5 text-xs font-medium text-fd-muted-foreground">
Server
</span>
<select
value={baseUrl}
onChange={(e) => setBaseUrl(e.target.value)}
className="min-w-0 flex-1 bg-transparent font-mono text-xs text-fd-foreground outline-none"
>
{baseUrls.map((url) => (
<option key={url} value={url}>
{url}
</option>
))}
</select>
</div>
);
}
export { useSchemaContext } from './contexts/schema';
+17 -15
View File
@@ -3,7 +3,7 @@ import { Fragment, type HTMLAttributes, type ReactNode } from 'react';
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
import { badgeVariants, getBadgeColor } from '@/ui/components/variants';
import type { APIInfoProps, PropertyProps } from '@/render/renderer';
import { CopyRouteButton } from '@/ui/client';
import { BaseUrlSelect, CopyRouteButton } from '@/ui/client';
export { Root, useSchemaContext, APIPlayground } from './client';
@@ -31,28 +31,30 @@ export function APIInfo({
className,
route,
badgeClassname,
baseUrls,
method = 'GET',
...props
}: APIInfoProps &
HTMLAttributes<HTMLDivElement> & {
badgeClassname?: string;
}): React.ReactElement {
}) {
return (
<div className={cn('min-w-0 flex-1', className)} {...props}>
<div className="sticky top-[var(--fd-api-info-top)] z-[4] mb-4 flex flex-row items-center gap-1.5 border-b border-fd-foreground/10 bg-fd-card/50 px-4 py-1.5 shadow-lg backdrop-blur-lg max-lg:-mx-3 max-md:-mx-4 md:rounded-full md:border md:px-1.5">
<span
className={cn(
badgeVariants({ color: getBadgeColor(method) }),
badgeClassname,
)}
>
{method}
</span>
<Route route={route} />
<CopyRouteButton className="ms-auto size-6 p-1.5" route={route} />
<div className="sticky top-[var(--fd-api-info-top)] z-[4] mb-4 border-b border-fd-foreground/10 bg-fd-card/50 px-4 py-1.5 shadow-lg backdrop-blur-lg max-lg:-mx-3 max-md:-mx-4 md:rounded-xl md:border md:px-1.5">
<div className="mb-2 flex flex-row items-center gap-1.5">
<span
className={cn(
badgeVariants({ color: getBadgeColor(method) }),
badgeClassname,
)}
>
{method}
</span>
<Route route={route} />
<CopyRouteButton className="ms-auto size-6 p-1.5" route={route} />
</div>
<BaseUrlSelect baseUrls={baseUrls} />
</div>
<div className="prose-no-margin">{children}</div>
</div>
);
@@ -16,7 +16,6 @@ export function generateDocument(
options: GenerateOptions & {
dereferenced: OpenAPI.Document;
page: ApiPageProps;
title: string;
description?: string;
context: DocumentContext;